Skip to content

Commit 1440a6b

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

1 file changed

Lines changed: 68 additions & 112 deletions

File tree

nxc/protocols/smb/atexec.py

Lines changed: 68 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import os
2-
import random
3-
import re
42
from textwrap import dedent
53
from impacket.dcerpc.v5 import tsch, transport
64
from impacket.dcerpc.v5.dtypes import NULL
@@ -12,7 +10,7 @@
1210

1311
class TSCH_EXEC:
1412
def __init__(self, target, share_name, username, password, domain, doKerberos=False, aesKey=None, remoteHost=None, kdcHost=None, hashes=None, logger=None, tries=None, share=None,
15-
# These options are used by the schtask_as module, except the run_task_as
13+
# These options are used by the schtask_as module, except the run_task_as
1614
# that defaults to NT AUTHORITY\System user (SID S-1-5-18) if not specified
1715
run_task_as="S-1-5-18", run_cmd=None, output_filename=None, task_name=None, output_file_location=None):
1816
self.__target = target
@@ -82,90 +80,52 @@ def get_end_boundary(self):
8280
return end_boundary.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3]
8381

8482
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-
match = re.match(r'^(.+?\\[^\\ ]+)\s+(.*)', command)
118-
if match:
119-
cmd_path = match.group(1)
120-
cmd_args = match.group(2)
121-
else:
122-
print("Could not split the command properly.")
123-
12483
xml = f"""<?xml version="1.0" encoding="UTF-16"?>
125-
<Task version="1.{random_digit}" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
84+
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
12685
<Triggers>
127-
<RegistrationTrigger>
128-
<EndBoundary>{self.get_end_boundary()}</EndBoundary>
129-
</RegistrationTrigger>
86+
<RegistrationTrigger>
87+
<EndBoundary>{self.get_end_boundary()}</EndBoundary>
88+
</RegistrationTrigger>
13089
</Triggers>
13190
<Principals>
132-
<Principal id="LocalSystem">
133-
<UserId>{self.run_task_as}</UserId>
134-
<RunLevel>HighestAvailable</RunLevel>
135-
</Principal>
91+
<Principal id="LocalSystem">
92+
<UserId>{self.run_task_as}</UserId>
93+
<RunLevel>HighestAvailable</RunLevel>
94+
</Principal>
13695
</Principals>
13796
<Settings>
138-
{randomized_settings}
139-
<IdleSettings>
140-
{randomized_IdleSettings}
141-
</IdleSettings>
142-
{randomized_settings2}
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>
143113
</Settings>
144114
<Actions Context="LocalSystem">
145-
<Exec>
146-
<Command>{cmd_path}</Command>
115+
<Exec>
116+
<Command>cmd.exe</Command>
147117
"""
148118
if self.__retOutput:
149119
file_location = "\\Windows\\Temp\\" if self.output_file_location is None else self.output_file_location
150120
if self.output_filename is None:
151-
self.__output_filename = os.path.join(file_location, gen_random_string(8))
121+
self.__output_filename = os.path.join(file_location, gen_random_string(6))
152122
else:
153123
self.__output_filename = os.path.join(file_location, self.output_filename)
154-
155-
if "cmd" in cmd_path.lower() or "powershell" in cmd_path.lower():
156-
cmd_output = f"&gt; {self.__output_filename} 2&gt;&amp;1"
157-
cmdstdout = 1
158-
argument_xml = f" <Arguments>{cmd_args} {cmd_output}</Arguments>"
159-
else:
160-
cmd_output = ""
161-
cmdstdout = 0
162-
argument_xml = f" <Arguments>{cmd_args} {cmd_output}</Arguments>"
163-
124+
argument_xml = f" <Arguments>/C {command} &gt; {self.__output_filename} 2&gt;&amp;1</Arguments>"
164125

165126
elif self.__retOutput is False:
166-
argument_xml = f" <Arguments>{cmd_args}</Arguments>"
167-
168-
127+
argument_xml = f" <Arguments>/C {command}</Arguments>"
128+
169129
self.logger.debug("Generated argument XML: " + argument_xml)
170130
xml += argument_xml
171131

@@ -222,44 +182,40 @@ def execute_handler(self, command):
222182
tries = 1
223183
# Give the command a bit of time to execute before we try to read the output, 0.4 seconds was good in testing
224184
sleep(0.4)
225-
if cmdstdout == 1:
226-
while True:
227-
try:
228-
self.logger.info(f"Attempting to read {self.__share}\\{self.__output_filename}")
229-
smbConnection.getFile(self.__share, self.__output_filename, self.output_callback)
230-
break
231-
except Exception as e:
232-
if tries >= self.__tries:
233-
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")
234-
break
235-
if "STATUS_BAD_NETWORK_NAME" in str(e):
236-
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!)")
237-
break
238-
elif "STATUS_VIRUS_INFECTED" in str(e):
239-
self.logger.fail("Command did not run because a virus was detected")
240-
break
241-
# When executing PowerShell and the command is still running, we get a sharing violation
242-
# We can use that information to wait longer than if the file is not found (probably av or something)
243-
if "STATUS_SHARING_VIOLATION" in str(e):
244-
self.logger.info(f"File {self.__share}\\{self.__output_filename} is still in use with {self.__tries - tries} tries left, retrying...")
245-
tries += 1
246-
sleep(1)
247-
elif "STATUS_OBJECT_NAME_NOT_FOUND" in str(e):
248-
self.logger.info(f"File {self.__share}\\{self.__output_filename} not found with {self.__tries - tries} tries left, deducting 10 tries and retrying...")
249-
tries += 10
250-
sleep(1)
251-
else:
252-
self.logger.debug(f"Exception when trying to read output file: {e!s}. {self.__tries - tries} tries left, retrying...")
253-
tries += 1
254-
sleep(1)
255-
256-
257-
try:
258-
self.logger.debug(f"Deleting file {self.__share}\\{self.__output_filename}")
259-
smbConnection.deleteFile(self.__share, self.__output_filename)
260-
except Exception:
261-
pass
262-
263-
else:
264-
self.logger.display("No output file was saved to be retrived")
265-
dce.disconnect()
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
220+
221+
dce.disconnect()

0 commit comments

Comments
 (0)