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
15 changes: 15 additions & 0 deletions nxc/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,18 @@
def process_secret(text):
reveal = text[:reveal_chars_of_pwd]
return text if not audit_mode else reveal + (audit_mode if len(audit_mode) > 1 else audit_mode * 8)


def process_secret_dump(line, sep=":", keep=1):
"""
Redacts the credential material of a dumped secret line (SAM/LSA/NTDS) when audit_mode is
enabled, keeping only the first `keep` fields (username, RID, secret name, etc.) before
masking the rest of the line.
"""
if not audit_mode:
return line
parts = line.split(sep)
if len(parts) <= keep:
return process_secret(line)
mask = audit_mode if len(audit_mode) > 1 else audit_mode * 8
return sep.join([*parts[:keep], mask])
24 changes: 12 additions & 12 deletions nxc/protocols/ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
from impacket.smbconnection import SessionError
from impacket.ntlm import getNTLMSSPType1

from nxc.config import process_secret, host_info_colors
from nxc.config import process_secret, process_secret_dump, host_info_colors
from nxc.connection import connection
from nxc.helpers.bloodhound import add_user_bh
from nxc.helpers.misc import get_bloodhound_info, convert, d2b, parse_argument
Expand Down Expand Up @@ -321,7 +321,7 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="",
if self.username and self.password == "" and self.args.asreproast:
hash_tgt = KerberosAttacks(self).get_tgt_asroast(self.username)
if hash_tgt:
self.logger.highlight(f"{hash_tgt}")
self.logger.highlight(process_secret_dump(hash_tgt))
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(f"{hash_tgt}\n")
return False
Expand Down Expand Up @@ -446,7 +446,7 @@ def plaintext_login(self, domain, username, password):
if self.username and self.password == "" and self.args.asreproast:
hash_tgt = KerberosAttacks(self).get_tgt_asroast(self.username)
if hash_tgt:
self.logger.highlight(f"{hash_tgt}")
self.logger.highlight(process_secret_dump(hash_tgt))
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(f"{hash_tgt}\n")
return False
Expand Down Expand Up @@ -542,7 +542,7 @@ def hash_login(self, domain, username, ntlm_hash):
if self.username and self.hash == "" and self.args.asreproast:
hash_tgt = KerberosAttacks(self).get_tgt_asroast(self.username)
if hash_tgt:
self.logger.highlight(f"{hash_tgt}")
self.logger.highlight(process_secret_dump(hash_tgt))
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(f"{hash_tgt}\n")
return False
Expand Down Expand Up @@ -969,7 +969,7 @@ def asreproast(self):
for user in resp_parsed:
hash_TGT = KerberosAttacks(self).get_tgt_asroast(user["sAMAccountName"])
if hash_TGT:
self.logger.highlight(f"{hash_TGT}")
self.logger.highlight(process_secret_dump(hash_TGT))
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(f"{hash_TGT}\n")

Expand Down Expand Up @@ -1071,7 +1071,7 @@ def kerberoasting(self):
pwdLastSet = "<never>" if str(user.get("pwdLastSet", 0)) == "0" else str(datetime.fromtimestamp(self.getUnixTime(int(user["pwdLastSet"]))))
lastLogon = "<never>" if str(user.get("lastLogon", 0)) == "0" else str(datetime.fromtimestamp(self.getUnixTime(int(user["lastLogon"]))))
self.logger.display(f"sAMAccountName: {user['sAMAccountName']}, memberOf: {user.get('memberOf', [])}, pwdLastSet: {pwdLastSet}, lastLogon: {lastLogon}")
self.logger.highlight(out)
self.logger.highlight(process_secret_dump(out, sep="*", keep=2))
if self.args.kerberoasting:
with open(self.args.kerberoasting, "a+") as hash_kerberoasting:
hash_kerberoasting.write(out + "\n")
Expand Down Expand Up @@ -1339,10 +1339,10 @@ def gmsa(self):
aes128 = aes256 = ""
if "msDS-ManagedPassword" in acc:
rc4, aes128, aes256 = self.gmsa_compute_secrets(acc["msDS-ManagedPassword"], acc["sAMAccountName"])
self.logger.highlight(f"Account: {acc['sAMAccountName']:<20} NTLM: {rc4:<36} PrincipalsAllowedToReadPassword: {principal_with_read}")
self.logger.highlight(f"Account: {acc['sAMAccountName']:<20} NTLM: {process_secret(rc4):<36} PrincipalsAllowedToReadPassword: {principal_with_read}")
if aes128 and aes256:
self.logger.highlight(f"Account: {acc['sAMAccountName']:<20} aes128-cts-hmac-sha1-96: {aes128}")
self.logger.highlight(f"Account: {acc['sAMAccountName']:<20} aes256-cts-hmac-sha1-96: {aes256}")
self.logger.highlight(f"Account: {acc['sAMAccountName']:<20} aes128-cts-hmac-sha1-96: {process_secret(aes128)}")
self.logger.highlight(f"Account: {acc['sAMAccountName']:<20} aes256-cts-hmac-sha1-96: {process_secret(aes256)}")

def gmsa_compute_secrets(self, password_data: bytes, sAMAccountName: str):
"""Generate RC4, AES128, and AES256 keys for a GMSA account based on the provided password data and username."""
Expand Down Expand Up @@ -1407,12 +1407,12 @@ def gmsa_decrypt_lsa(self):
# Compute the password and keys
data = bytes.fromhex(gmsa_pass)
rc4, aes128, aes256 = self.gmsa_compute_secrets(data, sAMAccountName)
self.logger.highlight(f"Account: {sAMAccountName:<20} NTLM: {rc4}")
self.logger.highlight(f"Account: {sAMAccountName:<20} NTLM: {process_secret(rc4)}")
if not sAMAccountName:
self.logger.fail("Could not find the GMSA account associated with the provided ID.")
else:
self.logger.highlight(f"Account: {sAMAccountName:<20} aes128-cts-hmac-sha1-96: {aes128}")
self.logger.highlight(f"Account: {sAMAccountName:<20} aes256-cts-hmac-sha1-96: {aes256}")
self.logger.highlight(f"Account: {sAMAccountName:<20} aes128-cts-hmac-sha1-96: {process_secret(aes128)}")
self.logger.highlight(f"Account: {sAMAccountName:<20} aes256-cts-hmac-sha1-96: {process_secret(aes256)}")
else:
self.logger.fail("The provided string does not appear to be a valid GMSA LSA secret.")

Expand Down
32 changes: 16 additions & 16 deletions nxc/protocols/smb.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@

from impacket.dcerpc.v5 import tsts as TSTS

from nxc.config import process_secret, host_info_colors, check_guest_account
from nxc.config import process_secret, process_secret_dump, host_info_colors, check_guest_account
from nxc.connection import connection, sem, requires_admin, dcom_FirewallChecker
from nxc.helpers.misc import gen_random_string, validate_ntlm
from nxc.logger import NXCAdapter
Expand Down Expand Up @@ -2102,7 +2102,7 @@ def sam(self):
host_id = self.db.get_hosts(filter_term=self.host)[0][0]

def add_sam_hash(sam_hash, host_id):
self.logger.highlight(sam_hash)
self.logger.highlight(process_secret_dump(sam_hash))
if "_history" in sam_hash:
return
username, _, lmhash, nthash, _, _, _ = sam_hash.split(":")
Expand Down Expand Up @@ -2185,7 +2185,7 @@ def sccm(self):
def sccm_callback(secret):
if isinstance(secret, SCCMCred):
tag = "NAA Account"
self.logger.highlight(f"[{tag}] {secret.username.decode('latin-1')}:{secret.password.decode('latin-1')}")
self.logger.highlight(f"[{tag}] {secret.username.decode('latin-1')}:{process_secret(secret.password.decode('latin-1'))}")
self.db.add_dpapi_secrets(
target.address,
f"SCCM - {tag}",
Expand All @@ -2196,7 +2196,7 @@ def sccm_callback(secret):
)
elif isinstance(secret, SCCMSecret):
tag = "Task sequences secret"
self.logger.highlight(f"[{tag}] {secret.secret.decode('latin-1')}")
self.logger.highlight(f"[{tag}] {process_secret(secret.secret.decode('latin-1'))}")
self.db.add_dpapi_secrets(
target.address,
f"SCCM - {tag}",
Expand All @@ -2207,7 +2207,7 @@ def sccm_callback(secret):
)
elif isinstance(secret, SCCMCollection):
tag = "Collection Variable"
self.logger.highlight(f"[{tag}] {secret.variable.decode('latin-1')}:{secret.value.decode('latin-1')}")
self.logger.highlight(f"[{tag}] {secret.variable.decode('latin-1')}:{process_secret(secret.value.decode('latin-1'))}")
self.db.add_dpapi_secrets(
target.address,
f"SCCM - {tag}",
Expand Down Expand Up @@ -2267,7 +2267,7 @@ def dpapi(self):
# Collect User and Machine Credentials Manager secrets
def credential_callback(credential):
tag = "CREDENTIAL"
line = f"[{credential.winuser}][{tag}] {credential.target} - {credential.username}:{credential.password}"
line = f"[{credential.winuser}][{tag}] {credential.target} - {credential.username}:{process_secret(credential.password)}"
self.logger.highlight(line)
if self.output_file:
self.output_file.write(line + "\n")
Expand Down Expand Up @@ -2305,7 +2305,7 @@ def credential_callback(credential):
def browser_callback(secret):
if isinstance(secret, LoginData):
secret_url = secret.url + " -" if secret.url != "" else "-"
line = f"[{secret.winuser}][{secret.browser.upper()}] {secret_url} {secret.username}:{secret.password}"
line = f"[{secret.winuser}][{secret.browser.upper()}] {secret_url} {secret.username}:{process_secret(secret.password)}"
self.logger.highlight(line)
if self.output_file:
self.output_file.write(line + "\n")
Expand All @@ -2318,7 +2318,7 @@ def browser_callback(secret):
secret.url,
)
elif isinstance(secret, GoogleRefreshToken):
line = f"[{secret.winuser}][{secret.browser.upper()}] Google Refresh Token: {secret.service}:{secret.token}"
line = f"[{secret.winuser}][{secret.browser.upper()}] Google Refresh Token: {secret.service}:{process_secret(secret.token)}"
self.logger.highlight(line)
if self.output_file:
self.output_file.write(line + "\n")
Expand All @@ -2331,7 +2331,7 @@ def browser_callback(secret):
"Google Refresh Token",
)
elif isinstance(secret, Cookie):
line = f"[{secret.winuser}][{secret.browser.upper()}] {secret.host}{secret.path} - {secret.cookie_name}:{secret.cookie_value}"
line = f"[{secret.winuser}][{secret.browser.upper()}] {secret.host}{secret.path} - {secret.cookie_name}:{process_secret(secret.cookie_value)}"
self.logger.highlight(line)
if self.output_file:
self.output_file.write(line + "\n")
Expand All @@ -2346,7 +2346,7 @@ def vault_callback(secret):
tag = "IEX"
if secret.type == "Internet Explorer":
resource = secret.resource + " -" if secret.resource != "" else "-"
line = f"[{secret.winuser}][{tag}] {resource} - {secret.username}:{secret.password}"
line = f"[{secret.winuser}][{tag}] {resource} - {secret.username}:{process_secret(secret.password)}"
self.logger.highlight(line)
if self.output_file:
self.output_file.write(line + "\n")
Expand All @@ -2370,7 +2370,7 @@ def firefox_callback(secret):
tag = "FIREFOX"
if isinstance(secret, FirefoxData):
url = secret.url + " -" if secret.url != "" else "-"
line = f"[{secret.winuser}][{tag}] {url} {secret.username}:{secret.password}"
line = f"[{secret.winuser}][{tag}] {url} {secret.username}:{process_secret(secret.password)}"
self.logger.highlight(line)
if self.output_file:
self.output_file.write(line + "\n")
Expand All @@ -2383,7 +2383,7 @@ def firefox_callback(secret):
secret.url,
)
elif isinstance(secret, FirefoxCookie):
line = f"[{secret.winuser}][{tag}] {secret.host}{secret.path} {secret.cookie_name}:{secret.cookie_value}"
line = f"[{secret.winuser}][{tag}] {secret.host}{secret.path} {secret.cookie_name}:{process_secret(secret.cookie_value)}"
self.logger.highlight(line)
if self.output_file:
self.output_file.write(line + "\n")
Expand Down Expand Up @@ -2422,7 +2422,7 @@ def lsa(self):

def add_lsa_secret(secret):
add_lsa_secret.secrets += 1
self.logger.highlight(secret)
self.logger.highlight(process_secret_dump(secret))
if "_SC_GMSA_{84A78B8C" in secret:
gmsa_id = secret.split("_")[4].split(":")[0]
data = bytes.fromhex(secret.split("_")[4].split(":")[1])
Expand All @@ -2432,7 +2432,7 @@ def add_lsa_secret(secret):
ntlm_hash = MD4.new()
ntlm_hash.update(currentPassword)
passwd = binascii.hexlify(ntlm_hash.digest()).decode("utf-8")
self.logger.highlight(f"GMSA ID: {gmsa_id:<20} NTLM: {passwd}")
self.logger.highlight(process_secret_dump(f"GMSA ID: {gmsa_id:<20} NTLM: {passwd}", sep="NTLM: "))

add_lsa_secret.secrets = 0

Expand Down Expand Up @@ -2494,10 +2494,10 @@ def add_hash(secret_type, secret, host_id):
if self.args.enabled:
if "Enabled" in secret:
secret = " ".join(secret.split(" ")[:-1])
self.logger.highlight(secret)
self.logger.highlight(process_secret_dump(secret, keep=2))
else:
secret = " ".join(secret.split(" ")[:-1]) if " " in secret else secret
self.logger.highlight(secret)
self.logger.highlight(process_secret_dump(secret, keep=2))

# Filter out computer accounts, history hashes and kerberos keys for adding to db
if secret.find("$") == -1 and secret_type == NTDSHashes.SECRET_TYPE.NTDS and "_history" not in secret:
Expand Down
Loading