Skip to content

Commit 6bc8acc

Browse files
committed
fix: add share export functionality to nxcdb for Kerberos auth and resolve SMB permissions display
1 parent 49b5a60 commit 6bc8acc

3 files changed

Lines changed: 51 additions & 19 deletions

File tree

nxc/database.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,21 +120,17 @@ def format_host_query(q, filter_term, HostsTable):
120120
# TODO: normalize these column names
121121
if hasattr(HostsTable.c, "ip"):
122122
ip_column = HostsTable.c.ip
123-
nxc_logger.debug("Using 'ip' column for filtering")
124123
elif hasattr(HostsTable.c, "host"):
125124
ip_column = HostsTable.c.host
126-
nxc_logger.debug("Using 'host' column for filtering")
127125
else:
128126
nxc_logger.debug("Neither 'ip' nor 'host' columns found in the table")
129127
return q
130128

131129
# first we check if its an ip address
132130
try:
133131
ipaddress.ip_address(filter_term)
134-
nxc_logger.debug(f"filter_term is an IP address: {filter_term}")
135132
q = q.filter(ip_column == filter_term)
136133
except ValueError:
137-
nxc_logger.debug(f"filter_term is not an IP address: {filter_term}")
138134
like_term = func.lower(f"%{filter_term}%")
139135

140136
# check if the hostname column exists for hostname searching

nxc/protocols/smb.py

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
from impacket.dcerpc.v5 import tsts as TSTS
4040

4141
from nxc.config import process_secret, host_info_colors, stealth_label
42-
4342
from nxc.connection import connection, sem, requires_admin, dcom_FirewallChecker
4443
from nxc.helpers.misc import gen_random_string, validate_ntlm
4544
from nxc.logger import NXCAdapter
@@ -270,7 +269,7 @@ def enum_host_info(self):
270269
self.logger.debug(f"Error logging off system: {e}")
271270

272271
# Check smbv1
273-
if hasattr(self.args, "smbv1") and self.args.smbv1:
272+
if self.args.smbv1:
274273
self.smbv1 = self.create_smbv1_conn(check=True)
275274

276275
try:
@@ -299,7 +298,8 @@ def print_host_info(self):
299298
smbv1 = colored(f"SMBv1:{self.smbv1}", host_info_colors[2], attrs=["bold"]) if self.smbv1 else colored(f"SMBv1:{self.smbv1}", host_info_colors[3], attrs=["bold"])
300299
ntlm = colored(f" (NTLM:{not self.no_ntlm})", host_info_colors[2], attrs=["bold"]) if self.no_ntlm else ""
301300
null_auth = colored(f" (Null Auth:{self.null_auth})", host_info_colors[2], attrs=["bold"]) if self.null_auth else ""
302-
self.logger.display(f"{self.server_os}{f' x{self.os_arch}' if self.os_arch else ''} (name:{self.hostname}) (domain:{self.targetDomain}) ({signing}) ({smbv1}){ntlm}{null_auth}")
301+
guest = colored(f" (Guest Auth:{self.is_guest})", host_info_colors[1], attrs=["bold"]) if self.is_guest else ""
302+
self.logger.display(f"{self.server_os}{f' x{self.os_arch}' if self.os_arch else ''} (name:{self.hostname}) (domain:{self.targetDomain}) ({signing}) ({smbv1}){ntlm}{null_auth}{guest}")
303303

304304
if self.args.generate_hosts_file or self.args.generate_krb5_file:
305305
if self.args.generate_hosts_file:
@@ -385,6 +385,12 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="",
385385
out = f"{self.domain}\\{self.username}{used_ccache} {self.mark_stealth()}"
386386
self.logger.success(out)
387387

388+
# Add Kerberos credential to database for shares and other functionality
389+
self.db.add_credential("kerberos", domain, self.username, used_ccache)
390+
user_id = self.db.get_credential("kerberos", domain, self.username, used_ccache)
391+
host_id = self.db.get_hosts(self.host)[0].id
392+
self.db.add_loggedin_relation(user_id, host_id)
393+
388394
if not self.args.local_auth and self.username != "" and not self.args.delegate:
389395
add_user_bh(self.username, domain, self.logger, self.config)
390396
if self.admin_privs:
@@ -1357,14 +1363,30 @@ def shares(self):
13571363
user_id = None
13581364
try:
13591365
self.logger.debug(f"domain: {self.domain}")
1360-
user_id = self.db.get_user(self.domain.upper(), self.username)[0][0]
1366+
# Try to get user_id from credentials table based on authentication method
1367+
if hasattr(self, "password") and self.password:
1368+
user_id = self.db.get_credential("plaintext", self.domain, self.username, self.password)
1369+
elif hasattr(self, "hash") and self.hash:
1370+
user_id = self.db.get_credential("hash", self.domain, self.username, self.hash)
1371+
elif self.kerberos:
1372+
# For Kerberos, try to find the credential by matching domain and username
1373+
creds = self.db.get_credentials()
1374+
for cred in creds:
1375+
if (cred[4] == "kerberos" and
1376+
cred[1].lower() == self.domain.lower() and
1377+
cred[2].lower() == self.username.lower()):
1378+
user_id = cred[0]
1379+
break
1380+
else:
1381+
# Fallback to original method for other auth types
1382+
user_id = self.db.get_user(self.domain.upper(), self.username)[0][0]
13611383
except IndexError as e:
13621384
if self.kerberos or self.username == "":
1363-
pass
1385+
self.logger.debug(f"IndexError during user lookup (this is normal for Kerberos): {e!s}")
13641386
else:
13651387
self.logger.fail(f"IndexError: {e!s}")
13661388
except Exception as e:
1367-
self.logger.fail(f"Error getting user: {get_error_string(e)}")
1389+
self.logger.debug(f"Error getting user: {get_error_string(e)}")
13681390

13691391
try:
13701392
shares = self.conn.listShares()
@@ -1475,7 +1497,13 @@ def shares(self):
14751497

14761498
if user_id and share_name != "IPC$" and read:
14771499
try:
1478-
self.db.add_share(self.hostname, user_id, share_name, share_remark, read, write)
1500+
# Get the host ID from the database
1501+
hosts = self.db.get_hosts(self.host) # self.host is the IP address
1502+
if hosts:
1503+
host_id = hosts[0][0] # First element is the ID
1504+
self.db.add_share(host_id, user_id, share_name, share_remark, read, write)
1505+
else:
1506+
self.logger.debug(f"Could not find host {self.host} in database to save share {share_name}")
14791507
except Exception as e:
14801508
self.logger.debug(f"Error saving share info to DB: {get_error_string(e)}")
14811509

@@ -1488,11 +1516,21 @@ def shares(self):
14881516

14891517
for share in permissions:
14901518
name = share["name"]
1491-
remark = share["remark"]
1492-
perms = ",".join(share["access"])
1519+
remark = share.get("remark", "") or ""
1520+
access = share.get("access", "")
1521+
1522+
# Normalize permissions
1523+
if isinstance(access, str):
1524+
perms = access
1525+
elif isinstance(access, (list, tuple, set)):
1526+
perms = "".join(access) if all(len(str(x)) == 1 for x in access) else ", ".join(access)
1527+
else:
1528+
perms = ""
1529+
14931530
if self.args.shares and self.args.shares.lower() not in perms.lower():
14941531
continue
1495-
self.logger.highlight(f"{name:<15} {','.join(perms):<15} {remark}")
1532+
self.logger.highlight(f"{name:<15} {perms:<15} {remark}")
1533+
14961534
return permissions
14971535

14981536
def dir(self):
@@ -2077,7 +2115,7 @@ def dpapi(self):
20772115

20782116
if self.args.pvk is not None:
20792117
try:
2080-
self.pvkbytes = open(self.args.pvk, "rb").read()
2118+
self.pvkbytes = open(self.args.pvk, "rb").read() # noqa: SIM115
20812119
self.logger.success(f"Loading domain backupkey from {self.args.pvk}")
20822120
except Exception as e:
20832121
self.logger.fail(str(e))
@@ -2098,7 +2136,7 @@ def dpapi(self):
20982136
use_kcache=self.use_kcache,
20992137
)
21002138

2101-
self.output_file = open(self.output_file_template.format(output_folder="dpapi"), "w", encoding="utf-8")
2139+
self.output_file = open(self.output_file_template.format(output_folder="dpapi"), "w", encoding="utf-8") # noqa: SIM115
21022140

21032141
conn = upgrade_to_dploot_connection(connection=self.conn, target=target)
21042142
if conn is None:

nxc/protocols/smb/database.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -481,9 +481,7 @@ def get_hosts(self, filter_term=None, domain=None):
481481
elif filter_term and filter_term != "":
482482
q = format_host_query(q, filter_term, self.HostsTable)
483483

484-
results = self.db_execute(q).all()
485-
nxc_logger.debug(f"smb hosts() - results: {results}")
486-
return results
484+
return self.db_execute(q).all()
487485

488486
def is_group_valid(self, group_id):
489487
"""Check if this group ID is valid."""

0 commit comments

Comments
 (0)