Skip to content

Commit daa37dd

Browse files
authored
Update atexec.py
Signed-off-by: Kahvi-0xFF <46513413+Kahvi-0@users.noreply.github.com>
1 parent bc593ec commit daa37dd

1 file changed

Lines changed: 110 additions & 65 deletions

File tree

nxc/protocols/smb/atexec.py

Lines changed: 110 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import os
2+
import random
3+
import re
24
from textwrap import dedent
35
from impacket.dcerpc.v5 import tsch, transport
46
from impacket.dcerpc.v5.dtypes import NULL
@@ -80,52 +82,91 @@ def get_end_boundary(self):
8082
return end_boundary.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3]
8183

8284
def gen_xml(self, command):
85+
global cmdstdout
86+
global cmd_path
87+
#Random setting order to help with detection
88+
settings = [
89+
" <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>",
90+
" <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>",
91+
" <AllowHardTerminate>true</AllowHardTerminate>",
92+
" <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>",
93+
" <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>"
94+
]
95+
random.shuffle(settings)
96+
randomized_settings = "\n".join(settings)
97+
98+
settings2 = [
99+
" <AllowStartOnDemand>true</AllowStartOnDemand>",
100+
" <Hidden>true</Hidden>",
101+
" <Enabled>true</Enabled>",
102+
" <RunOnlyIfIdle>false</RunOnlyIfIdle>",
103+
" <WakeToRun>false</WakeToRun>",
104+
" <Priority>7</Priority>",
105+
" <ExecutionTimeLimit>P3D</ExecutionTimeLimit>"
106+
]
107+
random.shuffle(settings2)
108+
randomized_settings2 = "\n".join(settings2)
109+
110+
IdleSettings = [
111+
" <StopOnIdleEnd>true</StopOnIdleEnd>",
112+
" <RestartOnIdle>false</RestartOnIdle>"
113+
]
114+
random.shuffle(IdleSettings)
115+
randomized_IdleSettings = "\n".join(IdleSettings)
116+
random_digit = random.randint(2, 6)
117+
118+
match = re.match(r'^(.+?\\[^\\ ]+)\s+(.*)', command)
119+
if match:
120+
cmd_path = match.group(1)
121+
cmd_args = match.group(2)
122+
else:
123+
print("Could not split the command properly.")
124+
83125
xml = f"""<?xml version="1.0" encoding="UTF-16"?>
84-
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
126+
<Task version="1.{random_digit}" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
85127
<Triggers>
86-
<RegistrationTrigger>
87-
<EndBoundary>{self.get_end_boundary()}</EndBoundary>
88-
</RegistrationTrigger>
128+
<RegistrationTrigger>
129+
<EndBoundary>{self.get_end_boundary()}</EndBoundary>
130+
</RegistrationTrigger>
89131
</Triggers>
90132
<Principals>
91-
<Principal id="LocalSystem">
92-
<UserId>{self.run_task_as}</UserId>
93-
<RunLevel>HighestAvailable</RunLevel>
94-
</Principal>
133+
<Principal id="LocalSystem">
134+
<UserId>{self.run_task_as}</UserId>
135+
<RunLevel>HighestAvailable</RunLevel>
136+
</Principal>
95137
</Principals>
96138
<Settings>
97-
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
98-
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
99-
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
100-
<AllowHardTerminate>true</AllowHardTerminate>
101-
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
102-
<IdleSettings>
103-
<StopOnIdleEnd>true</StopOnIdleEnd>
104-
<RestartOnIdle>false</RestartOnIdle>
105-
</IdleSettings>
106-
<AllowStartOnDemand>true</AllowStartOnDemand>
107-
<Enabled>true</Enabled>
108-
<Hidden>true</Hidden>
109-
<RunOnlyIfIdle>false</RunOnlyIfIdle>
110-
<WakeToRun>false</WakeToRun>
111-
<ExecutionTimeLimit>P3D</ExecutionTimeLimit>
112-
<Priority>7</Priority>
139+
{randomized_settings}
140+
<IdleSettings>
141+
{randomized_IdleSettings}
142+
</IdleSettings>
143+
{randomized_settings2}
113144
</Settings>
114145
<Actions Context="LocalSystem">
115-
<Exec>
116-
<Command>cmd.exe</Command>
146+
<Exec>
147+
<Command>{cmd_path}</Command>
117148
"""
118149
if self.__retOutput:
119150
file_location = "\\Windows\\Temp\\" if self.output_file_location is None else self.output_file_location
120151
if self.output_filename is None:
121-
self.__output_filename = os.path.join(file_location, gen_random_string(6))
152+
self.__output_filename = os.path.join(file_location, gen_random_string(8))
122153
else:
123154
self.__output_filename = os.path.join(file_location, self.output_filename)
124-
argument_xml = f" <Arguments>/C {command} &gt; {self.__output_filename} 2&gt;&amp;1</Arguments>"
155+
156+
if "cmd" in cmd_path.lower() or "powershell" in cmd_path.lower():
157+
cmd_output = f"&gt; {self.__output_filename} 2&gt;&amp;1"
158+
cmdstdout = 1
159+
argument_xml = f" <Arguments>{cmd_args} {cmd_output}</Arguments>"
160+
else:
161+
cmd_output = ""
162+
cmdstdout = 0
163+
argument_xml = f" <Arguments>{cmd_args} {cmd_output}</Arguments>"
164+
125165

126166
elif self.__retOutput is False:
127-
argument_xml = f" <Arguments>/C {command}</Arguments>"
128-
167+
argument_xml = f" <Arguments>{cmd_args}</Arguments>"
168+
169+
129170
self.logger.debug("Generated argument XML: " + argument_xml)
130171
xml += argument_xml
131172

@@ -182,40 +223,44 @@ def execute_handler(self, command):
182223
tries = 1
183224
# Give the command a bit of time to execute before we try to read the output, 0.4 seconds was good in testing
184225
sleep(0.4)
185-
while True:
186-
try:
187-
self.logger.info(f"Attempting to read {self.__share}\\{self.__output_filename}")
188-
smbConnection.getFile(self.__share, self.__output_filename, self.output_callback)
189-
break
190-
except Exception as e:
191-
if tries >= self.__tries:
192-
self.logger.fail("ATEXEC: Could not retrieve output file, it may have been detected by AV. Please increase the number of tries with the option '--get-output-tries'. If it is still failing, try the 'wmi' protocol or another exec method")
193-
break
194-
if "STATUS_BAD_NETWORK_NAME" in str(e):
195-
self.logger.fail(f"ATEXEC: Getting the output file failed - target has blocked access to the share: {self.__share} (but the command may have executed!)")
196-
break
197-
elif "STATUS_VIRUS_INFECTED" in str(e):
198-
self.logger.fail("Command did not run because a virus was detected")
199-
break
200-
# When executing PowerShell and the command is still running, we get a sharing violation
201-
# We can use that information to wait longer than if the file is not found (probably av or something)
202-
if "STATUS_SHARING_VIOLATION" in str(e):
203-
self.logger.info(f"File {self.__share}\\{self.__output_filename} is still in use with {self.__tries - tries} tries left, retrying...")
204-
tries += 1
205-
sleep(1)
206-
elif "STATUS_OBJECT_NAME_NOT_FOUND" in str(e):
207-
self.logger.info(f"File {self.__share}\\{self.__output_filename} not found with {self.__tries - tries} tries left, deducting 10 tries and retrying...")
208-
tries += 10
209-
sleep(1)
210-
else:
211-
self.logger.debug(f"Exception when trying to read output file: {e!s}. {self.__tries - tries} tries left, retrying...")
212-
tries += 1
213-
sleep(1)
214-
215-
try:
216-
self.logger.debug(f"Deleting file {self.__share}\\{self.__output_filename}")
217-
smbConnection.deleteFile(self.__share, self.__output_filename)
218-
except Exception:
219-
pass
226+
if cmdstdout == 1:
227+
while True:
228+
try:
229+
self.logger.info(f"Attempting to read {self.__share}\\{self.__output_filename}")
230+
smbConnection.getFile(self.__share, self.__output_filename, self.output_callback)
231+
break
232+
except Exception as e:
233+
if tries >= self.__tries:
234+
self.logger.fail("ATEXEC: Could not retrieve output file, it may have been detected by AV. Please increase the number of tries with the option '--get-output-tries'. If it is still failing, try the 'wmi' protocol or another exec method")
235+
break
236+
if "STATUS_BAD_NETWORK_NAME" in str(e):
237+
self.logger.fail(f"ATEXEC: Getting the output file failed - target has blocked access to the share: {self.__share} (but the command may have executed!)")
238+
break
239+
elif "STATUS_VIRUS_INFECTED" in str(e):
240+
self.logger.fail("Command did not run because a virus was detected")
241+
break
242+
# When executing PowerShell and the command is still running, we get a sharing violation
243+
# We can use that information to wait longer than if the file is not found (probably av or something)
244+
if "STATUS_SHARING_VIOLATION" in str(e):
245+
self.logger.info(f"File {self.__share}\\{self.__output_filename} is still in use with {self.__tries - tries} tries left, retrying...")
246+
tries += 1
247+
sleep(1)
248+
elif "STATUS_OBJECT_NAME_NOT_FOUND" in str(e):
249+
self.logger.info(f"File {self.__share}\\{self.__output_filename} not found with {self.__tries - tries} tries left, deducting 10 tries and retrying...")
250+
tries += 10
251+
sleep(1)
252+
else:
253+
self.logger.debug(f"Exception when trying to read output file: {e!s}. {self.__tries - tries} tries left, retrying...")
254+
tries += 1
255+
sleep(1)
256+
257+
258+
try:
259+
self.logger.debug(f"Deleting file {self.__share}\\{self.__output_filename}")
260+
smbConnection.deleteFile(self.__share, self.__output_filename)
261+
except Exception:
262+
pass
220263

264+
else:
265+
self.logger.display("No output file was saved to be retrived")
221266
dce.disconnect()

0 commit comments

Comments
 (0)