Skip to content

Commit b7d8eeb

Browse files
committed
Merge branch 'main' into qwinsta_grep
2 parents 6796ffa + 7ab386f commit b7d8eeb

3 files changed

Lines changed: 73 additions & 19 deletions

File tree

nxc/protocols/smb.py

Lines changed: 65 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,32 @@ def enumerate_sessions_info(self, sessions):
978978
except SessionError:
979979
self.logger.fail("RDP is probably not enabled, cannot list remote IPv4 addresses.")
980980

981+
@requires_admin
982+
def taskkill(self):
983+
with TSTS.LegacyAPI(self.conn, self.host, self.kerberos) as legacy:
984+
handle = legacy.hRpcWinStationOpenServer()
985+
if self.args.taskkill.isdigit():
986+
pidList = [int(self.args.taskkill)]
987+
else:
988+
res = legacy.hRpcWinStationGetAllProcesses(handle)
989+
if not res:
990+
self.logger.error("Could not get process list")
991+
return
992+
993+
pidList = [i["UniqueProcessId"] for i in res if i["ImageName"].lower() == self.args.taskkill.lower()]
994+
if not pidList:
995+
self.logger.fail(f"Could not find process named {self.args.taskkill}")
996+
return
997+
998+
for pid in pidList:
999+
try:
1000+
if legacy.hRpcWinStationTerminateProcess(handle, pid)["ErrorCode"]:
1001+
self.logger.highlight(f"Terminated PID {pid} ({self.args.taskkill})")
1002+
else:
1003+
self.logger.fail(f"Failed terminating PID {pid}")
1004+
except Exception as e:
1005+
self.logger.exception(f"Error terminating PID {pid}: {e}")
1006+
9811007
@requires_admin
9821008
def qwinsta(self):
9831009
desktop_states = {
@@ -1077,6 +1103,16 @@ def qwinsta(self):
10771103

10781104
@requires_admin
10791105
def tasklist(self):
1106+
# Formats a row to be printed on screen
1107+
def format_row(procInfo):
1108+
return template.format(
1109+
procInfo["ImageName"],
1110+
procInfo["UniqueProcessId"],
1111+
procInfo["SessionId"],
1112+
procInfo["pSid"],
1113+
f"{procInfo['WorkingSetSize'] // 1000:,} K",
1114+
)
1115+
10801116
try:
10811117
with TSTS.LegacyAPI(self.conn, self.host, self.kerberos) as legacy:
10821118
try:
@@ -1091,18 +1127,27 @@ def tasklist(self):
10911127
self.logger.success("Enumerated processes")
10921128
maxImageNameLen = max(len(i["ImageName"]) for i in res)
10931129
maxSidLen = max(len(i["pSid"]) for i in res)
1094-
template = "{: <%d} {: <8} {: <11} {: <%d} {: >12}" % (maxImageNameLen, maxSidLen) # noqa: UP031
1130+
template = f"{{: <{maxImageNameLen}}} {{: <8}} {{: <11}} {{: <{maxSidLen}}} {{: >12}}"
10951131
self.logger.highlight(template.format("Image Name", "PID", "Session#", "SID", "Mem Usage"))
10961132
self.logger.highlight(template.replace(": ", ":=").format("", "", "", "", ""))
1133+
found_task = False
1134+
1135+
# For each process on the remote host
10971136
for procInfo in res:
1098-
row = template.format(
1099-
procInfo["ImageName"],
1100-
procInfo["UniqueProcessId"],
1101-
procInfo["SessionId"],
1102-
procInfo["pSid"],
1103-
"{:,} K".format(procInfo["WorkingSetSize"] // 1000),
1104-
)
1105-
self.logger.highlight(row)
1137+
# If args.tasklist is not True then a process name was supplied
1138+
if self.args.tasklist is not True:
1139+
# So we look for it and print its information if found
1140+
if self.args.tasklist.lower() in procInfo["ImageName"].lower():
1141+
found_task = True
1142+
self.logger.highlight(format_row(procInfo))
1143+
# Else, no process was supplied, we print the entire list of remote processes
1144+
else:
1145+
self.logger.highlight(format_row(procInfo))
1146+
1147+
# If a process was suppliad to args.tasklist and it was not found, we print a fail message
1148+
if self.args.tasklist is not True and not found_task:
1149+
self.logger.fail(f"Didn't find process {self.args.tasklist}")
1150+
11061151
except SessionError:
11071152
self.logger.fail("Cannot list remote tasks, RDP is probably disabled.")
11081153

@@ -1214,16 +1259,20 @@ def shares(self):
12141259
error = get_error_string(e)
12151260
self.logger.debug(f"Error adding share: {error}")
12161261

1262+
if self.args.filter_shares:
1263+
self.logger.display("[REMOVED] Use the --shares read,write options instead.")
1264+
12171265
self.logger.display("Enumerated shares")
12181266
self.logger.highlight(f"{'Share':<15} {'Permissions':<15} {'Remark'}")
12191267
self.logger.highlight(f"{'-----':<15} {'-----------':<15} {'------'}")
1268+
12201269
for share in permissions:
12211270
name = share["name"]
12221271
remark = share["remark"]
1223-
perms = share["access"]
1224-
if self.args.filter_shares and not any(x in perms for x in self.args.filter_shares):
1272+
perms = ",".join(share["access"])
1273+
if self.args.shares and self.args.shares.lower() not in perms.lower():
12251274
continue
1226-
self.logger.highlight(f"{name:<15} {','.join(perms):<15} {remark}")
1275+
self.logger.highlight(f"{name:<15} {perms:<15} {remark}")
12271276
return permissions
12281277

12291278
def dir(self):
@@ -1503,16 +1552,16 @@ def rid_brute(self, max_rid=None):
15031552
max_rid = int(self.args.rid_brute)
15041553

15051554
KNOWN_PROTOCOLS = {
1506-
135: {"bindstr": rf"ncacn_ip_tcp:{self.host}"},
1507-
139: {"bindstr": rf"ncacn_np:{self.host}[\pipe\lsarpc]"},
1508-
445: {"bindstr": rf"ncacn_np:{self.host}[\pipe\lsarpc]"},
1555+
135: {"bindstr": rf"ncacn_ip_tcp:{self.remoteName}"},
1556+
139: {"bindstr": rf"ncacn_np:{self.remoteName}[\pipe\lsarpc]"},
1557+
445: {"bindstr": rf"ncacn_np:{self.remoteName}[\pipe\lsarpc]"},
15091558
}
15101559

15111560
try:
15121561
string_binding = KNOWN_PROTOCOLS[self.port]["bindstr"]
15131562
self.logger.debug(f"StringBinding {string_binding}")
15141563
rpc_transport = transport.DCERPCTransportFactory(string_binding)
1515-
rpc_transport.setRemoteHost(self.host)
1564+
rpc_transport.setRemoteHost(self.remoteName)
15161565

15171566
if hasattr(rpc_transport, "set_credentials"):
15181567
# This method exists only for selected protocol sequences.

nxc/protocols/smb/proto_args.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ def proto_args(parser, parents):
3737
cred_gathering_group.add_argument("--user", dest="userntds", type=str, help="Dump selected user from DC")
3838

3939
mapping_enum_group = smb_parser.add_argument_group("Mapping/Enumeration", "Options for Mapping/Enumerating")
40-
mapping_enum_group.add_argument("--shares", action="store_true", help="Enumerate shares and access")
40+
mapping_enum_group.add_argument("--shares", type=str, nargs="?", const="", help="Enumerate shares and access, filter on specified argument (read ; write ; read,write)")
4141
mapping_enum_group.add_argument("--dir", nargs="?", type=str, const="", help="List the content of a path (default path: '%(const)s')")
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)")
44-
mapping_enum_group.add_argument("--filter-shares", nargs="+", help="Filter share by access, option 'read' 'write' or 'read,write'")
44+
mapping_enum_group.add_argument("--filter-shares", nargs="+", help="Filter share by access, option 'READ' 'WRITE' or 'READ,WRITE'")
4545
mapping_enum_group.add_argument("--smb-sessions", action="store_true", help="Enumerate active smb sessions")
4646
mapping_enum_group.add_argument("--disks", action="store_true", help="Enumerate disks")
4747
mapping_enum_group.add_argument("--loggedon-users-filter", action="store", help="only search for specific user, works with regex")
@@ -54,7 +54,8 @@ def proto_args(parser, parents):
5454
mapping_enum_group.add_argument("--pass-pol", action="store_true", help="dump password policy")
5555
mapping_enum_group.add_argument("--rid-brute", nargs="?", type=int, const=4000, metavar="MAX_RID", help="Enumerate users by bruteforcing RIDs")
5656
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.")
57-
mapping_enum_group.add_argument("--tasklist", action="store_true", help="Enumerate running processes")
57+
mapping_enum_group.add_argument("--tasklist", type=str, nargs="?", const=True, help="Enumerate running processes and filter for the specified one if specified")
58+
mapping_enum_group.add_argument("--taskkill", type=str, help="Kills a specific PID or a proces name's PID's")
5859

5960
wmi_group = smb_parser.add_argument_group("WMI", "Options for WMI Queries")
6061
wmi_group.add_argument("--wmi", metavar="QUERY", type=str, help="issues the specified WMI query")

tests/e2e_commands.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --users-exp
1717
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --computers
1818
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --rid-brute
1919
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --local-groups
20+
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --qwinsta
21+
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --tasklist
22+
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --taskkill PID
23+
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --taskkill PROCESS_NAME
2024
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --gen-relay-list /tmp/relaylistOutputFilename.txt
2125
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --local-auth
2226
netexec smb TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --delegate LOGIN_USERNAME

0 commit comments

Comments
 (0)