From 416affa6e979606e583b6caaecf73b1f31110a9b Mon Sep 17 00:00:00 2001 From: SkelSec Date: Wed, 13 Mar 2024 20:40:36 +0100 Subject: [PATCH] helping signing scanners, adding timeout --- msldap/_version.py | 2 +- msldap/client.py | 6 ++++++ msldap/connection.py | 25 +++++++++++++++++++++---- msldap/examples/msldapclient.py | 23 +++++++++++++++++++++++ msldap/ldap_objects/adinfo.py | 8 +++++++- 5 files changed, 58 insertions(+), 6 deletions(-) diff --git a/msldap/_version.py b/msldap/_version.py index 3f46165..f123bc6 100644 --- a/msldap/_version.py +++ b/msldap/_version.py @@ -1,5 +1,5 @@ -__version__ = "0.5.9" +__version__ = "0.5.10" __banner__ = \ """ # msldap %s diff --git a/msldap/client.py b/msldap/client.py index 8f76b5e..02650fc 100644 --- a/msldap/client.py +++ b/msldap/client.py @@ -51,6 +51,9 @@ def __init__(self, target:MSLDAPTarget, creds:UniCredential, connection = None, self.target = target self.keepalive = keepalive self.ldap_query_page_size = 1000 + self._disable_channel_binding = False + self._disable_signing = False + self._null_channel_binding = False if self.target is not None: self.ldap_query_page_size = self.target.ldap_query_page_size @@ -109,6 +112,9 @@ async def connect(self): self.disconnected_evt = asyncio.Event() if self._con is None: self._con = MSLDAPClientConnection(self.target, self.creds) + self._con._disable_channel_binding = self._disable_channel_binding + self._con._disable_signing = self._disable_signing + self._con._null_channel_binding = self._null_channel_binding _, err = await self._con.connect() if err is not None: raise err diff --git a/msldap/connection.py b/msldap/connection.py index b4562ee..ac20331 100644 --- a/msldap/connection.py +++ b/msldap/connection.py @@ -49,6 +49,9 @@ def __init__(self, target:MSLDAPTarget, credential:UniCredential, auth=None): self.message_table_notify = {} self.encryption_sequence_counter = 0 # this will be set by the inderlying auth algo self.cb_data = None #for channel binding + self._disable_channel_binding = False # putting it here for scanners to be able to turn it off + self._disable_signing = False + self._null_channel_binding = False if self.credential.protocol == asyauthProtocol.NONE: self.is_anon = True @@ -187,12 +190,15 @@ async def connect(self): self.connection_closed_evt = asyncio.Event() packetizer = LDAPPacketizer() client = UniClient(self.target, packetizer) - self.network = await client.connect() + self.network = await asyncio.wait_for(client.connect(), timeout=self.target.timeout) # now processing channel binding options - if self.target.protocol == UniProto.CLIENT_SSL_TCP: - certdata = self.network.get_peer_certificate() - self.cb_data = b'tls-server-end-point:' + sha256(certdata).digest() + if self.target.protocol == UniProto.CLIENT_SSL_TCP and self._disable_channel_binding is False: + if self._null_channel_binding is True: + self.cb_data = b'tls-server-end-point:' + sha256(b'').digest() + else: + certdata = self.network.get_peer_certificate() + self.cb_data = b'tls-server-end-point:' + sha256(certdata).digest() self.handle_incoming_task = asyncio.create_task(self.__handle_incoming()) self.status = MSLDAPClientStatus.CONNECTED @@ -250,6 +256,12 @@ async def bind(self): flags = ISC_REQ.CONNECTION|ISC_REQ.CONFIDENTIALITY|ISC_REQ.INTEGRITY if self.target.protocol == UniProto.CLIENT_SSL_TCP: flags = ISC_REQ.CONNECTION + + # this switch is for the case when we are using NTLM and we want to disable signing + # useful for testing if the server supports it + if self._disable_signing is True: + flags = ISC_REQ.CONNECTION + data, to_continue, err = await self.auth.authenticate(None, spn=self.target.to_target_string(), flags=flags, cb_data = self.cb_data) if err is not None: return None, err @@ -385,6 +397,11 @@ async def bind(self): if self.target.protocol == UniProto.CLIENT_SSL_TCP: flags = ISC_REQ.CONNECTION + # this switch is for the case when we are using NTLM and we want to disable signing + # useful for testing if the server supports it + if self.credential.protocol == asyauthProtocol.NTLM and self._disable_signing is True: + flags = ISC_REQ.CONNECTION + data, to_continue, err = await self.auth.authenticate(challenge, cb_data = self.cb_data, spn=self.target.to_target_string(), flags=flags) if err is not None: raise err diff --git a/msldap/examples/msldapclient.py b/msldap/examples/msldapclient.py index 7622778..81d8419 100644 --- a/msldap/examples/msldapclient.py +++ b/msldap/examples/msldapclient.py @@ -56,6 +56,11 @@ def __init__(self, url = None): self.adinfo = None self.ldapinfo = None self.domain_name = None + self.noisig = False + self.nocb = False + self._disable_channel_binding = False + self._disable_signing = False + self._null_channel_binding = False self.__current_dirs = {} self._current_dn = None @@ -73,6 +78,9 @@ async def do_login(self, url = None): self.connection = self.conn_url.get_client() self.connection.keepalive = True + self.connection._disable_channel_binding = self._disable_channel_binding + self.connection._disable_signing = self._disable_signing + self.connection._null_channel_binding = self._null_channel_binding _, err = await self.connection.connect() if err is not None: raise err @@ -88,6 +96,21 @@ async def do_login(self, url = None): except: traceback.print_exc() return False + + async def do_nosig(self): + """Disables signing""" + self._disable_signing = True + return True + + async def do_nocb(self): + """Disables channel binding""" + self._disable_channel_binding = True + return True + + async def do_nullcb(self): + """Sets incorrect channel binding data""" + self._null_channel_binding = True + return True async def do_ldapinfo(self, show = True): """Prints detailed LDAP connection info (DSA)""" diff --git a/msldap/ldap_objects/adinfo.py b/msldap/ldap_objects/adinfo.py index 0ffb149..cb20974 100644 --- a/msldap/ldap_objects/adinfo.py +++ b/msldap/ldap_objects/adinfo.py @@ -15,7 +15,7 @@ 'objectGUID', 'objectSid', 'pwdHistoryLength', 'pwdProperties', 'serverState', 'systemFlags', 'uASCompat', 'uSNChanged', 'uSNCreated', 'whenChanged', 'whenCreated', 'rIDManagerReference', - 'msDS-Behavior-Version', 'description', 'isDeleted', 'gPLink' + 'msDS-Behavior-Version', 'description', 'isDeleted', 'gPLink', 'ms-DS-MachineAccountQuota' ] class MSADInfo: @@ -54,6 +54,7 @@ def __init__(self): self.description = None self.isDeleted = None self.gPLink = None + self.machineAccountQuota = None @staticmethod def from_ldap(entry): @@ -91,6 +92,7 @@ def from_ldap(entry): adi.description = entry['attributes'].get('description') adi.isDeleted = entry['attributes'].get('isDeleted') adi.gPLink = entry['attributes'].get('gPLink') + adi.machineAccountQuota = entry['attributes'].get('ms-DS-MachineAccountQuota') #https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/564dc969-6db3-49b3-891a-f2f8d0a68a7f adi.domainmodelevel = entry['attributes'].get('msDS-Behavior-Version') @@ -130,6 +132,7 @@ def to_dict(self): d['whenCreated'] = self.whenCreated d['domainmodelevel'] = self.domainmodelevel d['description'] = self.description + d['ms-DS-MachineAccountQuota'] = self.machineAccountQuota return d @@ -166,6 +169,9 @@ def __str__(self): t += 'whenCreated: %s\n' % self.whenCreated t += 'domainmodelevel: %s\n' % self.domainmodelevel t += 'description: %s\n' % self.description + t += 'isDeleted: %s\n' % self.isDeleted + t += 'gPLink: %s\n' % self.gPLink + t += 'ms-DS-MachineAccountQuota: %s\n' % self.machineAccountQuota return t def get_row(self, attrs):