From eb244948e691b2e26e05d127efd75001980d0a5a Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 24 Mar 2024 02:44:23 +0100 Subject: [PATCH 1/3] Add log message to explain potential ldap channel binding --- nxc/protocols/ldap.py | 80 +++++++++++-------------------------------- 1 file changed, 20 insertions(+), 60 deletions(-) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index b1e03cc6..4d142f98 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -307,16 +307,7 @@ class ldap(connection): self.logger.extra["protocol"] = "LDAP" return True - def kerberos_login( - self, - domain, - username, - password="", - ntlm_hash="", - aesKey="", - kdcHost="", - useCache=False, - ): + def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False): self.username = username self.password = password self.domain = domain @@ -350,20 +341,13 @@ class ldap(connection): try: # Connect to LDAP + self.logger.extra["protocol"] = "LDAPS" if (self.args.gmsa or self.port == 636) else "LDAP" + self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389" proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap" ldap_url = f"{proto}://{self.target}" self.logger.info(f"Connecting to {ldap_url} - {self.baseDN} [1]") self.ldapConnection = ldap_impacket.LDAPConnection(ldap_url, self.baseDN) - self.ldapConnection.kerberosLogin( - username, - password, - domain, - self.lmhash, - self.nthash, - aesKey, - kdcHost=kdcHost, - useCache=useCache, - ) + self.ldapConnection.kerberosLogin(username, password, domain, self.lmhash, self.nthash, aesKey, kdcHost=kdcHost, useCache=useCache) if self.username == "": self.username = self.get_ldap_username() @@ -371,11 +355,7 @@ class ldap(connection): self.check_if_admin() used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}" - out = f"{domain}\\{self.username}{used_ccache} {self.mark_pwned()}" - - self.logger.extra["protocol"] = "LDAP" - self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389" - self.logger.success(out) + self.logger.success(f"{domain}\\{self.username}{used_ccache} {self.mark_pwned()}") if not self.args.local_auth and self.username != "": add_user_bh(self.username, self.domain, self.logger, self.config) @@ -408,19 +388,12 @@ class ldap(connection): # We need to try SSL try: # Connect to LDAPS + self.logger.extra["protocol"] = "LDAPS" + self.logger.extra["port"] = "636" ldaps_url = f"ldaps://{self.target}" self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} [2]") self.ldapConnection = ldap_impacket.LDAPConnection(ldaps_url, self.baseDN) - self.ldapConnection.kerberosLogin( - username, - password, - domain, - self.lmhash, - self.nthash, - aesKey, - kdcHost=kdcHost, - useCache=useCache, - ) + self.ldapConnection.kerberosLogin(username, password, domain, self.lmhash, self.nthash, aesKey, kdcHost=kdcHost, useCache=useCache) if self.username == "": self.username = self.get_ldap_username() @@ -428,11 +401,7 @@ class ldap(connection): self.check_if_admin() # Prepare success credential text - out = f"{domain}\\{self.username} {self.mark_pwned()}" - - self.logger.extra["protocol"] = "LDAPS" - self.logger.extra["port"] = "636" - self.logger.success(out) + self.logger.success(f"{domain}\\{self.username} {self.mark_pwned()}") if not self.args.local_auth and self.username != "": add_user_bh(self.username, self.domain, self.logger, self.config) @@ -476,6 +445,8 @@ class ldap(connection): try: # Connect to LDAP + self.logger.extra["protocol"] = "LDAPS" if (self.args.gmsa or self.port == 636) else "LDAP" + self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389" proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap" ldap_url = f"{proto}://{self.target}" self.logger.debug(f"Connecting to {ldap_url} - {self.baseDN} [3]") @@ -484,11 +455,7 @@ class ldap(connection): self.check_if_admin() # Prepare success credential text - out = f"{domain}\\{self.username}:{process_secret(self.password)} {self.mark_pwned()}" - - self.logger.extra["protocol"] = "LDAP" - self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389" - self.logger.success(out) + self.logger.success(f"{domain}\\{self.username}:{process_secret(self.password)} {self.mark_pwned()}") if not self.args.local_auth and self.username != "": add_user_bh(self.username, self.domain, self.logger, self.config) @@ -500,23 +467,16 @@ class ldap(connection): # We need to try SSL try: # Connect to LDAPS + self.logger.extra["protocol"] = "LDAPS" + self.logger.extra["port"] = "636" ldaps_url = f"ldaps://{self.target}" self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN} [4]") self.ldapConnection = ldap_impacket.LDAPConnection(ldaps_url, self.baseDN) - self.ldapConnection.login( - self.username, - self.password, - self.domain, - self.lmhash, - self.nthash, - ) + self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash) self.check_if_admin() # Prepare success credential text - out = f"{domain}\\{self.username}:{process_secret(self.password)} {self.mark_pwned()}" - self.logger.extra["protocol"] = "LDAPS" - self.logger.extra["port"] = "636" - self.logger.success(out) + self.logger.success(f"{domain}\\{self.username}:{process_secret(self.password)} {self.mark_pwned()}") if not self.args.local_auth and self.username != "": add_user_bh(self.username, self.domain, self.logger, self.config) @@ -571,6 +531,8 @@ class ldap(connection): try: # Connect to LDAP + self.logger.extra["protocol"] = "LDAPS" if (self.args.gmsa or self.port == 636) else "LDAP" + self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389" proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap" ldaps_url = f"{proto}://{self.target}" self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN}") @@ -580,8 +542,6 @@ class ldap(connection): # Prepare success credential text out = f"{domain}\\{self.username}:{process_secret(self.nthash)} {self.mark_pwned()}" - self.logger.extra["protocol"] = "LDAP" - self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389" self.logger.success(out) if not self.args.local_auth and self.username != "": @@ -593,6 +553,8 @@ class ldap(connection): if str(e).find("strongerAuthRequired") >= 0: try: # We need to try SSL + self.logger.extra["protocol"] = "LDAPS" + self.logger.extra["port"] = "636" ldaps_url = f"{proto}://{self.target}" self.logger.debug(f"Connecting to {ldaps_url} - {self.baseDN}") self.ldapConnection = ldap_impacket.LDAPConnection(ldaps_url, self.baseDN) @@ -607,8 +569,6 @@ class ldap(connection): # Prepare success credential text out = f"{domain}\\{self.username}:{process_secret(self.nthash)} {self.mark_pwned()}" - self.logger.extra["protocol"] = "LDAPS" - self.logger.extra["port"] = "636" self.logger.success(out) if not self.args.local_auth and self.username != "": From 6a24d76db9e9b314385b8473db2efa4fc6150c54 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 24 Mar 2024 02:46:35 +0100 Subject: [PATCH 2/3] Now adding the message, oops --- nxc/protocols/ldap.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index 4d142f98..52ec675a 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -489,12 +489,15 @@ class ldap(connection): f"{self.domain}\\{self.username}:{process_secret(self.password)} {ldap_error_status[error_code] if error_code in ldap_error_status else ''}", color="magenta" if (error_code in ldap_error_status and error_code != 1) else "red", ) + self.logger.fail("LDAPS channel binding might be enabled, this is only supported with kerberos authentication. Try using '-k'.") else: error_code = str(e).split()[-2][:-1] self.logger.fail( f"{self.domain}\\{self.username}:{process_secret(self.password)} {ldap_error_status[error_code] if error_code in ldap_error_status else ''}", color="magenta" if (error_code in ldap_error_status and error_code != 1) else "red", ) + if proto == "ldaps": + self.logger.fail("LDAPS channel binding might be enabled, this is only supported with kerberos authentication. Try using '-k'.") return False except OSError as e: self.logger.fail(f"{self.domain}\\{self.username}:{process_secret(self.password)} {'Error connecting to the domain, are you sure LDAP service is running on the target?'} \nError: {e}") @@ -582,12 +585,15 @@ class ldap(connection): f"{self.domain}\\{self.username}:{process_secret(nthash)} {ldap_error_status[error_code] if error_code in ldap_error_status else ''}", color="magenta" if (error_code in ldap_error_status and error_code != 1) else "red", ) + self.logger.fail("LDAPS channel binding might be enabled, this is only supported with kerberos authentication. Try using '-k'.") else: error_code = str(e).split()[-2][:-1] self.logger.fail( f"{self.domain}\\{self.username}:{process_secret(nthash)} {ldap_error_status[error_code] if error_code in ldap_error_status else ''}", color="magenta" if (error_code in ldap_error_status and error_code != 1) else "red", ) + if proto == "ldaps": + self.logger.fail("LDAPS channel binding might be enabled, this is only supported with kerberos authentication. Try using '-k'.") return False except OSError as e: self.logger.fail(f"{self.domain}\\{self.username}:{process_secret(self.password)} {'Error connecting to the domain, are you sure LDAP service is running on the target?'} \nError: {e}") From 041645aed9e0b0c95dfbb2f94d902c4142d6af71 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 24 Mar 2024 02:46:58 +0100 Subject: [PATCH 3/3] Fix ldap not reporting DA when using hash login --- nxc/protocols/ldap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index 52ec675a..4f684027 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -661,7 +661,7 @@ class ldap(connection): attributes = ["objectSid"] resp = self.search(search_filter, attributes, sizeLimit=0) answers = [] - if resp and self.password != "" and self.username != "": + if resp and (self.password != "" or self.lmhash != "" or self.nthash != "") and self.username != "": for attribute in resp[0][1]: if str(attribute["type"]) == "objectSid": sid = self.sid_to_str(attribute["vals"][0])