Skip to content

Commit 18369b1

Browse files
committed
Remove ntlm hash for laps (as not compatible with py10) + add dns_server option to laps
1 parent 3c89bcb commit 18369b1

3 files changed

Lines changed: 23 additions & 23 deletions

File tree

nxc/connection.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ def __init__(self, args, db, target):
148148
self.kdcHost = self.args.kdcHost
149149
self.port = self.args.port
150150
self.local_ip = None
151+
self.dns_server = self.args.dns_server
151152

152153
# DNS resolution
153154
dns_result = self.resolver(target)
@@ -481,7 +482,7 @@ def try_credentials(self, domain, username, owned, secret, cred_type, data=None)
481482

482483
with sem:
483484
if cred_type == "plaintext":
484-
if self.args.kerberos:
485+
if self.args.kerberos and not hasattr(self.args, "laps"):
485486
self.logger.debug("Trying to authenticate using Kerberos")
486487
return self.kerberos_login(domain, username, secret, "", "", self.kdcHost, False)
487488
elif hasattr(self.args, "domain"): # Some protocols don't use domain for login
@@ -543,7 +544,7 @@ def login(self):
543544

544545
if hasattr(self.args, "laps") and self.args.laps:
545546
self.logger.debug("Trying to authenticate using LAPS")
546-
username[0], secret[0], domain[0], ntlm_hash = laps_search(self, username, secret, cred_type, domain)
547+
username[0], secret[0], domain[0] = laps_search(self, username, secret, cred_type, domain, self.dns_server)
547548
cred_type = ["plaintext"]
548549
if not (username[0] or secret[0] or domain[0]):
549550
return False

nxc/modules/laps.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def on_login(self, context, connection):
4545
values = {str(attr["type"]).lower(): attr["vals"][0] for attr in computer["attributes"]}
4646
if "mslaps-encryptedpassword" in values:
4747
msMCSAdmPwd = values["mslaps-encryptedpassword"]
48-
d = LAPSv2Extract(bytes(msMCSAdmPwd), connection.username if connection.username else "", connection.password if connection.password else "", connection.domain, connection.nthash if connection.nthash else "", connection.kerberos, connection.kdcHost, 339)
48+
d = LAPSv2Extract(bytes(msMCSAdmPwd), connection.username if connection.username else "", connection.password if connection.password else "", connection.domain, connection.nthash if connection.nthash else "", connection.kerberos, connection.kdcHost, 339, connection.dns_server)
4949
try:
5050
data = d.run()
5151
except Exception as e:

nxc/protocols/ldap/laps.py

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def __init__(self, host, port, hostname):
3737
def proto_logger(self, host, port, hostname):
3838
self.logger = NXCAdapter(extra={"protocol": "LDAP", "host": host, "port": port, "hostname": hostname})
3939

40-
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
40+
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False, dns_server=""):
4141
lmhash = ""
4242
nthash = ""
4343

@@ -59,7 +59,7 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="",
5959
baseDN = baseDN[:-1]
6060

6161
try:
62-
ldap_connection = ldap_impacket.LDAPConnection(f"ldap://{kdcHost}", baseDN)
62+
ldap_connection = ldap_impacket.LDAPConnection(f"ldap://{kdcHost}", baseDN, domain if not dns_server else dns_server)
6363
ldap_connection.kerberosLogin(
6464
username,
6565
password,
@@ -78,7 +78,7 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="",
7878
if str(e).find("strongerAuthRequired") >= 0:
7979
# We need to try SSL
8080
try:
81-
ldap_connection = ldap_impacket.LDAPConnection(f"ldaps://{kdcHost}", baseDN)
81+
ldap_connection = ldap_impacket.LDAPConnection(f"ldaps://{kdcHost}", baseDN, domain if not dns_server else dns_server)
8282
ldap_connection.login(
8383
username,
8484
password,
@@ -116,7 +116,7 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="",
116116
)
117117
return False
118118

119-
def auth_login(self, domain, username, password, ntlm_hash):
119+
def auth_login(self, domain, username, password, ntlm_hash, dns_server):
120120
lmhash = ""
121121
nthash = ""
122122

@@ -135,7 +135,7 @@ def auth_login(self, domain, username, password, ntlm_hash):
135135
base_dn = base_dn[:-1]
136136

137137
try:
138-
ldap_connection = ldap_impacket.LDAPConnection(f"ldap://{domain}", base_dn, domain)
138+
ldap_connection = ldap_impacket.LDAPConnection(f"ldap://{domain}", base_dn, domain if not dns_server else dns_server)
139139
ldap_connection.login(username, password, domain, lmhash, nthash)
140140

141141
# Connect to LDAP
@@ -148,7 +148,7 @@ def auth_login(self, domain, username, password, ntlm_hash):
148148
if str(e).find("strongerAuthRequired") >= 0:
149149
# We need to try SSL
150150
try:
151-
ldap_connection = ldap_impacket.LDAPConnection(f"ldaps://{domain}", base_dn, domain)
151+
ldap_connection = ldap_impacket.LDAPConnection(f"ldaps://{domain}", base_dn, domain if not dns_server else dns_server)
152152
ldap_connection.login(username, password, domain, lmhash, nthash)
153153
self.logger.extra["protocol"] = "LDAPS"
154154
self.logger.extra["port"] = "636"
@@ -173,7 +173,7 @@ def auth_login(self, domain, username, password, ntlm_hash):
173173

174174

175175
class LAPSv2Extract:
176-
def __init__(self, data, username, password, domain, ntlm_hash, do_kerberos, kdcHost, port):
176+
def __init__(self, data, username, password, domain, ntlm_hash, do_kerberos, kdcHost, port, dns_server):
177177
if ntlm_hash.find(":") != -1:
178178
self.lmhash, self.nthash = ntlm_hash.split(":")
179179
else:
@@ -187,6 +187,7 @@ def __init__(self, data, username, password, domain, ntlm_hash, do_kerberos, kdc
187187
self.do_kerberos = do_kerberos
188188
self.kdcHost = kdcHost
189189
self.logger = None
190+
self.dns_server = dns_server
190191
self.proto_logger(self.domain, port, self.domain)
191192

192193
def proto_logger(self, host, port, hostname):
@@ -218,7 +219,7 @@ def run(self):
218219
gke = kds_cache[key_id["RootKeyId"]]
219220
else:
220221
# Connect on RPC over TCP to MS-GKDI to call opnum 0 GetKey
221-
string_binding = hept_map(destHost=self.domain, remoteIf=MSRPC_UUID_GKDI, protocol="ncacn_ip_tcp")
222+
string_binding = hept_map(destHost=self.domain if not self.dns_server else self.dns_server, remoteIf=MSRPC_UUID_GKDI, protocol="ncacn_ip_tcp")
222223
rpc_transport = transport.DCERPCTransportFactory(string_binding)
223224
if hasattr(rpc_transport, "set_credentials"):
224225
rpc_transport.set_credentials(username=self.username, password=self.password, domain=self.domain, lmhash=self.lmhash, nthash=self.nthash)
@@ -263,7 +264,7 @@ def run(self):
263264
return plaintext[:-18].decode("utf-16le")
264265

265266

266-
def laps_search(self, username, password, cred_type, domain):
267+
def laps_search(self, username, password, cred_type, domain, dns_server):
267268
prev_protocol = self.logger.extra["protocol"]
268269
prev_port = self.logger.extra["port"]
269270
self.logger.extra["protocol"] = "LDAP"
@@ -283,18 +284,20 @@ def laps_search(self, username, password, cred_type, domain):
283284
password[0] if cred_type[0] == "hash" else "",
284285
kdcHost=self.kdcHost,
285286
aesKey=self.aesKey,
287+
dns_server=dns_server
286288
)
287289
else:
288290
connection = ldapco.auth_login(
289291
domain[0],
290292
username[0] if username else "",
291293
password[0] if cred_type[0] == "plaintext" else "",
292294
password[0] if cred_type[0] == "hash" else "",
295+
dns_server
293296
)
294297
if not connection:
295298
self.logger.fail(f"LDAP connection failed with account {username[0]}")
296299

297-
return None, None, None, None
300+
return None, None, None
298301

299302
search_filter = "(&(objectCategory=computer)(|(msLAPS-EncryptedPassword=*)(ms-MCS-AdmPwd=*)(msLAPS-Password=*))(name=" + self.hostname + "))"
300303
attributes = [
@@ -325,13 +328,14 @@ def laps_search(self, username, password, cred_type, domain):
325328
password[0] if cred_type[0] == "hash" else "",
326329
self.kerberos,
327330
self.kdcHost,
328-
339
331+
339,
332+
dns_server
329333
)
330334
try:
331335
data = d.run()
332336
except Exception as e:
333337
self.logger.fail(str(e))
334-
return None, None, None, None
338+
return None, None, None
335339
r = loads(data)
336340
msMCSAdmPwd = r["p"]
337341
username_laps = r["n"]
@@ -346,21 +350,16 @@ def laps_search(self, username, password, cred_type, domain):
346350
self.logger.debug(f"Host: {sAMAccountName:<20} Password: {msMCSAdmPwd} {self.hostname}")
347351
else:
348352
self.logger.fail(f"msMCSAdmPwd or msLAPS-Password is empty or account cannot read LAPS property for {self.hostname}")
349-
return None, None, None, None
353+
return None, None, None
350354

351355
if msMCSAdmPwd == "":
352356
self.logger.fail(f"msMCSAdmPwd or msLAPS-Password is empty or account cannot read LAPS property for {self.hostname}")
353-
return None, None, None, None
354-
355-
hash_ntlm = None
356-
if cred_type[0] == "hash":
357-
hash_ntlm = hashlib.new("md4", msMCSAdmPwd.encode("utf-16le")).digest()
358-
hash_ntlm = binascii.hexlify(hash_ntlm).decode()
357+
return None, None, None
359358

360359
username = username_laps if username_laps else self.args.laps
361360
password = msMCSAdmPwd
362361
domain = self.hostname
363362
self.args.local_auth = True
364363
self.logger.extra["protocol"] = prev_protocol
365364
self.logger.extra["port"] = prev_port
366-
return username, password, domain, hash_ntlm
365+
return username, password, domain

0 commit comments

Comments
 (0)