3131from impacket .ldap import ldaptypes
3232from impacket .ldap import ldapasn1 as ldapasn1_impacket
3333from impacket .ldap .ldap import LDAPFilterSyntaxError
34- from impacket .smb import SMB_DIALECT
3534from impacket .smbconnection import SMBConnection , SessionError
35+ from impacket .ntlm import getNTLMSSPType1
3636
3737from nxc .config import process_secret , host_info_colors
3838from nxc .connection import connection
4242from nxc .protocols .ldap .gmsa import MSDS_MANAGEDPASSWORD_BLOB
4343from nxc .protocols .ldap .kerberos import KerberosAttacks
4444from nxc .parsers .ldap_results import parse_result_attributes
45+ from nxc .helpers .ntlm_parser import parse_challenge
4546
4647ldap_error_status = {
4748 "1" : "STATUS_NOT_SUPPORTED" ,
@@ -163,25 +164,25 @@ def proto_logger(self):
163164 }
164165 )
165166
166- def get_ldap_info (self , host ):
167+ def create_conn_obj (self ):
167168 try :
168169 proto = "ldaps" if (self .args .gmsa or self .port == 636 ) else "ldap"
169- ldap_url = f"{ proto } ://{ host } "
170+ ldap_url = f"{ proto } ://{ self . host } "
170171 self .logger .info (f"Connecting to { ldap_url } with no baseDN" )
171172 try :
172- ldap_connection = ldap_impacket .LDAPConnection (ldap_url , dstIp = self .host )
173- if ldap_connection :
174- self .logger .debug (f"ldap_connection: { ldap_connection } " )
173+ self . ldap_connection = ldap_impacket .LDAPConnection (ldap_url , dstIp = self .host )
174+ if self . ldap_connection :
175+ self .logger .debug (f"ldap_connection: { self . ldap_connection } " )
175176 except SysCallError as e :
176177 if proto == "ldaps" :
177178 self .logger .fail (f"LDAPs connection to { ldap_url } failed - { e } " )
178179 # https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/enable-ldap-over-ssl-3rd-certification-authority
179180 self .logger .fail ("Even if the port is open, LDAPS may not be configured" )
180181 else :
181182 self .logger .fail (f"LDAP connection to { ldap_url } failed: { e } " )
182- exit ( 1 )
183+ return False
183184
184- resp = ldap_connection .search (
185+ resp = self . ldap_connection .search (
185186 scope = ldapasn1_impacket .Scope ("baseObject" ),
186187 attributes = ["defaultNamingContext" , "dnsHostName" ],
187188 sizeLimit = 0 ,
@@ -208,42 +209,18 @@ def get_ldap_info(self, host):
208209 self .logger .debug ("Exception:" , exc_info = True )
209210 self .logger .info (f"Skipping item, cannot process due to error { e } " )
210211 except OSError :
211- return [ None , None , None ]
212+ return False
212213 self .logger .debug (f"Target: { target } ; target_domain: { target_domain } ; base_dn: { base_dn } " )
213- return [target , target_domain , base_dn ]
214-
215- def get_os_arch (self ):
216- try :
217- string_binding = rf"ncacn_ip_tcp:{ self .host } [135]"
218- transport = DCERPCTransportFactory (string_binding )
219- transport .setRemoteHost (self .host )
220- transport .set_connect_timeout (5 )
221- dce = transport .get_dce_rpc ()
222- if self .args .kerberos :
223- dce .set_auth_type (RPC_C_AUTHN_GSS_NEGOTIATE )
224- dce .connect ()
225- try :
226- dce .bind (
227- MSRPC_UUID_PORTMAP ,
228- transfer_syntax = ("71710533-BEBA-4937-8319-B5DBEF9CCC36" , "1.0" ),
229- )
230- except DCERPCException as e :
231- if str (e ).find ("syntaxes_not_supported" ) >= 0 :
232- dce .disconnect ()
233- return 32
234- else :
235- dce .disconnect ()
236- return 64
237- except Exception as e :
238- self .logger .fail (f"Error retrieving os arch of { self .host } : { e !s} " )
239-
240- return 0
214+ self .target = target
215+ self .targetDomain = target_domain
216+ self .baseDN = base_dn
217+ return True
241218
242219 def get_ldap_username (self ):
243220 extended_request = ldapasn1_impacket .ExtendedRequest ()
244221 extended_request ["requestName" ] = "1.3.6.1.4.1.4203.1.11.3" # whoami
245222
246- response = self .ldapConnection .sendReceive (extended_request )
223+ response = self .ldap_connection .sendReceive (extended_request )
247224 for message in response :
248225 search_result = message ["protocolOp" ].getComponent ()
249226 if search_result ["resultCode" ] == ldapasn1_impacket .ResultCode ("success" ):
@@ -254,46 +231,26 @@ def get_ldap_username(self):
254231 return ""
255232
256233 def enum_host_info (self ):
257- self .target , self .targetDomain , self .baseDN = self .get_ldap_info (self .host )
258234 self .baseDN = self .args .base_dn if self .args .base_dn else self .baseDN # Allow overwriting baseDN from args
259235 self .hostname = self .target
260236 self .remoteName = self .target
261237 self .domain = self .targetDomain
262- # smb no open, specify the domain
263- if not self .args .no_smb :
264- self .local_ip = self .conn .getSMBServer ().get_socket ().getsockname ()[0 ]
265238
266- try :
267- self .conn .login ("" , "" )
268- except BrokenPipeError as e :
269- self .logger .fail (f"Broken Pipe Error while attempting to login: { e } " )
270- except Exception as e :
271- if "STATUS_NOT_SUPPORTED" in str (e ):
272- self .no_ntlm = True
273- if not self .no_ntlm :
274- self .hostname = self .conn .getServerName ()
275- self .targetDomain = self .domain = self .conn .getServerDNSDomainName ()
276- self .server_os = self .conn .getServerOS ()
277- self .signing = self .conn .isSigningRequired () if self .smbv1 else self .conn ._SMBConnection ._Connection ["RequireSigning" ]
278- self .os_arch = self .get_os_arch ()
279- self .logger .extra ["hostname" ] = self .hostname
280-
281- if not self .domain :
282- self .domain = self .hostname
283- if self .args .domain :
284- self .domain = self .args .domain
285- if self .args .local_auth :
286- self .domain = self .hostname
287- self .remoteName = self .host if not self .kerberos else f"{ self .hostname } .{ self .domain } "
288-
289- try : # noqa: SIM105
290- # DC's seem to want us to logoff first, windows workstations sometimes reset the connection
291- self .conn .logoff ()
292- except Exception :
293- pass
294-
295- # Re-connect since we logged off
296- self .create_conn_obj ()
239+ ntlm_challenge = None
240+ bindRequest = ldapasn1_impacket .BindRequest ()
241+ bindRequest ['version' ] = 3
242+ bindRequest ['name' ] = ""
243+ negotiate = getNTLMSSPType1 ()
244+ bindRequest ['authentication' ]['sicilyNegotiate' ] = negotiate .getData ()
245+ try :
246+ response = self .ldap_connection .sendReceive (bindRequest )[0 ]['protocolOp' ]
247+ ntlm_challenge = bytes (response ['bindResponse' ]['matchedDN' ])
248+ except Exception as e :
249+ self .logger .debug (f"Failed to get target { self .host } ntlm challenge, error: { e !s} " )
250+
251+ if ntlm_challenge :
252+ ntlm_info = parse_challenge (ntlm_challenge )
253+ self .server_os = ntlm_info ["os_version" ]
297254
298255 if not self .kdcHost and self .domain :
299256 result = self .resolver (self .domain )
@@ -304,17 +261,10 @@ def enum_host_info(self):
304261
305262 def print_host_info (self ):
306263 self .logger .debug ("Printing host info for LDAP" )
307- if self .args .no_smb :
308- self .logger .extra ["protocol" ] = "LDAP" if self .port == 389 else "LDAPS"
309- self .logger .extra ["port" ] = self .port
310- self .logger .display (f'{ self .baseDN } (Hostname: { self .hostname .split ("." )[0 ]} ) (domain: { self .domain } )' )
311- else :
312- self .logger .extra ["protocol" ] = "SMB" if not self .no_ntlm else "LDAP"
313- self .logger .extra ["port" ] = "445" if not self .no_ntlm else "389"
314- signing = colored (f"signing:{ self .signing } " , host_info_colors [0 ], attrs = ["bold" ]) if self .signing else colored (f"signing:{ self .signing } " , host_info_colors [1 ], attrs = ["bold" ])
315- smbv1 = colored (f"SMBv1:{ self .smbv1 } " , host_info_colors [2 ], attrs = ["bold" ]) if self .smbv1 else colored (f"SMBv1:{ self .smbv1 } " , host_info_colors [3 ], attrs = ["bold" ])
316- self .logger .display (f"{ self .server_os } { f' x{ self .os_arch } ' if self .os_arch else '' } (name:{ self .hostname } ) (domain:{ self .targetDomain } ) ({ signing } ) ({ smbv1 } )" )
317- self .logger .extra ["protocol" ] = "LDAP"
264+ self .logger .extra ["protocol" ] = "LDAP" if str (self .port ) == "389" else "LDAPS"
265+ self .logger .extra ["port" ] = self .port
266+ self .logger .extra ["hostname" ] = self .target .split ("." )[0 ].upper ()
267+ self .logger .display (f"{ self .server_os } (name:{ self .hostname } ) (domain:{ self .domain } )" )
318268
319269 def kerberos_login (self , domain , username , password = "" , ntlm_hash = "" , aesKey = "" , kdcHost = "" , useCache = False ):
320270 self .username = username
@@ -594,40 +544,6 @@ def hash_login(self, domain, username, ntlm_hash):
594544 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?' } \n Error: { e } " )
595545 return False
596546
597- def create_smbv1_conn (self ):
598- self .logger .debug ("Creating smbv1 connection object" )
599- try :
600- self .conn = SMBConnection (self .host , self .host , None , 445 , preferredDialect = SMB_DIALECT )
601- self .smbv1 = True
602- if self .conn :
603- self .logger .debug ("SMBv1 Connection successful" )
604- except OSError as e :
605- if str (e ).find ("Connection reset by peer" ) != - 1 :
606- self .logger .debug (f"SMBv1 might be disabled on { self .host } " )
607- return False
608- except Exception as e :
609- self .logger .debug (f"Error creating SMBv1 connection to { self .host } : { e } " )
610- return False
611- return True
612-
613- def create_smbv3_conn (self ):
614- self .logger .debug ("Creating smbv3 connection object" )
615- try :
616- self .conn = SMBConnection (self .host , self .host , None , 445 )
617- self .smbv1 = False
618- if self .conn :
619- self .logger .debug ("SMBv3 Connection successful" )
620- except OSError :
621- return False
622- except Exception as e :
623- self .logger .debug (f"Error creating SMBv3 connection to { self .host } : { e } " )
624- return False
625-
626- return True
627-
628- def create_conn_obj (self ):
629- return bool (self .args .no_smb or self .create_smbv1_conn () or self .create_smbv3_conn ())
630-
631547 def get_sid (self ):
632548 self .logger .highlight (f"Domain SID { self .sid_domain } " )
633549
0 commit comments