Skip to content

Commit cb35641

Browse files
committed
Add powershell command execution method instead of using powershell in normal exec
1 parent 64aa7c0 commit cb35641

2 files changed

Lines changed: 56 additions & 7 deletions

File tree

nxc/protocols/wmi.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="",
208208
username = ccache.credentials[0].header["client"].prettyPrint().decode().split("@")[0]
209209
self.username = username
210210
used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}"
211-
211+
212212
try:
213213
self.logger.debug(f"Attempting to connect via WMI to {self.host}")
214214
self.conn.set_credentials(username=username, password=password, domain=domain, lmhash=lmhash, nthash=nthash, aesKey=self.aesKey)
@@ -369,7 +369,7 @@ def hash_login(self, domain, username, ntlm_hash):
369369
@requires_admin
370370
def wmi(self, wql=None, namespace=None):
371371
"""Execute WQL syntax via WMI
372-
372+
373373
This is done via the --wmi flag
374374
"""
375375
records = []

nxc/protocols/wmi/wmiexec.py

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,17 @@ def __init__(self, target, username, password, domain, lmhash, nthash, doKerbero
5151
iWbemLevel1Login.RemRelease()
5252
self.__win32Process, _ = self.__iWbemServices.GetObject("Win32_Process")
5353

54-
def execute(self, command, output=False):
54+
def execute(self, command, output=False, use_powershell=False):
55+
"""Execute a command on the remote host using WMI.
56+
Options:
57+
- No output
58+
- Output with bash (limited to ~1MB)
59+
- Output with PowerShell (recommended for larger outputs)
60+
"""
5561
if output:
5662
self.execute_WithOutput(command)
63+
elif output and use_powershell:
64+
self.execute_WithOutput_psh(command)
5765
else:
5866
command = self.__shell + command
5967
self.execute_remote(command)
@@ -80,13 +88,54 @@ def execute_WithOutput(self, command):
8088
self.logger.info(f"Waiting {self.__exec_timeout}s for command to complete.")
8189
time.sleep(self.__exec_timeout)
8290

91+
# 2. Base64 encode the file
92+
self.execute_remote(f"{self.__shell} certutil -encodehex -f {result_output} {result_output_b64} 0x40000001")
93+
time.sleep(0.5)
94+
95+
# 3. Store content in registry
96+
self.execute_remote(f'{self.__shell} for /F "usebackq" %G in ("{result_output_b64}") do reg add HKLM\\{self.__registry_Path} /v {keyName} /t REG_SZ /d "%G" /f')
97+
time.sleep(0.1)
98+
99+
self.queryRegistry(keyName)
100+
self.clean_up(result_output, result_output_b64)
101+
102+
def queryRegistry(self, keyName):
103+
try:
104+
# Spawn an instance of StdRegProv to access the registry
105+
self.logger.debug(f"Retrieving output from: HKLM\\{self.__registry_Path}")
106+
descriptor, _ = self.__iWbemServices.GetObject("StdRegProv")
107+
descriptor = descriptor.SpawnInstance()
108+
109+
# Retrieve the base64 content from the registry
110+
for _ in range(10):
111+
self.logger.debug(f"Retrieving key: {keyName}")
112+
outputBuffer_b64 = descriptor.GetStringValue(0x80000002, self.__registry_Path, keyName).sValue
113+
if outputBuffer_b64 is not None:
114+
break
115+
time.sleep(1)
116+
self.__outputBuffer = base64.b64decode(outputBuffer_b64).decode(self.__codec, errors="replace").rstrip("\r\n")
117+
except Exception:
118+
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'")
119+
120+
def execute_WithOutput_psh(self, command):
121+
"""Same functionality as execute_WithOutput, but uses PowerShell to handle larger outputs by splitting the base64 content into chunks and storing it in the registry."""
122+
result_output = f"C:\\windows\\temp\\{uuid.uuid4()!s}.txt"
123+
result_output_b64 = f"C:\\windows\\temp\\{uuid.uuid4()!s}.txt"
124+
keyName = str(uuid.uuid4())
125+
self.__registry_Path = f"Software\\Classes\\test_nxc_{gen_random_string(6)}"
126+
127+
# 1. Run the command and write output to file
128+
self.execute_remote(f'powershell {command} 1> "{result_output}" 2>&1')
129+
self.logger.info(f"Waiting {self.__exec_timeout}s for command to complete.")
130+
time.sleep(self.__exec_timeout)
131+
83132
# 2. Base64 encode the file using PowerShell
84-
self.execute_remote(f'{self.__shell} powershell -Command "[Convert]::ToBase64String([IO.File]::ReadAllBytes(\'{result_output}\')) | Out-File -Encoding ASCII \'{result_output_b64}\'"')
133+
self.execute_remote(f'powershell -Command "[Convert]::ToBase64String([IO.File]::ReadAllBytes(\'{result_output}\')) | Out-File -Encoding ASCII \'{result_output_b64}\'"')
85134
time.sleep(0.5)
86135

87136
# 3. Use PowerShell to split base64 content into 16KB chunks and store in registry
88137
self.execute_remote(
89-
f'{self.__shell} powershell -Command "$b64 = Get-Content -Raw \'{result_output_b64}\'; '
138+
f'powershell -Command "$b64 = Get-Content -Raw \'{result_output_b64}\'; '
90139
f'$chunksize = 16000; '
91140
f'$count = [math]::Ceiling($b64.Length / $chunksize); '
92141
f'for ($i = 0; $i -lt $count; $i++) {{ '
@@ -97,10 +146,10 @@ def execute_WithOutput(self, command):
97146
)
98147
time.sleep(0.1)
99148

100-
self.queryRegistry(keyName)
149+
self.queryRegistry_psh(keyName)
101150
self.clean_up(result_output, result_output_b64)
102151

103-
def queryRegistry(self, keyName):
152+
def queryRegistry_psh(self, keyName):
104153
try:
105154
# Spawn an instance of StdRegProv to access the registry
106155
self.logger.debug(f"Retrieving output from: HKLM\\{self.__registry_Path}")

0 commit comments

Comments
 (0)