|
63 | 63 | from dploot.lib.target import Target |
64 | 64 | from dploot.triage.sccm import SCCMTriage, SCCMCred, SCCMSecret, SCCMCollection |
65 | 65 |
|
66 | | -from time import time, ctime |
| 66 | +from time import time, ctime, sleep |
67 | 67 | from traceback import format_exc |
68 | 68 | from termcolor import colored |
69 | 69 | import contextlib |
@@ -1149,6 +1149,147 @@ def format_row(procInfo): |
1149 | 1149 | except SessionError: |
1150 | 1150 | self.logger.fail("Cannot list remote tasks, RDP is probably disabled.") |
1151 | 1151 |
|
| 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 | + |
1152 | 1293 | def shares(self): |
1153 | 1294 | temp_dir = ntpath.normpath("\\" + gen_random_string()) |
1154 | 1295 | temp_file = ntpath.normpath("\\" + gen_random_string() + ".txt") |
@@ -1370,7 +1511,7 @@ def get_dc_ips(self): |
1370 | 1511 | return dc_ips |
1371 | 1512 |
|
1372 | 1513 | 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") |
1374 | 1515 | return |
1375 | 1516 |
|
1376 | 1517 | def disks(self): |
|
0 commit comments