@@ -209,8 +209,25 @@ def ldap3_kerberos_login(connection, target, user, password, domain='', lmhash='
209
209
connection .bound = True
210
210
211
211
return True
212
+ # Modified by azox: added fallback to SIMPLE bind and LDAP error code printing for better debug
213
+
214
+
215
+ LDAP_ERROR_CODES = {
216
+ '525' : 'User not found' ,
217
+ '530' : 'Not permitted to logon at this time' ,
218
+ '531' : 'Not permitted to logon from this workstation' ,
219
+ '532' : 'Password expired' ,
220
+ '533' : 'Account disabled' ,
221
+ '701' : 'Account expired' ,
222
+ '773' : 'User must reset password' ,
223
+ '775' : 'Account locked'
224
+ }
225
+
212
226
213
227
def _init_ldap_connection (target , tls_version , domain , username , password , lmhash , nthash , k , dc_ip , aesKey ):
228
+ from ldap3 .core .exceptions import LDAPBindError
229
+ import re
230
+
214
231
user = '%s\\ %s' % (domain , username )
215
232
connect_to = target
216
233
if dc_ip is not None :
@@ -224,13 +241,37 @@ def _init_ldap_connection(target, tls_version, domain, username, password, lmhas
224
241
port = 389
225
242
tls = None
226
243
ldap_server = ldap3 .Server (connect_to , get_info = ldap3 .ALL , port = port , use_ssl = use_ssl , tls = tls )
244
+
227
245
if k :
228
246
ldap_session = ldap3 .Connection (ldap_server )
229
247
ldap_session .bind ()
230
248
ldap3_kerberos_login (ldap_session , target , username , password , domain , lmhash , nthash , aesKey , kdcHost = dc_ip )
231
249
elif lmhash == '' and nthash == '' :
232
- ldap_session = ldap3 .Connection (ldap_server , user = user , password = password , authentication = ldap3 .NTLM , auto_bind = True )
250
+ # Try NTLM bind first without auto_bind to handle fallback
251
+ ldap_session = ldap3 .Connection (ldap_server , user = user , password = password , authentication = ldap3 .NTLM , auto_bind = False )
252
+ try :
253
+ if ldap_session .bind ():
254
+ print ("[+] NTLM bind succeeded." ) # Informative message on successful NTLM bind
255
+ else :
256
+ # Extract and print LDAP bind error code if available
257
+ error_message = ldap_session .result .get ('message' , '' )
258
+ match = re .search (r'data\s+([0-9a-f]{3})' , error_message , re .IGNORECASE )
259
+ if match :
260
+ data_code = match .group (1 ).lower ()
261
+ explanation = LDAP_ERROR_CODES .get (data_code , 'Unknown LDAP error code' )
262
+ print (f"[!] NTLM bind failed with LDAP error code: { data_code } ({ explanation } )" )
263
+ else :
264
+ print ("[!] NTLM bind failed with unknown error." )
265
+
266
+ # Fallback to SIMPLE bind using UPN format (username@domain)
267
+ user_upn = f"{ username } @{ domain } "
268
+ ldap_session = ldap3 .Connection (ldap_server , user = user_upn , password = password , authentication = ldap3 .SIMPLE , auto_bind = True )
269
+ print ("[*] SIMPLE bind succeeded." ) # Informative message on fallback success
270
+ except LDAPBindError as e :
271
+ print (f"[!] LDAP bind failed: { e } " ) # General bind failure catch
272
+ raise
233
273
else :
274
+ # Bind using NTLM and password hash
234
275
ldap_session = ldap3 .Connection (ldap_server , user = user , password = lmhash + ":" + nthash , authentication = ldap3 .NTLM , auto_bind = True )
235
276
236
277
return ldap_server , ldap_session
0 commit comments