Skip to content

Commit b6a3a36

Browse files
authored
Merge pull request #80 from azoxlpf/Patch_RBCD.py
Add fallback to SIMPLE bind when NTLM fails (e.g. userWorkstations restriction)
2 parents 8779a1e + dc562a7 commit b6a3a36

File tree

1 file changed

+42
-1
lines changed

1 file changed

+42
-1
lines changed

impacket/examples/utils.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,25 @@ def ldap3_kerberos_login(connection, target, user, password, domain='', lmhash='
209209
connection.bound = True
210210

211211
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+
212226

213227
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+
214231
user = '%s\\%s' % (domain, username)
215232
connect_to = target
216233
if dc_ip is not None:
@@ -224,13 +241,37 @@ def _init_ldap_connection(target, tls_version, domain, username, password, lmhas
224241
port = 389
225242
tls = None
226243
ldap_server = ldap3.Server(connect_to, get_info=ldap3.ALL, port=port, use_ssl=use_ssl, tls=tls)
244+
227245
if k:
228246
ldap_session = ldap3.Connection(ldap_server)
229247
ldap_session.bind()
230248
ldap3_kerberos_login(ldap_session, target, username, password, domain, lmhash, nthash, aesKey, kdcHost=dc_ip)
231249
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
233273
else:
274+
# Bind using NTLM and password hash
234275
ldap_session = ldap3.Connection(ldap_server, user=user, password=lmhash + ":" + nthash, authentication=ldap3.NTLM, auto_bind=True)
235276

236277
return ldap_server, ldap_session

0 commit comments

Comments
 (0)