Hi all,
We detected a problem with the function is_ldapv3 in ldap.inc leading to a false positive in plugin 10723 (ldap_null_bind.nasl) when testing an active directory (all windows server version). The is_ldapv3 function is to basic and check directly a hardcoded offset for the error code. This works fine for (at least) some openldap version, but it fails when querying an active directory server, whereas the answer is perfectly valid. This is due to the fact that active directories encode the BER/ASN.1 fields in a different (but legal) way than openldap. - OpenLDAP encodes sizes on 1 byte since answers are < 128bytes. - Active Directory behaves differently and encodes size using the special value 0x80, than means that size is encoded in the following bytes (see http://en.wikipedia.org/wiki/Basic_Encoding_Rules#Length for details). It seems to always store size as a 4bytes integer even if the size is < 128bytes As a consequence, to determine the offset of the error code in the response data, the LDAPMessage has to be carefully parsed, decoding all the BER/ASN.1 tokens to correctly shift the offset ! So find attached to this mail : - the modified ldap.inc tested against openldap and windows AD; - the (same) modifications as a patch against current ldap.inc. With this changes, the V3 bind is correctly checked against either an openldap server or an active directory, and the false positive in ldap_null_bind.nasl vanishes. If you want to play with wireshark, find enclosed 2 pcap files of the V3 bind on an openldap server and on a win 2000 active directory. This illustrates the different ways of encoding the sizes, leading to the differents offsets to probe for the bind error code. One more thought about LDAPv2 and NULL bind. LDAPv2 is deprecated since some years because of design flaws and security issues (see http://tools.ietf.org/html/rfc3494). LDAPv3 is widely used since many years too. So IMHO, ldap_null_bind.nasl should be removed as it does not sound relevant anymore and replaced by a plugin testing if V3 bind is allowed or not. If V3 bind is not allowed, this is a security hole (too old and insecure ldap server), whereas if it's allowed, there is no problem since null bind (on rootdse) is mandatory in V3. Thanks for reading ! -- Guillaume Castagnino [email protected] / [email protected]
###############################################################################
# Functions for LDAP
#
# Authors:
# Michael Meyer
#
# Copyright:
# Copyright (c) 2009 Greenbone Networks GmbH
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2
# (or any later version), as published by the Free Software
# Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
# USA.
###############################################################################
function ldap_alive(port)
{
local_var req, buf, response;
req = raw_string(0x30,0x84,0x00,0x00,0x00,0x59,0x02,0x01,0x05,0x63,0x84,0x00,
0x00,0x00,0x50,0x04,0x13,0x64,0x63,0x3d,0x6f,0x70,0x65,0x6e,
0x76,0x61,0x73,0x64,0x63,0x2c,0x64,0x63,0x3d,0x6e,0x65,0x74,
0x0a,0x01,0x02,0x0a,0x01,0x00,0x02,0x01,0x00,0x02,0x01,0x00,
0x01,0x01,0x00,0xa3,0x84,0x00,0x00,0x00,0x13,0x04,0x0b,0x6f,
0x62,0x6a,0x65,0x63,0x74,0x43,0x6c,0x61,0x73,0x73,0x04,0x04,
0x75,0x73,0x65,0x72,0x30,0x84,0x00,0x00,0x00,0x0d,0x04,0x0b,
0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x4e,0x61,0x6d,0x65);
soc = open_sock_tcp(port);
if(!soc)return NULL;
send(socket:soc, data:req);
buf = recv(socket:soc, length:1);
if( buf == NULL )return NULL;
close(soc);
if(strlen(buf) == 1) {
response = hexstr(buf);
if(response =~ "^30$" )return TRUE;
}
return NULL;
}
# extract the message length
# WARNING offset is updated by the function call (reference) !
function get_ber_size(buf, offset) {
local_var lm_length, length_length, i;
lm_length = ord(buf[offset]);
offset++;
if(lm_length > 128) {
# undetermined length message
length_length = lm_length - 128;
lm_length = 0;
for(i=0; i<length_length; i++) {
lm_length = (lm_length << 8) | ord(buf[offset++]);
}
}
return lm_length;
}
function is_ldapv3(port) {
local_var offset, lm_length, messageId_length, bindResponse_length, resultCode_length, resultCode, i, soc, buf;
soc = open_sock_tcp(port);
if(!soc) return FALSE;
req = raw_string(0x30,0x0c,0x02,0x01,0x01,0x60,0x07,0x02,0x01,0x03,0x04,0x00,0x80,0x00); # v3 bind
send(socket:soc, data:req);
buf = recv(socket:soc, length:128);
close(soc);
if(!buf) return FALSE;
# decode ldapMessage length (encoded as BER)
offset = 0;
if(ord(buf[offset++]) != 48) return FALSE; # (0x30)
lm_length = get_ber_size(buf, offset);
if (strlen(buf) < lm_length + offset) return FALSE; # whoops, we have not enough data (should never happen since bindResponse is a short message)
# we are not at offset = message id, we skip it
if (ord(buf[offset++]) != 2) return FALSE; # messageId is an INT
messageId_length = get_ber_size(buf, offset);
offset += messageId_length;
# now enter the bindResponse
if (ord(buf[offset++]) != 97) return FALSE; # (0x61)
bindResponse_length = get_ber_size(buf, offset);
# now dig into response code
if (ord(buf[offset++]) != 10) return FALSE; # (0x0A)
resultCode_length = get_ber_size(buf, offset);
resultCode = 0;
for (i=0; i<resultCode_length; i++) {
resultCode = (resultCode << 8) | ord(buf[offset++]);
}
if (resultCode == 0) return TRUE; # server has accepted the v3 bind
return FALSE;
}
--- ldap.inc.old 2012-03-28 11:53:59.511051142 +0200
+++ ldap.inc 2012-03-28 13:59:40.056263026 +0200
@@ -52,23 +52,60 @@
return NULL;
}
-function is_ldapv3(port) {
-
- soc = open_sock_tcp(port);
- if(!soc)return FALSE;
-
- req = raw_string(0x30,0x0c,0x02,0x01,0x01,0x60,0x07,0x02,0x01,0x03,0x04,0x00,0x80,0x00); # v3 bind
- send(socket:soc, data:req);
+# extract the message length
+# WARNING offset is updated by the function call (reference) !
+function get_ber_size(buf, offset) {
+ local_var lm_length, length_length, i;
+ lm_length = ord(buf[offset]);
+ offset++;
+ if(lm_length > 128) {
+ # undetermined length message
+ length_length = lm_length - 128;
+ lm_length = 0;
+ for(i=0; i<length_length; i++) {
+ lm_length = (lm_length << 8) | ord(buf[offset++]);
+ }
+ }
+ return lm_length;
+}
- buf = recv(socket:soc, length:128);
- close(soc);
+function is_ldapv3(port) {
+ local_var offset, lm_length, messageId_length, bindResponse_length, resultCode_length, resultCode, i, soc, buf;
- if(!buf)return FALSE;
+ soc = open_sock_tcp(port);
+ if(!soc) return FALSE;
- if(ord(buf[9]) == 0 && ord(buf[10]) == 4 && ord(buf[11] == 0)) { # ord(buf[9] == bindResponse -> resultCode. 0 == success, >0 == error
- return TRUE;
- }
+ req = raw_string(0x30,0x0c,0x02,0x01,0x01,0x60,0x07,0x02,0x01,0x03,0x04,0x00,0x80,0x00); # v3 bind
+ send(socket:soc, data:req);
- return FALSE;
+ buf = recv(socket:soc, length:128);
+ close(soc);
+
+ if(!buf) return FALSE;
+
+ # decode ldapMessage length (encoded as BER)
+ offset = 0;
+ if(ord(buf[offset++]) != 48) return FALSE; # (0x30)
+ lm_length = get_ber_size(buf, offset);
+ if (strlen(buf) < lm_length + offset) return FALSE; # whoops, we have not enough data (should never happen since bindResponse is a short message)
+
+ # we are not at offset = message id, we skip it
+ if (ord(buf[offset++]) != 2) return FALSE; # messageId is an INT
+ messageId_length = get_ber_size(buf, offset);
+ offset += messageId_length;
+
+ # now enter the bindResponse
+ if (ord(buf[offset++]) != 97) return FALSE; # (0x61)
+ bindResponse_length = get_ber_size(buf, offset);
+
+ # now dig into response code
+ if (ord(buf[offset++]) != 10) return FALSE; # (0x0A)
+ resultCode_length = get_ber_size(buf, offset);
+ resultCode = 0;
+ for (i=0; i<resultCode_length; i++) {
+ resultCode = (resultCode << 8) | ord(buf[offset++]);
+ }
+ if (resultCode == 0) return TRUE; # server has accepted the v3 bind
+ return FALSE;
}
ldap-bindv3-openldap.pcap
Description: Binary data
ldap-bindv3-win2k.pcap
Description: Binary data
_______________________________________________ Openvas-plugins mailing list [email protected] http://lists.wald.intevation.org/cgi-bin/mailman/listinfo/openvas-plugins
