Skip to content

Commit af822e5

Browse files
committed
Add -X for powershell execution
1 parent cb35641 commit af822e5

4 files changed

Lines changed: 31 additions & 8 deletions

File tree

nxc/protocols/wmi.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ def wmi(self, wql=None, namespace=None):
409409
return records
410410

411411
@requires_admin
412-
def execute(self, command=None, get_output=False):
412+
def execute(self, command=None, get_output=False, use_powershell=False):
413413
output = ""
414414

415415
# Execution via -x
@@ -428,18 +428,38 @@ def execute(self, command=None, get_output=False):
428428

429429
if self.args.exec_method == "wmiexec":
430430
exec_method = wmiexec.WMIEXEC(self.remoteName, self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.host, self.aesKey, self.logger, self.args.exec_timeout, self.args.codec)
431-
output = exec_method.execute(command, get_output)
431+
output = exec_method.execute(command, get_output, use_powershell=use_powershell)
432432

433433
elif self.args.exec_method == "wmiexec-event":
434434
exec_method = wmiexec_event.WMIEXEC_EVENT(self.remoteName, self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.host, self.aesKey, self.logger, self.args.exec_timeout, self.args.codec)
435-
output = exec_method.execute(command, get_output)
435+
output = exec_method.execute(command, get_output, use_powershell=use_powershell)
436436

437437
self.conn.disconnect()
438438
if self.args.execute and get_output:
439439
self.logger.success(f'Executed command: "{command}" via {self.args.exec_method}')
440440
buf = StringIO(output).readlines()
441441
for line in buf:
442-
self.logger.highlight(line.strip())
442+
if line.strip():
443+
self.logger.highlight(line.strip())
443444
return output
444445
else:
445446
return output
447+
448+
def execute_psh(self, command=None, get_output=False):
449+
# Execution via -X
450+
if not command and self.args.execute_psh:
451+
command = self.args.execute_psh
452+
if not self.args.no_output:
453+
get_output = True
454+
455+
output = self.execute(command, get_output, use_powershell=True)
456+
457+
if self.args.execute_psh and get_output:
458+
self.logger.success(f'Executed PowerShell command: "{command}" via {self.args.exec_method}')
459+
buf = StringIO(output).readlines()
460+
for line in buf:
461+
if line.strip().rstrip("\ufeff"):
462+
self.logger.highlight(line.strip())
463+
return output
464+
else:
465+
return output

nxc/protocols/wmi/proto_args.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def proto_args(parser, parents):
1616
cgroup = wmi_parser.add_argument_group("Command Execution", "Options for executing commands")
1717
cgroup.add_argument("--no-output", action="store_true", help="do not retrieve command output")
1818
cgroup.add_argument("-x", metavar="COMMAND", dest="execute", type=str, help="Creates a new cmd process and executes the specified command with output")
19+
cgroup.add_argument("-X", metavar="COMMAND", dest="execute_psh", type=str, help="Creates a new PowerShell process and executes the specified command with output")
1920
cgroup.add_argument("--exec-method", choices={"wmiexec", "wmiexec-event"}, default="wmiexec", help="method to execute the command. (default: wmiexec). [wmiexec (win32_process + StdRegProv)]: get command results over registry instead of using smb connection. [wmiexec-event (T1546.003)]: this method is not very stable, highly recommend use this method in single host, using on multiple hosts may crash (just try again if it crashed).")
2021
cgroup.add_argument("--exec-timeout", default=2, metavar="exec_timeout", dest="exec_timeout", type=int, help="Set timeout (in seconds) when executing a command, minimum 5 seconds is recommended. Default: %(default)s")
2122
cgroup.add_argument("--codec", default="utf-8", help="Set encoding used (codec) from the target's output (default: utf-8). If errors are detected, run chcp.com at the target & map the result with https://docs.python.org/3/library/codecs.html#standard-encodings and then execute again with --codec and the corresponding codec")

nxc/protocols/wmi/wmiexec.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def execute(self, command, output=False, use_powershell=False):
5858
- Output with bash (limited to ~1MB)
5959
- Output with PowerShell (recommended for larger outputs)
6060
"""
61-
if output:
61+
if output and not use_powershell:
6262
self.execute_WithOutput(command)
6363
elif output and use_powershell:
6464
self.execute_WithOutput_psh(command)
@@ -125,7 +125,7 @@ def execute_WithOutput_psh(self, command):
125125
self.__registry_Path = f"Software\\Classes\\test_nxc_{gen_random_string(6)}"
126126

127127
# 1. Run the command and write output to file
128-
self.execute_remote(f'powershell {command} 1> "{result_output}" 2>&1')
128+
self.execute_remote(f'powershell -Command {command} 1> "{result_output}" 2>&1')
129129
self.logger.info(f"Waiting {self.__exec_timeout}s for command to complete.")
130130
time.sleep(self.__exec_timeout)
131131

@@ -173,7 +173,7 @@ def queryRegistry_psh(self, keyName):
173173
chunk_name = f"{keyName}_chunk_{i}"
174174
self.logger.debug(f"Retrieving chunk: {chunk_name}")
175175
outputBuffer_b64 += descriptor.GetStringValue(0x80000002, self.__registry_Path, chunk_name).sValue
176-
self.__outputBuffer = base64.b64decode(outputBuffer_b64).decode(self.__codec, errors="replace").rstrip("\r\n")
176+
self.__outputBuffer = base64.b64decode(outputBuffer_b64).decode("utf-16le", errors="replace").rstrip("\r\n")
177177
except Exception:
178178
self.logger.fail("WMIEXEC: Could not retrieve output file! Either command timed out or AV killed the process. Please try increasing the timeout: '--exec-timeout 10'")
179179

nxc/protocols/wmi/wmiexec_event.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,11 @@ def __init__(self, target, username, password, domain, lmhash, nthash, doKerbero
5757
self.__iWbemServices = iWbemLevel1Login.NTLMLogin("//./root/subscription", NULL, NULL)
5858
iWbemLevel1Login.RemRelease()
5959

60-
def execute(self, command, output=False):
60+
def execute(self, command, output=False, use_powershell=False):
6161
if "'" in command:
6262
command = command.replace("'", r'"')
63+
if use_powershell:
64+
command = f"powershell.exe -Command {command}"
6365
self.__retOutput = output
6466
self.execute_handler(command)
6567

0 commit comments

Comments
 (0)