Skip to content

Commit 5ae4134

Browse files
authored
Merge branch 'main' into dumpSecurityQuestionsModule
2 parents 2575bce + 3a5c109 commit 5ae4134

11 files changed

Lines changed: 124 additions & 88 deletions

File tree

nxc/connection.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ def get_host_addr_info(target, force_ipv6, dns_server, dns_tcp, dns_timeout):
8585
def requires_admin(func):
8686
def _decorator(self, *args, **kwargs):
8787
if self.admin_privs is False:
88+
if hasattr(self.args, "exec_method") and self.args.exec_method == "mmcexec":
89+
return func(self, *args, **kwargs)
8890
return None
8991
return func(self, *args, **kwargs)
9092

@@ -146,6 +148,7 @@ def __init__(self, args, db, target):
146148
self.kdcHost = self.args.kdcHost
147149
self.port = self.args.port
148150
self.local_ip = None
151+
self.dns_server = self.args.dns_server
149152

150153
# DNS resolution
151154
dns_result = self.resolver(target)
@@ -541,7 +544,7 @@ def login(self):
541544

542545
if hasattr(self.args, "laps") and self.args.laps:
543546
self.logger.debug("Trying to authenticate using LAPS")
544-
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)
545548
cred_type = ["plaintext"]
546549
if not (username[0] or secret[0] or domain[0]):
547550
return False

nxc/modules/add-computer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,9 @@ def do_samr_add(self, context):
102102
None
103103
"""
104104
string_binding = epm.hept_map(self.__host, samr.MSRPC_UUID_SAMR, protocol="ncacn_np")
105+
string_binding = string_binding.replace(self.__host, self.__kdcHost) if self.__kdcHost is not None else string_binding
105106

106-
rpc_transport = transport.DCERPCTransportFactory(string_binding.replace(self.__host, self.__kdcHost))
107+
rpc_transport = transport.DCERPCTransportFactory(string_binding)
107108
rpc_transport.setRemoteHost(self.__host)
108109

109110
if hasattr(rpc_transport, "set_credentials"):

nxc/modules/enum_av.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,27 @@ def LsarLookupNames(self, dce, policyHandle, service):
358358
{"name": "sophoslivequery_*", "processes": [""]}
359359
]
360360
},
361+
{
362+
"name": "Trellix Endpoint Detection and Response (EDR)",
363+
"services": [
364+
{"name": "McAfee Endpoint Security Platform Service", "description": "Trellix Core Service"},
365+
{"name": "mfemactl", "description": "Trellix Management Service"},
366+
{"name": "mfemms", "description": "McAfee Management Service"},
367+
{"name": "mfefire", "description": "Trellix Firewall Core Service"},
368+
{"name": "masvc", "description": "Trellix Agent Service"},
369+
{"name": "macmnsvc", "description": "Trellix Agent Common Service"},
370+
{"name": "mfetp", "description": "Trellix Endpoint Threat Prevention Service"},
371+
{"name": "mfewc", "description": "Trellix Endpoint Security Web Control Service"},
372+
{"name": "mfeaack", "description": "Trellix Anti-Malware Core Service"}
373+
],
374+
"pipes": [
375+
{"name": "TrellixEDR_Pipe_*", "processes": ["McAfeeEDR.exe"]},
376+
{"name": "mfemactl_*", "processes": ["mfemactl.exe"]},
377+
{"name": "mfefire_*", "processes": ["mfefire.exe"]},
378+
{"name": "McAfeeAgent_Pipe_*", "processes": ["McAfeeAgent.exe"]},
379+
{"name": "mfetp_*", "processes": ["mfetp.exe"]}
380+
]
381+
},
361382
{
362383
"name": "Trend Micro Endpoint Security",
363384
"services": [

nxc/modules/laps.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,12 @@ 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:
52-
self.logger.fail(str(e))
53-
return
52+
context.log.fail(str(e))
53+
continue
5454
r = json.loads(data)
5555
laps_computers.append((str(values["samaccountname"]), r["n"], str(r["p"])))
5656
elif "mslaps-password" in values:

nxc/protocols/ldap.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,12 @@ def get_ldap_info(self, host):
172172
self.logger.debug(f"ldap_connection: {ldap_connection}")
173173
except SysCallError as e:
174174
if proto == "ldaps":
175-
self.logger.debug(f"LDAPs connection to {ldap_url} failed - {e}")
175+
self.logger.fail(f"LDAPs connection to {ldap_url} failed - {e}")
176176
# https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/enable-ldap-over-ssl-3rd-certification-authority
177-
self.logger.debug("Even if the port is open, LDAPS may not be configured")
177+
self.logger.fail("Even if the port is open, LDAPS may not be configured")
178178
else:
179-
self.logger.debug(f"LDAP connection to {ldap_url} failed: {e}")
180-
return [None, None, None]
179+
self.logger.fail(f"LDAP connection to {ldap_url} failed: {e}")
180+
exit(1)
181181

182182
resp = ldap_connection.search(
183183
scope=ldapasn1_impacket.Scope("baseObject"),
@@ -1419,6 +1419,7 @@ def bloodhound(self):
14191419
auth.get_tgt()
14201420
if self.args.use_kcache:
14211421
self.logger.highlight("Using kerberos auth from ccache")
1422+
auth.load_ccache()
14221423

14231424
timestamp = datetime.now().strftime("%Y-%m-%d_%H%M%S") + "_"
14241425
bloodhound = BloodHound(ad, self.hostname, self.host, self.port)

nxc/protocols/ldap/laps.py

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import binascii
2-
import hashlib
31
from json import loads
42
from pyasn1.codec.der import decoder
53
from pyasn1_modules import rfc5652
@@ -37,12 +35,13 @@ def __init__(self, host, port, hostname):
3735
def proto_logger(self, host, port, hostname):
3836
self.logger = NXCAdapter(extra={"protocol": "LDAP", "host": host, "port": port, "hostname": hostname})
3937

40-
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
38+
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False, dns_server=""):
4139
lmhash = ""
4240
nthash = ""
4341

44-
if kdcHost is None:
45-
kdcHost = domain
42+
if kdcHost is None or domain not in kdcHost:
43+
self.logger.fail("Please provide the FQDN of the domain controller with --kdcHost")
44+
exit(1)
4645

4746
# This checks to see if we didn't provide the LM Hash
4847
if ntlm_hash and ntlm_hash.find(":") != -1:
@@ -54,12 +53,13 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="",
5453
baseDN = ""
5554
domainParts = domain.split(".")
5655
for i in domainParts:
57-
baseDN += f"dc={i},"
56+
baseDN += f"DC={i},"
5857
# Remove last ','
5958
baseDN = baseDN[:-1]
6059

6160
try:
62-
ldap_connection = ldap_impacket.LDAPConnection(f"ldap://{kdcHost}", baseDN)
61+
self.logger.info(f"Connecting to ldap://{kdcHost} - {baseDN} - {domain} [1]")
62+
ldap_connection = ldap_impacket.LDAPConnection(f"ldap://{kdcHost}", baseDN, dns_server if dns_server else domain)
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, dns_server if dns_server else domain)
8282
ldap_connection.login(
8383
username,
8484
password,
@@ -105,18 +105,14 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="",
105105
color="magenta" if error_code in ldap_error_status else "red",
106106
)
107107
return False
108-
109108
except OSError:
110109
self.logger.debug(f"{domain}\\{username}:{password if password else ntlm_hash} {'Error connecting to the domain, please add option --kdcHost with the FQDN of the domain controller'}")
111110
return False
112111
except KerberosError as e:
113-
self.logger.fail(
114-
f"{domain}\\{username}:{password if password else ntlm_hash} {e!s}",
115-
color="red",
116-
)
112+
self.logger.fail(f"{domain}\\{username}:{password if password else ntlm_hash} {e!s}", color="red")
117113
return False
118114

119-
def auth_login(self, domain, username, password, ntlm_hash):
115+
def auth_login(self, domain, username, password, ntlm_hash, dns_server):
120116
lmhash = ""
121117
nthash = ""
122118

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

137133
try:
138-
ldap_connection = ldap_impacket.LDAPConnection(f"ldap://{domain}", base_dn, domain)
134+
ldap_connection = ldap_impacket.LDAPConnection(f"ldap://{domain}", base_dn, dns_server if dns_server else domain)
139135
ldap_connection.login(username, password, domain, lmhash, nthash)
140136

141137
# Connect to LDAP
@@ -148,7 +144,7 @@ def auth_login(self, domain, username, password, ntlm_hash):
148144
if str(e).find("strongerAuthRequired") >= 0:
149145
# We need to try SSL
150146
try:
151-
ldap_connection = ldap_impacket.LDAPConnection(f"ldaps://{domain}", base_dn, domain)
147+
ldap_connection = ldap_impacket.LDAPConnection(f"ldaps://{domain}", base_dn, dns_server if dns_server else domain)
152148
ldap_connection.login(username, password, domain, lmhash, nthash)
153149
self.logger.extra["protocol"] = "LDAPS"
154150
self.logger.extra["port"] = "636"
@@ -173,7 +169,7 @@ def auth_login(self, domain, username, password, ntlm_hash):
173169

174170

175171
class LAPSv2Extract:
176-
def __init__(self, data, username, password, domain, ntlm_hash, do_kerberos, kdcHost, port):
172+
def __init__(self, data, username, password, domain, ntlm_hash, do_kerberos, kdcHost, port, dns_server):
177173
if ntlm_hash.find(":") != -1:
178174
self.lmhash, self.nthash = ntlm_hash.split(":")
179175
else:
@@ -187,6 +183,7 @@ def __init__(self, data, username, password, domain, ntlm_hash, do_kerberos, kdc
187183
self.do_kerberos = do_kerberos
188184
self.kdcHost = kdcHost
189185
self.logger = None
186+
self.dns_server = dns_server
190187
self.proto_logger(self.domain, port, self.domain)
191188

192189
def proto_logger(self, host, port, hostname):
@@ -218,7 +215,7 @@ def run(self):
218215
gke = kds_cache[key_id["RootKeyId"]]
219216
else:
220217
# 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")
218+
string_binding = hept_map(destHost=self.domain if not self.dns_server else self.dns_server, remoteIf=MSRPC_UUID_GKDI, protocol="ncacn_ip_tcp")
222219
rpc_transport = transport.DCERPCTransportFactory(string_binding)
223220
if hasattr(rpc_transport, "set_credentials"):
224221
rpc_transport.set_credentials(username=self.username, password=self.password, domain=self.domain, lmhash=self.lmhash, nthash=self.nthash)
@@ -263,7 +260,7 @@ def run(self):
263260
return plaintext[:-18].decode("utf-16le")
264261

265262

266-
def laps_search(self, username, password, cred_type, domain):
263+
def laps_search(self, username, password, cred_type, domain, dns_server):
267264
prev_protocol = self.logger.extra["protocol"]
268265
prev_port = self.logger.extra["port"]
269266
self.logger.extra["protocol"] = "LDAP"
@@ -274,7 +271,7 @@ def laps_search(self, username, password, cred_type, domain):
274271
if self.kerberos:
275272
if self.kdcHost is None:
276273
self.logger.fail("Add --kdcHost parameter to use laps with kerberos")
277-
return None, None, None, None
274+
return None, None, None
278275

279276
connection = ldapco.kerberos_login(
280277
domain[0],
@@ -283,18 +280,20 @@ def laps_search(self, username, password, cred_type, domain):
283280
password[0] if cred_type[0] == "hash" else "",
284281
kdcHost=self.kdcHost,
285282
aesKey=self.aesKey,
283+
dns_server=dns_server
286284
)
287285
else:
288286
connection = ldapco.auth_login(
289287
domain[0],
290288
username[0] if username else "",
291289
password[0] if cred_type[0] == "plaintext" else "",
292290
password[0] if cred_type[0] == "hash" else "",
291+
dns_server
293292
)
294293
if not connection:
295294
self.logger.fail(f"LDAP connection failed with account {username[0]}")
296295

297-
return None, None, None, None
296+
return None, None, None
298297

299298
search_filter = "(&(objectCategory=computer)(|(msLAPS-EncryptedPassword=*)(ms-MCS-AdmPwd=*)(msLAPS-Password=*))(name=" + self.hostname + "))"
300299
attributes = [
@@ -325,13 +324,14 @@ def laps_search(self, username, password, cred_type, domain):
325324
password[0] if cred_type[0] == "hash" else "",
326325
self.kerberos,
327326
self.kdcHost,
328-
339
327+
339,
328+
dns_server
329329
)
330330
try:
331331
data = d.run()
332332
except Exception as e:
333333
self.logger.fail(str(e))
334-
return None, None, None, None
334+
return None, None, None
335335
r = loads(data)
336336
msMCSAdmPwd = r["p"]
337337
username_laps = r["n"]
@@ -346,21 +346,17 @@ def laps_search(self, username, password, cred_type, domain):
346346
self.logger.debug(f"Host: {sAMAccountName:<20} Password: {msMCSAdmPwd} {self.hostname}")
347347
else:
348348
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
349+
return None, None, None
350350

351351
if msMCSAdmPwd == "":
352352
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()
353+
return None, None, None
359354

360355
username = username_laps if username_laps else self.args.laps
361356
password = msMCSAdmPwd
362357
domain = self.hostname
363358
self.args.local_auth = True
359+
self.args.kerberos = False
364360
self.logger.extra["protocol"] = prev_protocol
365361
self.logger.extra["port"] = prev_port
366-
return username, password, domain, hash_ntlm
362+
return username, password, domain

nxc/protocols/ldap/proto_args.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@ def proto_args(parser, parents):
3333

3434
bgroup = ldap_parser.add_argument_group("Bloodhound Scan", "Options to play with Bloodhoud")
3535
bgroup.add_argument("--bloodhound", action="store_true", help="Perform a Bloodhound scan")
36-
bgroup.add_argument("-c", "--collection", default="Collection", help="Which information to collect. Supported: Group, LocalAdmin, Session, Trusts, Default, DCOnly, DCOM, RDP, PSRemote, LoggedOn, Container, ObjectProps, ACL, All. You can specify more than one by separating them with a comma")
36+
bgroup.add_argument("-c", "--collection", default="Default", help="Which information to collect. Supported: Group, LocalAdmin, Session, Trusts, Default, DCOnly, DCOM, RDP, PSRemote, LoggedOn, Container, ObjectProps, ACL, All. You can specify more than one by separating them with a comma")
3737

3838
return parser

0 commit comments

Comments
 (0)