Skip to content

Commit 83b7148

Browse files
authored
Merge pull request Pennyw0rth#824 from MaxToffy/reg-sessions
Add --reg-sessions option to SMB protocol
2 parents 84d8f10 + 70922c5 commit 83b7148

2 files changed

Lines changed: 148 additions & 6 deletions

File tree

nxc/protocols/smb.py

Lines changed: 143 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
from dploot.lib.target import Target
6464
from dploot.triage.sccm import SCCMTriage, SCCMCred, SCCMSecret, SCCMCollection
6565

66-
from time import time, ctime
66+
from time import time, ctime, sleep
6767
from traceback import format_exc
6868
from termcolor import colored
6969
import contextlib
@@ -1149,6 +1149,147 @@ def format_row(procInfo):
11491149
except SessionError:
11501150
self.logger.fail("Cannot list remote tasks, RDP is probably disabled.")
11511151

1152+
def reg_sessions(self):
1153+
1154+
def output(sessions):
1155+
if sessions:
1156+
# Calculate max lengths for formatting
1157+
maxSidLen = max(len(key) + 1 for key in sessions)
1158+
maxSidLen = max(maxSidLen, len("SID") + 1)
1159+
maxUsernameLen = max(len(vals["Username"] + vals["Domain"]) + 1 for vals in sessions.values()) + 1
1160+
maxUsernameLen = max(maxUsernameLen, len("USERNAME") + 1)
1161+
1162+
# Create the template for formatting
1163+
template = (f"{{USERNAME: <{maxUsernameLen}}} {{SID: <{maxSidLen}}}")
1164+
1165+
# Create headers
1166+
header = template.format(USERNAME="USERNAME", SID="SID")
1167+
header2 = template.replace(" <", "=<").format(USERNAME="", SID="")
1168+
1169+
# Store result
1170+
result = [header, header2]
1171+
1172+
for sid, vals in sessions.items():
1173+
username = vals["Username"]
1174+
domain = vals["Domain"]
1175+
user_full = f"{domain}\\{username}" if username else ""
1176+
1177+
row = template.format(USERNAME=user_full, SID=sid)
1178+
result.append(row)
1179+
1180+
self.logger.success("Remote Registry enumerated sessions")
1181+
for row in result:
1182+
self.logger.highlight(row)
1183+
else:
1184+
self.logger.info(f"No active session found for specified user(s) using the Remote Registry service on {self.hostname}.")
1185+
1186+
# Bind to the Remote Registry Pipe
1187+
rpctransport = transport.SMBTransport(self.conn.getRemoteName(), self.conn.getRemoteHost(), filename=r"\winreg", smb_connection=self.conn)
1188+
for binding_attempts in range(2, 0, -1):
1189+
dce = rpctransport.get_dce_rpc()
1190+
try:
1191+
dce.connect()
1192+
dce.bind(rrp.MSRPC_UUID_RRP)
1193+
break
1194+
except SessionError as e:
1195+
self.logger.debug(f"Could not bind to the Remote Registry on {self.hostname}: {e}")
1196+
if binding_attempts == 1: # Last attempt
1197+
self.logger.info(f"The Remote Registry service seems to be disabled on {self.hostname}.")
1198+
return
1199+
# STATUS_PIPE_NOT_AVAILABLE : Waiting 1 second for the service to start (if idle and set to 'Automatic' startup type)
1200+
sleep(1)
1201+
1202+
# Open HKU hive
1203+
try:
1204+
resp = rrp.hOpenUsers(dce)
1205+
except DCERPCException as e:
1206+
if "rpc_s_access_denied" in str(e).lower():
1207+
self.logger.info(f"Access denied while enumerating session using the Remote Registry on {self.hostname}.")
1208+
return
1209+
else:
1210+
self.logger.fail(f"Exception connecting to RPC on {self.hostname}: {e}")
1211+
except Exception as e:
1212+
self.logger.fail(f"Exception connecting to RPC on {self.hostname}: {e}")
1213+
1214+
# Enumerate HKU subkeys and recover SIDs
1215+
sid_filter = "^S-1-.*\\d$"
1216+
exclude_sid = ["S-1-5-18", "S-1-5-19", "S-1-5-20"]
1217+
1218+
key_handle = resp["phKey"]
1219+
index = 1
1220+
sessions = {}
1221+
1222+
while True:
1223+
try:
1224+
resp = rrp.hBaseRegEnumKey(dce, key_handle, index)
1225+
sid = resp["lpNameOut"].rstrip("\0")
1226+
if re.match(sid_filter, sid) and sid not in exclude_sid:
1227+
self.logger.info(f"User with SID {sid} is logged in on {self.hostname}")
1228+
sessions.setdefault(sid, {"Username": "", "Domain": ""})
1229+
index += 1
1230+
except rrp.DCERPCSessionError as e:
1231+
if "ERROR_NO_MORE_ITEMS" in str(e):
1232+
self.logger.debug(f"No more items found in HKU on {self.hostname}.")
1233+
break
1234+
else:
1235+
self.logger.fail(f"Error enumerating HKU subkeys on {self.hostname}: {e}")
1236+
break
1237+
1238+
rrp.hBaseRegCloseKey(dce, key_handle)
1239+
dce.disconnect()
1240+
1241+
if not sessions:
1242+
self.logger.info(f"No sessions found via the Remote Registry service on {self.hostname}.")
1243+
return
1244+
1245+
# Bind to the LSARPC Pipe for SID resolution
1246+
rpctransport = transport.SMBTransport(self.conn.getRemoteName(), self.conn.getRemoteHost(), filename=r"\lsarpc", smb_connection=self.conn)
1247+
dce = rpctransport.get_dce_rpc()
1248+
try:
1249+
dce.connect()
1250+
dce.bind(lsat.MSRPC_UUID_LSAT)
1251+
except Exception as e:
1252+
self.logger.debug(f"Failed to connect to LSARPC for SID resolution on {self.hostname}: {e}")
1253+
output(sessions)
1254+
return
1255+
1256+
# Resolve SIDs with names
1257+
policy_handle = lsad.hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES)["PolicyHandle"]
1258+
try:
1259+
resp = lsat.hLsarLookupSids(dce, policy_handle, sessions.keys(), lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta)
1260+
except DCERPCException as e:
1261+
if str(e).find("STATUS_SOME_NOT_MAPPED") >= 0:
1262+
resp = e.get_packet()
1263+
self.logger.debug(f"Could not resolve some SIDs: {e}")
1264+
else:
1265+
resp = None
1266+
self.logger.debug(f"Could not resolve SID(s): {e}")
1267+
1268+
if resp:
1269+
for sid, item in zip(sessions.keys(), resp["TranslatedNames"]["Names"], strict=False):
1270+
if item["DomainIndex"] >= 0:
1271+
sessions[sid]["Username"] = item["Name"]
1272+
sessions[sid]["Domain"] = resp["ReferencedDomains"]["Domains"][item["DomainIndex"]]["Name"]
1273+
1274+
# Filter for usernames
1275+
if self.args.reg_sessions:
1276+
arg = self.args.reg_sessions
1277+
if os.path.isfile(arg):
1278+
with open(arg) as f:
1279+
usernames = [line.strip().lower() for line in f if line.strip()]
1280+
else:
1281+
usernames = [arg.lower()]
1282+
1283+
filtered_sessions = {}
1284+
for sid, info in sessions.items():
1285+
if info["Username"].lower() not in usernames:
1286+
continue
1287+
else:
1288+
filtered_sessions[sid] = info
1289+
output(filtered_sessions)
1290+
else:
1291+
output(sessions)
1292+
11521293
def shares(self):
11531294
temp_dir = ntpath.normpath("\\" + gen_random_string())
11541295
temp_file = ntpath.normpath("\\" + gen_random_string() + ".txt")
@@ -1370,7 +1511,7 @@ def get_dc_ips(self):
13701511
return dc_ips
13711512

13721513
def smb_sessions(self):
1373-
self.logger.fail("[REMOVED] Use option --qwinsta or --loggedon-users")
1514+
self.logger.fail("[REMOVED] Use option --reg-sessions --qwinsta or --loggedon-users")
13741515
return
13751516

13761517
def disks(self):

nxc/protocols/smb/proto_args.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,18 @@ def proto_args(parser, parents):
4242
mapping_enum_group.add_argument("--interfaces", action="store_true", help="Enumerate network interfaces")
4343
mapping_enum_group.add_argument("--no-write-check", action="store_true", help="Skip write check on shares (avoid leaving traces when missing delete permissions)")
4444
mapping_enum_group.add_argument("--filter-shares", nargs="+", help="Filter share by access, option 'READ' 'WRITE' or 'READ,WRITE'")
45-
mapping_enum_group.add_argument("--smb-sessions", action="store_true", help="Enumerate active smb sessions")
4645
mapping_enum_group.add_argument("--disks", action="store_true", help="Enumerate disks")
47-
mapping_enum_group.add_argument("--loggedon-users-filter", action="store", help="only search for specific user, works with regex")
48-
mapping_enum_group.add_argument("--loggedon-users", nargs="?", const="", help="Enumerate logged on users, if a user is specified than a regex filter is applied.")
4946
mapping_enum_group.add_argument("--users", nargs="*", metavar="USER", help="Enumerate domain users, if a user is specified than only its information is queried.")
5047
mapping_enum_group.add_argument("--users-export", help="Enumerate domain users and export them to the specified file")
5148
mapping_enum_group.add_argument("--groups", nargs="?", const="", metavar="GROUP", help="Enumerate domain groups, if a group is specified than its members are Enumerated")
52-
mapping_enum_group.add_argument("--computers", nargs="?", const="", metavar="COMPUTER", help="Enumerate computer users")
5349
mapping_enum_group.add_argument("--local-groups", nargs="?", const="", metavar="GROUP", help="Enumerate local groups, if a group is specified then its members are Enumerated")
50+
mapping_enum_group.add_argument("--computers", nargs="?", const="", metavar="COMPUTER", help="Enumerate computer users")
5451
mapping_enum_group.add_argument("--pass-pol", action="store_true", help="dump password policy")
5552
mapping_enum_group.add_argument("--rid-brute", nargs="?", type=int, const=4000, metavar="MAX_RID", help="Enumerate users by bruteforcing RIDs")
53+
mapping_enum_group.add_argument("--smb-sessions", action="store_true", help="Enumerate active smb sessions")
54+
mapping_enum_group.add_argument("--reg-sessions", type=str, nargs="?", const="", help="Enumerate users sessions using the Remote Registry. If a username is given, filter for it. If a file is given, filter for listed usernames. If no value is given, list all.")
55+
mapping_enum_group.add_argument("--loggedon-users", nargs="?", const="", help="Enumerate logged on users, if a user is specified than a regex filter is applied.")
56+
mapping_enum_group.add_argument("--loggedon-users-filter", action="store", help="only search for specific user, works with regex")
5657
mapping_enum_group.add_argument("--qwinsta", type=str, nargs="?", const="", help="Enumerate user sessions. If a username is given, filter for it; if a file is given, filter for listed usernames. If no value is given, list all.")
5758
mapping_enum_group.add_argument("--tasklist", type=str, nargs="?", const=True, help="Enumerate running processes and filter for the specified one if specified")
5859
mapping_enum_group.add_argument("--taskkill", type=str, help="Kills a specific PID or a proces name's PID's")

0 commit comments

Comments
 (0)