@@ -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
0 commit comments