Skip to content

Commit 71ccfd9

Browse files
authored
Merge pull request Pennyw0rth#709 from Pennyw0rth/ldap_checker_removal
Ldap checker removal
2 parents 1c1194a + 155553a commit 71ccfd9

6 files changed

Lines changed: 67 additions & 12 deletions

File tree

nxc/modules/ldap-checker.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class NXCModule:
2323
Original work thankfully taken from @zyn3rgy's Ldap Relay Scan project: https://github.com/zyn3rgy/LdapRelayScan
2424
"""
2525
name = "ldap-checker"
26-
description = "Checks whether LDAP signing and channel binding are required and / or enforced"
26+
description = "[REMOVED] Checks whether LDAP signing and channel binding are required and / or enforced"
2727
supported_protocols = ["ldap"]
2828
opsec_safe = True
2929
multiple_hosts = True
@@ -173,6 +173,8 @@ async def run_ldap(self, context, target, credential):
173173
# Determine authentication context and proceed to
174174
# enumerate LDAP signing and channel binding settings
175175
def on_login(self, context, connection):
176+
context.log.fail("[REMOVED] Now natively supported in the host banner")
177+
return
176178
stype = asyauthSecret.PASS
177179
secret = connection.password
178180
if connection.nthash:

nxc/protocols/ldap.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ def __init__(self, args, db, host):
149149
self.output_filename = None
150150
self.smbv1 = None
151151
self.signing = False
152+
self.signing_required = None
153+
self.cbt_status = None
152154
self.admin_privs = False
153155
self.no_ntlm = False
154156
self.sid_domain = ""
@@ -232,6 +234,52 @@ def get_ldap_username(self):
232234
return value.split("\\")[1]
233235
return ""
234236

237+
def check_ldap_signing(self):
238+
self.signing_required = False
239+
ldap_url = f"ldap://{self.target}"
240+
try:
241+
ldap_connection = ldap_impacket.LDAPConnection(url=ldap_url, baseDN=self.baseDN, dstIp=self.host, signing=False)
242+
ldap_connection.login(domain=self.domain)
243+
self.logger.debug(f"LDAP signing is not enforced on {self.host}")
244+
except ldap_impacket.LDAPSessionError as e:
245+
if str(e).find("strongerAuthRequired") >= 0:
246+
self.logger.debug(f"LDAP signing is enforced on {self.host}")
247+
self.signing_required = True
248+
else:
249+
self.logger.debug(f"LDAPSessionError while checking for signing requirements (likely NTLM disabled): {e!s}")
250+
251+
def check_ldaps_cbt(self):
252+
self.cbt_status = "Never"
253+
ldap_url = f"ldaps://{self.target}"
254+
try:
255+
ldap_connection = ldap_impacket.LDAPConnection(url=ldap_url, baseDN=self.baseDN, dstIp=self.host)
256+
ldap_connection._LDAPConnection__channel_binding_value = None
257+
ldap_connection.login(user=" ", domain=self.domain)
258+
except ldap_impacket.LDAPSessionError as e:
259+
if str(e).find("data 80090346") >= 0:
260+
self.logger.debug(f"LDAPS channel binding enforced on host {self.host}")
261+
self.cbt_status = "Always" # CBT is Required
262+
# Login failed (wrong credentials). test if we get an error with an existing, but wrong CBT -> When supported
263+
elif str(e).find("data 52e") >= 0:
264+
ldap_connection = ldap_impacket.LDAPConnection(url=ldap_url, baseDN=self.baseDN, dstIp=self.host)
265+
new_cbv = bytearray(ldap_connection._LDAPConnection__channel_binding_value)
266+
new_cbv[15] = (new_cbv[3] + 1) % 256
267+
ldap_connection._LDAPConnection__channel_binding_value = bytes(new_cbv)
268+
try:
269+
ldap_connection.login(user=" ", domain=self.domain)
270+
except ldap_impacket.LDAPSessionError as e:
271+
if str(e).find("data 80090346") >= 0:
272+
self.logger.debug(f"LDAPS channel binding is set to 'When Supported' on host {self.host}")
273+
self.cbt_status = "When Supported" # CBT is When Supported
274+
else:
275+
self.logger.debug(f"LDAPSessionError while checking for channel binding requirements (likely NTLM disabled): {e!s}")
276+
except SysCallError as e:
277+
self.logger.debug(f"Received SysCallError when trying to enumerate channel binding support: {e!s}")
278+
if e.args[1] == "ECONNRESET":
279+
self.cbt_status = "No TLS cert"
280+
else:
281+
raise
282+
235283
def enum_host_info(self):
236284
self.hostname = self.target.split(".")[0].upper() if "." in self.target else self.target
237285
self.remoteName = self.target
@@ -262,6 +310,9 @@ def enum_host_info(self):
262310
else:
263311
self.domain = self.targetDomain
264312

313+
self.check_ldap_signing()
314+
self.check_ldaps_cbt()
315+
265316
# using kdcHost is buggy on impacket when using trust relation between ad so we kdcHost must stay to none if targetdomain is not equal to domain
266317
if not self.kdcHost and self.domain and self.domain == self.targetDomain:
267318
result = self.resolver(self.domain)
@@ -282,10 +333,14 @@ def enum_host_info(self):
282333

283334
def print_host_info(self):
284335
self.logger.debug("Printing host info for LDAP")
336+
signing = colored("signing:Enforced", host_info_colors[0], attrs=["bold"]) if self.signing_required else colored("signing:None", host_info_colors[1], attrs=["bold"])
337+
cbt_status = colored(f"channel binding:{self.cbt_status}", host_info_colors[3], attrs=["bold"]) if self.cbt_status == "Always" else colored(f"channel binding:{self.cbt_status}", host_info_colors[2], attrs=["bold"])
338+
ntlm = colored(f"(NTLM:{not self.no_ntlm})", host_info_colors[2], attrs=["bold"]) if self.no_ntlm else ""
339+
285340
self.logger.extra["protocol"] = "LDAP" if str(self.port) == "389" else "LDAPS"
286341
self.logger.extra["port"] = self.port
287342
self.logger.extra["hostname"] = self.hostname
288-
self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.domain})")
343+
self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.domain}) ({signing}) ({cbt_status}) {ntlm}")
289344

290345
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
291346
self.username = username if not self.username else self.username # With ccache we get the username from the ticket
@@ -1304,7 +1359,7 @@ def bloodhound(self):
13041359
self.logger.fail("Your configuration has BloodHound-CE enabled, but the regular BloodHound package is installed. Modify your ~/.nxc/nxc.conf config file or follow the instructions:")
13051360
self.logger.fail("Please run the following commands to fix this:")
13061361
self.logger.fail("poetry remove bloodhound-ce # poetry falsely recognizes bloodhound-ce as a the old bloodhound package")
1307-
self.logger.fail("poetry add bloodhound-ce")
1362+
self.logger.fail("poetry add bloodhound-ce")
13081363
self.logger.fail("")
13091364

13101365
# If using pipx

nxc/protocols/ldap/db_navigator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,4 @@ def help_clear_database(self):
188188
THIS COMPLETELY DESTROYS ALL DATA IN THE CURRENTLY CONNECTED DATABASE
189189
YOU CANNOT UNDO THIS COMMAND
190190
"""
191-
print_help(help_string)
191+
print_help(help_string)

poetry.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ dependencies = [
2828
"lsassy>=3.1.11",
2929
"masky>=0.2.0",
3030
"minikerberos>=0.4.1",
31-
"msldap>=0.5.10",
3231
"neo4j>=5.0.0",
3332
"paramiko>=3.3.1",
3433
"pyasn1-modules>=0.3.0",

tests/e2e_commands.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,6 @@ netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M get-des
198198
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M get-network
199199
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M groupmembership -o USER=LOGIN_USERNAME
200200
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M laps
201-
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M ldap-checker
202201
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M maq
203202
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M subnets
204203
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -M user-desc

0 commit comments

Comments
 (0)