From ab6509daf1418e60e0cd440cb11c54e56cd6af69 Mon Sep 17 00:00:00 2001 From: XiaoliChan <2209553467@qq.com> Date: Sat, 9 Sep 2023 12:25:34 +0800 Subject: [PATCH 01/16] [wmi] Improve admin check & bug fix Signed-off-by: XiaoliChan <2209553467@qq.com> --- cme/connection.py | 6 +++++- cme/protocols/wmi.py | 41 +++++++++++++++++------------------------ poetry.lock | 7 +++---- pyproject.toml | 2 +- 4 files changed, 26 insertions(+), 30 deletions(-) diff --git a/cme/connection.py b/cme/connection.py index 09aa0ef3..5c86b196 100755 --- a/cme/connection.py +++ b/cme/connection.py @@ -42,7 +42,7 @@ def requires_admin(func): return wraps(func)(_decorator) -def dcom_FirewallChecker(iInterface, timeout): +def dcom_FirewallChecker(iInterface, timeout=None): stringBindings = iInterface.get_cinstance().get_string_bindings() for strBinding in stringBindings: if strBinding['wTowerId'] == 7: @@ -60,6 +60,10 @@ def dcom_FirewallChecker(iInterface, timeout): stringBinding = 'ncacn_ip_tcp:%s%s' % (iInterface.get_target(), bindingPort) if "stringBinding" not in locals(): return True, None + + # if not timeout, which means not doing firewall check. + if not timeout: + return True, stringBinding try: rpctransport = transport.DCERPCTransportFactory(stringBinding) rpctransport.set_connect_timeout(timeout) diff --git a/cme/protocols/wmi.py b/cme/protocols/wmi.py index c5eb8fb9..72e2a5a6 100644 --- a/cme/protocols/wmi.py +++ b/cme/protocols/wmi.py @@ -164,36 +164,29 @@ class wmi(connection): def check_if_admin(self): try: dcom = DCOMConnection(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.doKerberos ,kdcHost=self.kdcHost, aesKey=self.aesKey) + dcom.set_connect_timeout(self.args.rpc_timeout) iInterface = dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login) - flag, self.stringBinding = dcom_FirewallChecker(iInterface, self.args.rpc_timeout) + _, self.stringBinding = dcom_FirewallChecker(iInterface) + iWbemLevel1Login = IWbemLevel1Login(iInterface) + iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL) except Exception as e: if "dcom" in locals(): dcom.disconnect() - if not str(e).find("access_denied") > 0: - self.logger.fail(str(e)) - else: - if not flag or not self.stringBinding: - dcom.disconnect() - error_msg = f'Check admin error: dcom initialization failed with stringbinding: "{self.stringBinding}", please try "--rpc-timeout" option. (probably is admin)' - - if not self.stringBinding: - error_msg = "Check admin error: dcom initialization failed: can't get target stringbinding, maybe cause by IPv6 or any other issues, please check your target again" - - self.logger.fail(error_msg) if not flag else self.logger.debug(error_msg) - else: - try: - iWbemLevel1Login = IWbemLevel1Login(iInterface) - iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL) - except Exception as e: - dcom.disconnect() + error_msg = str(e).lower() - if not str(e).find("access_denied") > 0: - self.logger.fail(str(e)) - else: - dcom.disconnect() - self.logger.extra['protocol'] = "WMI" - self.admin_privs = True + if error_msg.find("access_denied") > 0: + pass + else: + if error_msg.find("timed out") > 0 or error_msg.lower().find("connection refused") > 0: + error_msg = f'Check admin error: dcom initialization failed with stringbinding: "{self.stringBinding}", please try "--rpc-timeout" option. (probably is admin)' + elif not self.stringBinding: + error_msg = "Check admin error: dcom initialization failed: can't get target stringbinding, maybe cause by IPv6 or any other issues, please check your target again" + self.logger.fail(error_msg) + else: + dcom.disconnect() + self.logger.extra['protocol'] = "WMI" + self.admin_privs = True return def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False): diff --git a/poetry.lock b/poetry.lock index 5065c3d1..c3d433fd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -853,9 +853,8 @@ files = [ [[package]] name = "impacket" -version = "0.12.0.dev1+20230816.160145.f6e03b99" +version = "0.12.0.dev1+20230905.183005.f0a4bf2b" description = "Network protocols Constructors and Dissectors" -category = "main" optional = false python-versions = "*" files = [] @@ -876,9 +875,9 @@ six = "*" [package.source] type = "git" -url = "https://github.com/mpgn/impacket.git" +url = "https://github.com/XiaoliChan/impacket.git" reference = "gkdi" -resolved_reference = "f6e03b99ce9da7f502a03488c8d26151236fa679" +resolved_reference = "0ccac0aa8e07655678b717debc97696581080d9b" [[package]] name = "importlib-metadata" diff --git a/pyproject.toml b/pyproject.toml index 4b6bff24..cbc915ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ neo4j = "^4.1.1" pylnk3 = "^0.4.2" pypsrp = "^0.7.0" paramiko = "^2.7.2" -impacket = { git = "https://github.com/mpgn/impacket.git", branch = "gkdi" } +impacket = { git = "https://github.com/XiaoliChan/impacket.git", branch = "gkdi" } dsinternals = "^1.2.4" xmltodict = "^0.12.0" terminaltables = "^3.1.0" From 823f9cadf26efd1fd83c5503028bfcb09c4b3cbf Mon Sep 17 00:00:00 2001 From: XiaoliChan <2209553467@qq.com> Date: Sat, 9 Sep 2023 22:13:12 +0800 Subject: [PATCH 02/16] [wmi] switch imapcket back to mpgn's forked Signed-off-by: XiaoliChan <2209553467@qq.com> --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index c3d433fd..25c43c42 100644 --- a/poetry.lock +++ b/poetry.lock @@ -853,7 +853,7 @@ files = [ [[package]] name = "impacket" -version = "0.12.0.dev1+20230905.183005.f0a4bf2b" +version = "0.12.0.dev1+20230909.154612.3beeda7c" description = "Network protocols Constructors and Dissectors" optional = false python-versions = "*" @@ -875,9 +875,9 @@ six = "*" [package.source] type = "git" -url = "https://github.com/XiaoliChan/impacket.git" +url = "https://github.com/mpgn/impacket.git" reference = "gkdi" -resolved_reference = "0ccac0aa8e07655678b717debc97696581080d9b" +resolved_reference = "3beeda7c3188936ed20f58c2c169430c2cfdfb1a" [[package]] name = "importlib-metadata" diff --git a/pyproject.toml b/pyproject.toml index cbc915ed..4b6bff24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ neo4j = "^4.1.1" pylnk3 = "^0.4.2" pypsrp = "^0.7.0" paramiko = "^2.7.2" -impacket = { git = "https://github.com/XiaoliChan/impacket.git", branch = "gkdi" } +impacket = { git = "https://github.com/mpgn/impacket.git", branch = "gkdi" } dsinternals = "^1.2.4" xmltodict = "^0.12.0" terminaltables = "^3.1.0" From 0e528ae8f0eaa436d0a4fb315541c140763f98f7 Mon Sep 17 00:00:00 2001 From: Alex <61382599+NeffIsBack@users.noreply.github.com> Date: Sat, 9 Sep 2023 19:19:41 +0200 Subject: [PATCH 03/16] Add us to cli output --- cme/cli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cme/cli.py b/cme/cli.py index 623ff288..a8f7e407 100755 --- a/cme/cli.py +++ b/cme/cli.py @@ -24,7 +24,8 @@ def gen_cli_args(): \______|| _| `._____|/__/ \__\ \______||__|\__\ |__| |__| /__/ \__\ | _| |_______|/__/ \__\ |_______| \______| A swiss army knife for pentesting networks - Forged by @byt3bl33d3r and @mpgn_x64 using the powah of dank memes + Forged by @byt3bl33d3r and @mpgn_x64 using the powah of dank memes. + Maintained as an open source project by @NeffIsBack, @MJHallenbeck, @_zblurx {highlight('Version', 'red')} : {highlight(VERSION)} {highlight('Codename', 'red')}: {highlight(CODENAME)} From 2400bc6c200963a273463d160fab52836fa8cb8c Mon Sep 17 00:00:00 2001 From: Alex <61382599+NeffIsBack@users.noreply.github.com> Date: Sat, 9 Sep 2023 19:20:46 +0200 Subject: [PATCH 04/16] Make exec fail messages more precise --- cme/protocols/smb/atexec.py | 2 +- cme/protocols/smb/mmcexec.py | 2 +- cme/protocols/smb/proto_args.py | 2 +- cme/protocols/smb/smbexec.py | 2 +- cme/protocols/smb/wmiexec.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cme/protocols/smb/atexec.py b/cme/protocols/smb/atexec.py index 1e4d02bd..049b84ed 100755 --- a/cme/protocols/smb/atexec.py +++ b/cme/protocols/smb/atexec.py @@ -203,7 +203,7 @@ class TSCH_EXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f'ATEXEC: Get output file error, maybe got detected by AV software, please increase the number of tries with the option "--get-output-tries". If it\'s still failing maybe something is blocking the schedule job, try another exec method') + self.logger.fail(f'ATEXEC: Couldn\'t retrieve output file, maybe got detected by AV. Please increase the number of tries with the option "--get-output-tries". If it\'s 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: Get ouput failed, target has blocked ADMIN$ access (maybe command executed!)') diff --git a/cme/protocols/smb/mmcexec.py b/cme/protocols/smb/mmcexec.py index 4ab99c76..3b320a59 100644 --- a/cme/protocols/smb/mmcexec.py +++ b/cme/protocols/smb/mmcexec.py @@ -252,7 +252,7 @@ class MMCEXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f'MMCEXEC: Get output file error, maybe got detected by AV software, please increase the number of tries with the option "--get-output-tries". If it\'s still failing maybe something is blocking the schedule job, try another exec method') + self.logger.fail(f'MMCEXEC: Couldn\'t retrieve output file, maybe got detected by AV. Please increase the number of tries with the option "--get-output-tries". If it\'s still failing, try the wmi protocol or another exec method') break if str(e).find("STATUS_BAD_NETWORK_NAME") >0 : self.logger.fail(f'MMCEXEC: Get ouput failed, target has blocked {self.__share} access (maybe command executed!)') diff --git a/cme/protocols/smb/proto_args.py b/cme/protocols/smb/proto_args.py index 9dac1c8b..eb083226 100644 --- a/cme/protocols/smb/proto_args.py +++ b/cme/protocols/smb/proto_args.py @@ -91,7 +91,7 @@ def proto_args(parser, std_parser, module_parser): help="force the PowerShell command to run in a 32-bit process") cgroup.add_argument("--no-output", action="store_true", help="do not retrieve command output") cegroup = cgroup.add_mutually_exclusive_group() - cegroup.add_argument("-x", metavar="COMMAND", dest="execute", help="execute the specified command") + cegroup.add_argument("-x", metavar="COMMAND", dest="execute", help="execute the specified CMD command") cegroup.add_argument("-X", metavar="PS_COMMAND", dest="ps_execute", help="execute the specified PowerShell command") psgroup = smb_parser.add_argument_group("Powershell Obfuscation", "Options for PowerShell script obfuscation") psgroup.add_argument("--obfs", action="store_true", help="Obfuscate PowerShell scripts") diff --git a/cme/protocols/smb/smbexec.py b/cme/protocols/smb/smbexec.py index 1f51acd2..a54275ed 100755 --- a/cme/protocols/smb/smbexec.py +++ b/cme/protocols/smb/smbexec.py @@ -170,7 +170,7 @@ class SMBEXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f'SMBEXEC: Get output file error, maybe got detected by AV software, please increase the number of tries with the option "--get-output-tries". If it\'s still failing maybe something is blocking the schedule job, try another exec method') + self.logger.fail(f'SMBEXEC: Couldn\'t retrieve output file, maybe got detected by AV. Please increase the number of tries with the option "--get-output-tries". If it\'s still failing, try the wmi protocol or another exec method') break if str(e).find("STATUS_BAD_NETWORK_NAME") >0 : self.logger.fail(f'SMBEXEC: Get ouput failed, target has blocked {self.__share} access (maybe command executed!)') diff --git a/cme/protocols/smb/wmiexec.py b/cme/protocols/smb/wmiexec.py index 419ed4fb..13693c90 100755 --- a/cme/protocols/smb/wmiexec.py +++ b/cme/protocols/smb/wmiexec.py @@ -166,7 +166,7 @@ class WMIEXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f'WMIEXEC: Get output file error, maybe got detected by AV software, please increase the number of tries with the option "--get-output-tries". If it\'s still failing maybe something is blocking the schedule job, try another exec method') + self.logger.fail(f'WMIEXEC: Couldn\'t retrieve output-file, maybe got detected by AV. Try the wmi protocol or another exec method') break if str(e).find("STATUS_BAD_NETWORK_NAME") >0 : self.logger.fail(f'SMB connection: target has blocked {self.__share} access (maybe command executed!)') From 4a739a0f323026bcc08e9390e11a1c1675fdffa0 Mon Sep 17 00:00:00 2001 From: Alex <61382599+NeffIsBack@users.noreply.github.com> Date: Sat, 9 Sep 2023 19:21:57 +0200 Subject: [PATCH 05/16] Change wmi --interval-time to --exec-timeout to more accuratly represent the effect --- cme/protocols/wmi.py | 6 +++--- cme/protocols/wmi/proto_args.py | 2 +- cme/protocols/wmi/wmiexec.py | 10 +++++----- cme/protocols/wmi/wmiexec_event.py | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cme/protocols/wmi.py b/cme/protocols/wmi.py index c5eb8fb9..a2ac6ae4 100644 --- a/cme/protocols/wmi.py +++ b/cme/protocols/wmi.py @@ -432,7 +432,7 @@ class wmi(connection): if not self.args.no_output: get_output = True - if "systeminfo" in command and self.args.interval_time < 10: + if "systeminfo" in command and self.args.exec_timeout < 10: self.logger.fail("Execute 'systeminfo' must set the interval time higher than 10 seconds") return False @@ -441,11 +441,11 @@ class wmi(connection): return False if self.args.exec_method == "wmiexec": - exec_method = wmiexec.WMIEXEC(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.aesKey, self.logger, self.args.interval_time, self.args.codec) + exec_method = wmiexec.WMIEXEC(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.aesKey, self.logger, self.args.exec_timeout, self.args.codec) output = exec_method.execute(command, get_output) elif self.args.exec_method == "wmiexec-event": - exec_method = wmiexec_event.WMIEXEC_EVENT(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.aesKey, self.logger, self.args.interval_time, self.args.codec) + exec_method = wmiexec_event.WMIEXEC_EVENT(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, self.doKerberos, self.kdcHost, self.aesKey, self.logger, self.args.exec_timeout, self.args.codec) output = exec_method.execute(command, get_output) self.conn.disconnect() diff --git a/cme/protocols/wmi/proto_args.py b/cme/protocols/wmi/proto_args.py index 6bb1605e..53d37f68 100644 --- a/cme/protocols/wmi/proto_args.py +++ b/cme/protocols/wmi/proto_args.py @@ -23,7 +23,7 @@ def proto_args(parser, std_parser, module_parser): "[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).") - cgroup.add_argument("--interval-time", default=5 ,metavar='INTERVAL_TIME', dest='interval_time', type=int, help='Set interval time(seconds) when executing command, unrecommend set it lower than 5') + cgroup.add_argument("--exec-timeout", default=5, 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') 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, " diff --git a/cme/protocols/wmi/wmiexec.py b/cme/protocols/wmi/wmiexec.py index 70b027af..59d167fe 100644 --- a/cme/protocols/wmi/wmiexec.py +++ b/cme/protocols/wmi/wmiexec.py @@ -34,7 +34,7 @@ from impacket.dcerpc.v5.dcomrt import DCOMConnection from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Login, WBEM_FLAG_FORWARD_ONLY, IWbemLevel1Login class WMIEXEC: - def __init__(self, host, username, password, domain, lmhash, nthash, doKerberos, kdcHost, aesKey, logger, interval_time, codec): + def __init__(self, host, username, password, domain, lmhash, nthash, doKerberos, kdcHost, aesKey, logger, exec_timeout, codec): self.__host = host self.__username = username self.__password = password @@ -45,7 +45,7 @@ class WMIEXEC: self.__kdcHost = kdcHost self.__aesKey = aesKey self.logger = logger - self.__interval_time = interval_time + self.__exec_timeout = exec_timeout self.__registry_Path = "" self.__outputBuffer = "" self.__retOutput = True @@ -91,8 +91,8 @@ class WMIEXEC: command = fr'''{self.__shell} {command} 1> {result_output} 2>&1 && certutil -encodehex -f {result_output} {result_output_b64} 0x40000001 && for /F "usebackq" %G in ("{result_output_b64}") do reg add HKLM\{self.__registry_Path} /v {keyName} /t REG_SZ /d "%G" /f && del /q /f /s {result_output} {result_output_b64}''' self.execute_remote(command) - self.logger.info("Waiting {}s for command completely executed.".format(self.__interval_time)) - time.sleep(self.__interval_time) + self.logger.info("Waiting {}s for command completely executed.".format(self.__exec_timeout)) + time.sleep(self.__exec_timeout) self.queryRegistry(keyName) @@ -104,7 +104,7 @@ class WMIEXEC: retVal = descriptor.GetStringValue(2147483650, self.__registry_Path, keyName) self.__outputBuffer = base64.b64decode(retVal.sValue).decode(self.__codec, errors='replace').rstrip('\r\n') except Exception as e: - self.logger.fail(f'WMIEXEC: Get output file error, maybe command not executed successfully or got detected by AV software, please increase the interval time of command execution with "--interval-time" option. If it\'s still failing maybe something is blocking the schedule job in vbscript, try another exec method') + self.logger.fail(f'WMIEXEC: Couldn\'t retrieve output-file. Either command timed out or got detected by AV. Try increasing the timeout with "--exec-timeout" option. If it\'s still failing, try the smb protocol or another exec method') try: self.logger.debug(f"Removing temporary registry path: HKLM\\{self.__registry_Path}") diff --git a/cme/protocols/wmi/wmiexec_event.py b/cme/protocols/wmi/wmiexec_event.py index e1ada22c..8e1c6dcd 100644 --- a/cme/protocols/wmi/wmiexec_event.py +++ b/cme/protocols/wmi/wmiexec_event.py @@ -37,7 +37,7 @@ from impacket.dcerpc.v5.dcom.wmi import WBEMSTATUS from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Login, WBEM_FLAG_FORWARD_ONLY, IWbemLevel1Login, WBEMSTATUS class WMIEXEC_EVENT: - def __init__(self, host, username, password, domain, lmhash, nthash, doKerberos, kdcHost, aesKey, logger, interval_time, codec): + def __init__(self, host, username, password, domain, lmhash, nthash, doKerberos, kdcHost, aesKey, logger, exec_timeout, codec): self.__host = host self.__username = username self.__password = password @@ -51,7 +51,7 @@ class WMIEXEC_EVENT: self.__retOutput = True self.logger = logger - self.__interval_time = interval_time + self.__exec_timeout = exec_timeout self.__codec = codec self.__instanceID = f"windows-object-{str(uuid.uuid4())}" self.__instanceID_StoreResult = f"windows-object-{str(uuid.uuid4())}" @@ -84,8 +84,8 @@ class WMIEXEC_EVENT: self.execute_remote(command) # Get command results - self.logger.info("Waiting {}s for command completely executed.".format(self.__interval_time)) - time.sleep(self.__interval_time) + self.logger.info("Waiting {}s for command completely executed.".format(self.__exec_timeout)) + time.sleep(self.__exec_timeout) if self.__retOutput: self.get_CommandResult() @@ -190,7 +190,7 @@ class WMIEXEC_EVENT: record = dict(command_ResultObject.getProperties()) self.__outputBuffer = base64.b64decode(record['ScriptText']['value']).decode(self.__codec, errors='replace') except Exception as e: - self.logger.fail(f'WMIEXEC-EVENT: Get output file error, maybe command not executed successfully or got detected by AV software, please increase the interval time of command execution with "--interval-time" option. If it\'s still failing maybe something is blocking the schedule job in vbscript, try another exec method') + self.logger.fail(f'WMIEXEC-EVENT: Couldn\'t retrieve output-file. Either command timed out or got detected by AV. Try increasing the timeout with "--exec-timeout" option. If it\'s still failing, try the smb protocol or another exec method') def remove_Instance(self): if self.__retOutput: From 2119d37e528051dbd6b2ef556f5dad0fb48ef328 Mon Sep 17 00:00:00 2001 From: Alex <61382599+NeffIsBack@users.noreply.github.com> Date: Sat, 9 Sep 2023 19:40:13 +0200 Subject: [PATCH 06/16] Typo --- cme/protocols/smb/wmiexec.py | 2 +- cme/protocols/wmi/wmiexec.py | 2 +- cme/protocols/wmi/wmiexec_event.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cme/protocols/smb/wmiexec.py b/cme/protocols/smb/wmiexec.py index 13693c90..21f6c313 100755 --- a/cme/protocols/smb/wmiexec.py +++ b/cme/protocols/smb/wmiexec.py @@ -166,7 +166,7 @@ class WMIEXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f'WMIEXEC: Couldn\'t retrieve output-file, maybe got detected by AV. Try the wmi protocol or another exec method') + self.logger.fail(f'WMIEXEC: Couldn\'t retrieve output file, maybe got detected by AV. Try the wmi protocol or another exec method') break if str(e).find("STATUS_BAD_NETWORK_NAME") >0 : self.logger.fail(f'SMB connection: target has blocked {self.__share} access (maybe command executed!)') diff --git a/cme/protocols/wmi/wmiexec.py b/cme/protocols/wmi/wmiexec.py index 59d167fe..38b79274 100644 --- a/cme/protocols/wmi/wmiexec.py +++ b/cme/protocols/wmi/wmiexec.py @@ -104,7 +104,7 @@ class WMIEXEC: retVal = descriptor.GetStringValue(2147483650, self.__registry_Path, keyName) self.__outputBuffer = base64.b64decode(retVal.sValue).decode(self.__codec, errors='replace').rstrip('\r\n') except Exception as e: - self.logger.fail(f'WMIEXEC: Couldn\'t retrieve output-file. Either command timed out or got detected by AV. Try increasing the timeout with "--exec-timeout" option. If it\'s still failing, try the smb protocol or another exec method') + self.logger.fail(f'WMIEXEC: Couldn\'t retrieve output file. Either command timed out or got detected by AV. Try increasing the timeout with "--exec-timeout" option. If it\'s still failing, try the smb protocol or another exec method') try: self.logger.debug(f"Removing temporary registry path: HKLM\\{self.__registry_Path}") diff --git a/cme/protocols/wmi/wmiexec_event.py b/cme/protocols/wmi/wmiexec_event.py index 8e1c6dcd..e5619ae5 100644 --- a/cme/protocols/wmi/wmiexec_event.py +++ b/cme/protocols/wmi/wmiexec_event.py @@ -190,7 +190,7 @@ class WMIEXEC_EVENT: record = dict(command_ResultObject.getProperties()) self.__outputBuffer = base64.b64decode(record['ScriptText']['value']).decode(self.__codec, errors='replace') except Exception as e: - self.logger.fail(f'WMIEXEC-EVENT: Couldn\'t retrieve output-file. Either command timed out or got detected by AV. Try increasing the timeout with "--exec-timeout" option. If it\'s still failing, try the smb protocol or another exec method') + self.logger.fail(f'WMIEXEC-EVENT: Couldn\'t retrieve output file. Either command timed out or got detected by AV. Try increasing the timeout with "--exec-timeout" option. If it\'s still failing, try the smb protocol or another exec method') def remove_Instance(self): if self.__retOutput: From 95ac793b2aa401d57644bdf53c539f95188e5566 Mon Sep 17 00:00:00 2001 From: Alex <61382599+NeffIsBack@users.noreply.github.com> Date: Sat, 9 Sep 2023 23:13:06 +0200 Subject: [PATCH 07/16] Replacing single quotes with double quotes in strings --- cme/protocols/smb/atexec.py | 4 ++-- cme/protocols/smb/mmcexec.py | 4 ++-- cme/protocols/smb/smbexec.py | 4 ++-- cme/protocols/smb/wmiexec.py | 4 ++-- cme/protocols/wmi/wmiexec.py | 2 +- cme/protocols/wmi/wmiexec_event.py | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cme/protocols/smb/atexec.py b/cme/protocols/smb/atexec.py index 049b84ed..0f539455 100755 --- a/cme/protocols/smb/atexec.py +++ b/cme/protocols/smb/atexec.py @@ -203,10 +203,10 @@ class TSCH_EXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f'ATEXEC: Couldn\'t retrieve output file, maybe got detected by AV. Please increase the number of tries with the option "--get-output-tries". If it\'s still failing, try the wmi protocol or another exec method') + self.logger.fail(f"ATEXEC: Couldn't retrieve output file, maybe got detected by AV. Please increase the number of tries with the option \"--get-output-tries\". If it's 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: Get ouput failed, target has blocked ADMIN$ access (maybe command executed!)') + self.logger.fail(f"ATEXEC: Get ouput failed, target has blocked ADMIN$ access (maybe command executed!)") break if str(e).find("SHARING") > 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0: sleep(3) diff --git a/cme/protocols/smb/mmcexec.py b/cme/protocols/smb/mmcexec.py index 3b320a59..0cc5637a 100644 --- a/cme/protocols/smb/mmcexec.py +++ b/cme/protocols/smb/mmcexec.py @@ -252,10 +252,10 @@ class MMCEXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f'MMCEXEC: Couldn\'t retrieve output file, maybe got detected by AV. Please increase the number of tries with the option "--get-output-tries". If it\'s still failing, try the wmi protocol or another exec method') + self.logger.fail(f"MMCEXEC: Couldn't retrieve output file, maybe got detected by AV. Please increase the number of tries with the option \"--get-output-tries\". If it's still failing, try the wmi protocol or another exec method") break if str(e).find("STATUS_BAD_NETWORK_NAME") >0 : - self.logger.fail(f'MMCEXEC: Get ouput failed, target has blocked {self.__share} access (maybe command executed!)') + self.logger.fail(f"MMCEXEC: Get ouput failed, target has blocked {self.__share} access (maybe command executed!)") break if str(e).find("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0: # Output not finished, let's wait diff --git a/cme/protocols/smb/smbexec.py b/cme/protocols/smb/smbexec.py index a54275ed..b58a4f84 100755 --- a/cme/protocols/smb/smbexec.py +++ b/cme/protocols/smb/smbexec.py @@ -170,10 +170,10 @@ class SMBEXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f'SMBEXEC: Couldn\'t retrieve output file, maybe got detected by AV. Please increase the number of tries with the option "--get-output-tries". If it\'s still failing, try the wmi protocol or another exec method') + self.logger.fail(f"SMBEXEC: Couldn't retrieve output file, maybe got detected by AV. Please increase the number of tries with the option \"--get-output-tries\". If it's still failing, try the wmi protocol or another exec method") break if str(e).find("STATUS_BAD_NETWORK_NAME") >0 : - self.logger.fail(f'SMBEXEC: Get ouput failed, target has blocked {self.__share} access (maybe command executed!)') + self.logger.fail(f"SMBEXEC: Get ouput failed, target has blocked {self.__share} access (maybe command executed!)") break if str(e).find("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0: # Output not finished, let's wait diff --git a/cme/protocols/smb/wmiexec.py b/cme/protocols/smb/wmiexec.py index 21f6c313..45e65495 100755 --- a/cme/protocols/smb/wmiexec.py +++ b/cme/protocols/smb/wmiexec.py @@ -166,10 +166,10 @@ class WMIEXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f'WMIEXEC: Couldn\'t retrieve output file, maybe got detected by AV. Try the wmi protocol or another exec method') + self.logger.fail(f"WMIEXEC: Couldn't retrieve output file, maybe got detected by AV. Try the wmi protocol or another exec method") break if str(e).find("STATUS_BAD_NETWORK_NAME") >0 : - self.logger.fail(f'SMB connection: target has blocked {self.__share} access (maybe command executed!)') + self.logger.fail(f"SMB connection: target has blocked {self.__share} access (maybe command executed!)") break if str(e).find("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0: sleep(2) diff --git a/cme/protocols/wmi/wmiexec.py b/cme/protocols/wmi/wmiexec.py index 38b79274..cf9cd0de 100644 --- a/cme/protocols/wmi/wmiexec.py +++ b/cme/protocols/wmi/wmiexec.py @@ -104,7 +104,7 @@ class WMIEXEC: retVal = descriptor.GetStringValue(2147483650, self.__registry_Path, keyName) self.__outputBuffer = base64.b64decode(retVal.sValue).decode(self.__codec, errors='replace').rstrip('\r\n') except Exception as e: - self.logger.fail(f'WMIEXEC: Couldn\'t retrieve output file. Either command timed out or got detected by AV. Try increasing the timeout with "--exec-timeout" option. If it\'s still failing, try the smb protocol or another exec method') + self.logger.fail(f"WMIEXEC: Couldn't retrieve output file. Either command timed out or got detected by AV. Try increasing the timeout with \"--exec-timeout\" option. If it's still failing, try the smb protocol or another exec method") try: self.logger.debug(f"Removing temporary registry path: HKLM\\{self.__registry_Path}") diff --git a/cme/protocols/wmi/wmiexec_event.py b/cme/protocols/wmi/wmiexec_event.py index e5619ae5..5a0421e6 100644 --- a/cme/protocols/wmi/wmiexec_event.py +++ b/cme/protocols/wmi/wmiexec_event.py @@ -190,7 +190,7 @@ class WMIEXEC_EVENT: record = dict(command_ResultObject.getProperties()) self.__outputBuffer = base64.b64decode(record['ScriptText']['value']).decode(self.__codec, errors='replace') except Exception as e: - self.logger.fail(f'WMIEXEC-EVENT: Couldn\'t retrieve output file. Either command timed out or got detected by AV. Try increasing the timeout with "--exec-timeout" option. If it\'s still failing, try the smb protocol or another exec method') + self.logger.fail(f"WMIEXEC-EVENT: Couldn't retrieve output file. Either command timed out or got detected by AV. Try increasing the timeout with \"--exec-timeout\" option. If it's still failing, try the smb protocol or another exec method") def remove_Instance(self): if self.__retOutput: From fe517be3e6e3b5d7d578a10f4bdfad96016c9781 Mon Sep 17 00:00:00 2001 From: Alex <61382599+NeffIsBack@users.noreply.github.com> Date: Sat, 9 Sep 2023 23:54:08 +0200 Subject: [PATCH 08/16] Fix spelling mistakes and formatting --- cme/modules/veeam_dump.py | 2 +- cme/protocols/smb/atexec.py | 4 ++-- cme/protocols/smb/mmcexec.py | 4 ++-- cme/protocols/smb/smbexec.py | 2 +- cme/protocols/wmi/wmiexec.py | 2 +- cme/protocols/wmi/wmiexec_event.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cme/modules/veeam_dump.py b/cme/modules/veeam_dump.py index e2092a79..c43a678b 100644 --- a/cme/modules/veeam_dump.py +++ b/cme/modules/veeam_dump.py @@ -144,7 +144,7 @@ class CMEModule: return connection.execute("powershell.exe -e {} -OutputFormat Text".format(psScipt_b64), True) def printCreds(self, context, output): - # Format ouput if returned in some XML Format + # Format output if returned in some XML Format if "CLIXML" in output: output = self.stripXmlOutput(context, output) diff --git a/cme/protocols/smb/atexec.py b/cme/protocols/smb/atexec.py index 0f539455..bd2e9139 100755 --- a/cme/protocols/smb/atexec.py +++ b/cme/protocols/smb/atexec.py @@ -203,10 +203,10 @@ class TSCH_EXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f"ATEXEC: Couldn't retrieve output file, maybe got detected by AV. Please increase the number of tries with the option \"--get-output-tries\". If it's still failing, try the wmi protocol or another exec method") + self.logger.fail(f"ATEXEC: Couldn't retrieve output file, maybe got detected by AV. Please increase the number of tries with the option '--get-output-tries'. If it's 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: Get ouput failed, target has blocked ADMIN$ access (maybe command executed!)") + self.logger.fail(f"ATEXEC: Get output failed, target has blocked ADMIN$ access (maybe command executed!)") break if str(e).find("SHARING") > 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0: sleep(3) diff --git a/cme/protocols/smb/mmcexec.py b/cme/protocols/smb/mmcexec.py index 0cc5637a..c6784564 100644 --- a/cme/protocols/smb/mmcexec.py +++ b/cme/protocols/smb/mmcexec.py @@ -252,10 +252,10 @@ class MMCEXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f"MMCEXEC: Couldn't retrieve output file, maybe got detected by AV. Please increase the number of tries with the option \"--get-output-tries\". If it's still failing, try the wmi protocol or another exec method") + self.logger.fail(f"MMCEXEC: Couldn't retrieve output file, maybe got detected by AV. Please increase the number of tries with the option '--get-output-tries'. If it's still failing, try the wmi protocol or another exec method") break if str(e).find("STATUS_BAD_NETWORK_NAME") >0 : - self.logger.fail(f"MMCEXEC: Get ouput failed, target has blocked {self.__share} access (maybe command executed!)") + self.logger.fail(f"MMCEXEC: Get output failed, target has blocked {self.__share} access (maybe command executed!)") break if str(e).find("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0: # Output not finished, let's wait diff --git a/cme/protocols/smb/smbexec.py b/cme/protocols/smb/smbexec.py index b58a4f84..0816f837 100755 --- a/cme/protocols/smb/smbexec.py +++ b/cme/protocols/smb/smbexec.py @@ -173,7 +173,7 @@ class SMBEXEC: self.logger.fail(f"SMBEXEC: Couldn't retrieve output file, maybe got detected by AV. Please increase the number of tries with the option \"--get-output-tries\". If it's still failing, try the wmi protocol or another exec method") break if str(e).find("STATUS_BAD_NETWORK_NAME") >0 : - self.logger.fail(f"SMBEXEC: Get ouput failed, target has blocked {self.__share} access (maybe command executed!)") + self.logger.fail(f"SMBEXEC: Get output failed, target has blocked {self.__share} access (maybe command executed!)") break if str(e).find("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0: # Output not finished, let's wait diff --git a/cme/protocols/wmi/wmiexec.py b/cme/protocols/wmi/wmiexec.py index cf9cd0de..db5d74e2 100644 --- a/cme/protocols/wmi/wmiexec.py +++ b/cme/protocols/wmi/wmiexec.py @@ -104,7 +104,7 @@ class WMIEXEC: retVal = descriptor.GetStringValue(2147483650, self.__registry_Path, keyName) self.__outputBuffer = base64.b64decode(retVal.sValue).decode(self.__codec, errors='replace').rstrip('\r\n') except Exception as e: - self.logger.fail(f"WMIEXEC: Couldn't retrieve output file. Either command timed out or got detected by AV. Try increasing the timeout with \"--exec-timeout\" option. If it's still failing, try the smb protocol or another exec method") + self.logger.fail(f"WMIEXEC: Couldn't retrieve output file. Either command timed out or got detected by AV. Try increasing the timeout with '--exec-timeout' option. If it's still failing, try the smb protocol or another exec method") try: self.logger.debug(f"Removing temporary registry path: HKLM\\{self.__registry_Path}") diff --git a/cme/protocols/wmi/wmiexec_event.py b/cme/protocols/wmi/wmiexec_event.py index 5a0421e6..5519c84e 100644 --- a/cme/protocols/wmi/wmiexec_event.py +++ b/cme/protocols/wmi/wmiexec_event.py @@ -190,7 +190,7 @@ class WMIEXEC_EVENT: record = dict(command_ResultObject.getProperties()) self.__outputBuffer = base64.b64decode(record['ScriptText']['value']).decode(self.__codec, errors='replace') except Exception as e: - self.logger.fail(f"WMIEXEC-EVENT: Couldn't retrieve output file. Either command timed out or got detected by AV. Try increasing the timeout with \"--exec-timeout\" option. If it's still failing, try the smb protocol or another exec method") + self.logger.fail(f"WMIEXEC-EVENT: Couldn't retrieve output file. Either command timed out or got detected by AV. Try increasing the timeout with '--exec-timeout' option. If it's still failing, try the smb protocol or another exec method") def remove_Instance(self): if self.__retOutput: From ba51e3036c048daab83190325a481fb36d688936 Mon Sep 17 00:00:00 2001 From: Alex <61382599+NeffIsBack@users.noreply.github.com> Date: Sun, 10 Sep 2023 12:18:55 +0200 Subject: [PATCH 09/16] Replace double quote with single quote --- cme/protocols/smb/smbexec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cme/protocols/smb/smbexec.py b/cme/protocols/smb/smbexec.py index 0816f837..260baf36 100755 --- a/cme/protocols/smb/smbexec.py +++ b/cme/protocols/smb/smbexec.py @@ -170,7 +170,7 @@ class SMBEXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f"SMBEXEC: Couldn't retrieve output file, maybe got detected by AV. Please increase the number of tries with the option \"--get-output-tries\". If it's still failing, try the wmi protocol or another exec method") + self.logger.fail(f"SMBEXEC: Couldn't retrieve output file, maybe got detected by AV. Please increase the number of tries with the option '--get-output-tries'. If it's still failing, try the wmi protocol or another exec method") break if str(e).find("STATUS_BAD_NETWORK_NAME") >0 : self.logger.fail(f"SMBEXEC: Get output failed, target has blocked {self.__share} access (maybe command executed!)") From 47199d6d79e8c7ea73be2075524171d7672c8a9b Mon Sep 17 00:00:00 2001 From: Alex <61382599+NeffIsBack@users.noreply.github.com> Date: Sun, 10 Sep 2023 12:33:05 +0200 Subject: [PATCH 10/16] Enable using any share with atexec --- cme/protocols/smb.py | 5 +++-- cme/protocols/smb/atexec.py | 24 +++++++++++++----------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/cme/protocols/smb.py b/cme/protocols/smb.py index a9cc2265..c1cfd162 100755 --- a/cme/protocols/smb.py +++ b/cme/protocols/smb.py @@ -734,8 +734,9 @@ class smb(connection): self.kdcHost, self.hash, self.logger, - self.args.get_output_tries - ) # self.args.share) + self.args.get_output_tries, + self.args.share + ) self.logger.info("Executed command via atexec") break except: diff --git a/cme/protocols/smb/atexec.py b/cme/protocols/smb/atexec.py index bd2e9139..cc783675 100755 --- a/cme/protocols/smb/atexec.py +++ b/cme/protocols/smb/atexec.py @@ -38,6 +38,8 @@ class TSCH_EXEC: self.__doKerberos = doKerberos self.__kdcHost = kdcHost self.__tries = tries + self.__output = None + self.__share = share self.logger = logger if hashes is not None: @@ -73,7 +75,7 @@ class TSCH_EXEC: def output_callback(self, data): self.__outputBuffer = data - def gen_xml(self, command, tmpFileName, fileless=False): + def gen_xml(self, command, fileless=False): xml = """ @@ -114,11 +116,12 @@ class TSCH_EXEC: cmd.exe """ if self.__retOutput: + self.__output = "\\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}\\{tmpFileName} 2>&1" + argument_xml = f" /C {command} > \\\\{local_ip}\\{self.__share_name}\\{self.__output} 2>&1" else: - argument_xml = f" /C {command} > %windir%\\Temp\\{tmpFileName} 2>&1" + argument_xml = f" /C {command} > {self.__output} 2>&1" elif self.__retOutput is False: argument_xml = f" /C {command}" @@ -143,9 +146,8 @@ class TSCH_EXEC: # dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY) tmpName = gen_random_string(8) - tmpFileName = tmpName + ".tmp" - xml = self.gen_xml(command, tmpFileName, fileless) + xml = self.gen_xml(command, fileless) self.logger.info(f"Task XML: {xml}") taskCreated = False @@ -187,7 +189,7 @@ class TSCH_EXEC: if fileless: while True: try: - with open(os.path.join("/tmp", "cme_hosted", tmpFileName), "r") as output: + with open(os.path.join("/tmp", "cme_hosted", self.__output), "r") as output: self.output_callback(output.read()) break except IOError: @@ -198,15 +200,15 @@ class TSCH_EXEC: tries = 1 while True: try: - self.logger.info(f"Attempting to read ADMIN$\\Temp\\{tmpFileName}") - smbConnection.getFile("ADMIN$", f"Temp\\{tmpFileName}", self.output_callback) + self.logger.info(f"Attempting to read {self.__share}\\{self.__output}") + smbConnection.getFile(self.__share, self.__output, self.output_callback) break except Exception as e: if tries >= self.__tries: self.logger.fail(f"ATEXEC: Couldn't retrieve output file, maybe got detected by AV. Please increase the number of tries with the option '--get-output-tries'. If it's 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: Get output failed, target has blocked ADMIN$ access (maybe command executed!)") + self.logger.fail(f"ATEXEC: Get output failed, target has blocked {self.__share} access (maybe command executed!)") break if str(e).find("SHARING") > 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0: sleep(3) @@ -215,7 +217,7 @@ class TSCH_EXEC: self.logger.debug(str(e)) if self.__outputBuffer: - self.logger.debug(f"Deleting file ADMIN$\\Temp\\{tmpFileName}") - smbConnection.deleteFile("ADMIN$", f"Temp\\{tmpFileName}") + self.logger.debug(f"Deleting file {self.__share}\\{self.__output}") + smbConnection.deleteFile(self.__share, self.__output) dce.disconnect() From 604099f1b0e2dc8dcbceafe654b253f59021e81f Mon Sep 17 00:00:00 2001 From: XiaoliChan <2209553467@qq.com> Date: Mon, 11 Sep 2023 15:46:38 +0800 Subject: [PATCH 11/16] [winrm] less ugly if condition Signed-off-by: XiaoliChan <2209553467@qq.com> --- cme/protocols/winrm.py | 66 ++++++++++-------------------------------- 1 file changed, 16 insertions(+), 50 deletions(-) diff --git a/cme/protocols/winrm.py b/cme/protocols/winrm.py index 1c460bc3..5fea95e3 100644 --- a/cme/protocols/winrm.py +++ b/cme/protocols/winrm.py @@ -231,31 +231,14 @@ class winrm(connection): self.password = password self.username = username self.domain = domain - if self.args.ssl and self.args.ignore_ssl_cert: - self.conn = Client( - self.host, - auth="ntlm", - username=f"{domain}\\{self.username}", - password=self.password, - ssl=True, - cert_validation=False, - ) - elif self.args.ssl: - self.conn = Client( - self.host, - auth="ntlm", - username=f"{domain}\\{self.username}", - password=self.password, - ssl=True, - ) - else: - self.conn = Client( - self.host, - auth="ntlm", - username=f"{domain}\\{self.username}", - password=self.password, - ssl=False, - ) + self.conn = Client( + self.host, + auth="ntlm", + username=f"{domain}\\{self.username}", + password=self.password, + ssl=True if self.args.ssl else False, + cert_validation=False if self.args.ignore_ssl_cert else True, + ) # TO DO: right now we're just running the hostname command to make the winrm library auth to the server # we could just authenticate without running a command :) (probably) @@ -308,31 +291,14 @@ class winrm(connection): nthash = self.hash self.domain = domain - if self.args.ssl and self.args.ignore_ssl_cert: - self.conn = Client( - self.host, - auth="ntlm", - username=f"{self.domain}\\{self.username}", - password=lmhash + nthash, - ssl=True, - cert_validation=False, - ) - elif self.args.ssl: - self.conn = Client( - self.host, - auth="ntlm", - username=f"{self.domain}\\{self.username}", - password=lmhash + nthash, - ssl=True, - ) - else: - self.conn = Client( - self.host, - auth="ntlm", - username=f"{self.domain}\\{self.username}", - password=lmhash + nthash, - ssl=False, - ) + self.conn = Client( + self.host, + auth="ntlm", + username=f"{self.domain}\\{self.username}", + password=lmhash + nthash, + ssl=True if self.args.ssl else False, + cert_validation=False if self.args.ignore_ssl_cert else True, + ) # TO DO: right now we're just running the hostname command to make the winrm library auth to the server # we could just authenticate without running a command :) (probably) From 0b4a6ffdc10224fdd0695f0e71819110b25c2f4f Mon Sep 17 00:00:00 2001 From: Alex <61382599+NeffIsBack@users.noreply.github.com> Date: Wed, 13 Sep 2023 00:24:04 +0200 Subject: [PATCH 12/16] Add CODEOWNERS file for review approval --- .github/CODEOWNERS | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..423f56d5 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,6 @@ +########################################## +# code ownership +########################################## + +# default ownership: +* @zblurx @Marshall-Hallenbeck @NeffIsBack \ No newline at end of file From 78f1dd41362fcf5b69d11dbc2dea797613821ded Mon Sep 17 00:00:00 2001 From: Alex <61382599+NeffIsBack@users.noreply.github.com> Date: Thu, 14 Sep 2023 00:18:59 +0200 Subject: [PATCH 13/16] Fix cli to only run building binaries manually and test on review submit --- .github/workflows/crackmapexec-test.yml | 7 ++----- .github/workflows/crackmapexec.yml | 7 +------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/.github/workflows/crackmapexec-test.yml b/.github/workflows/crackmapexec-test.yml index bc6fcb63..3769e312 100644 --- a/.github/workflows/crackmapexec-test.yml +++ b/.github/workflows/crackmapexec-test.yml @@ -1,11 +1,8 @@ name: CrackMapExec Tests on: - workflow_dispatch: - push: - branches: [ master ] - pull_request: - branches: [ master ] + pull_request_review: + types: [submitted] jobs: build: diff --git a/.github/workflows/crackmapexec.yml b/.github/workflows/crackmapexec.yml index e1ab04d1..c8067330 100644 --- a/.github/workflows/crackmapexec.yml +++ b/.github/workflows/crackmapexec.yml @@ -1,12 +1,7 @@ -name: CrackMapExec Tests & Build +name: CrackMapExec Build Binaries on: workflow_dispatch: - branches: [ main ] - push: - branches: [ main ] - pull_request: - branches: [ main ] jobs: build: From 473c4b1952a4f1dcf66d00946e34055c6c79e426 Mon Sep 17 00:00:00 2001 From: Alex <61382599+NeffIsBack@users.noreply.github.com> Date: Thu, 14 Sep 2023 00:29:55 +0200 Subject: [PATCH 14/16] Improve feedback text for the user --- cme/protocols/smb/atexec.py | 22 +++++++++++----------- cme/protocols/smb/mmcexec.py | 4 ++-- cme/protocols/smb/smbexec.py | 4 ++-- cme/protocols/smb/wmiexec.py | 2 +- cme/protocols/wmi/wmiexec.py | 2 +- cme/protocols/wmi/wmiexec_event.py | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/cme/protocols/smb/atexec.py b/cme/protocols/smb/atexec.py index cc783675..ae8273d2 100755 --- a/cme/protocols/smb/atexec.py +++ b/cme/protocols/smb/atexec.py @@ -38,7 +38,7 @@ class TSCH_EXEC: self.__doKerberos = doKerberos self.__kdcHost = kdcHost self.__tries = tries - self.__output = None + self.__output_filename = None self.__share = share self.logger = logger @@ -116,12 +116,12 @@ class TSCH_EXEC: cmd.exe """ if self.__retOutput: - self.__output = "\\Windows\\Temp\\" + gen_random_string(6) + 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} 2>&1" + argument_xml = f" /C {command} > \\\\{local_ip}\\{self.__share_name}\\{self.__output_filename} 2>&1" else: - argument_xml = f" /C {command} > {self.__output} 2>&1" + argument_xml = f" /C {command} > {self.__output_filename} 2>&1" elif self.__retOutput is False: argument_xml = f" /C {command}" @@ -189,7 +189,7 @@ class TSCH_EXEC: if fileless: while True: try: - with open(os.path.join("/tmp", "cme_hosted", self.__output), "r") as output: + with open(os.path.join("/tmp", "cme_hosted", self.__output_filename), "r") as output: self.output_callback(output.read()) break except IOError: @@ -200,15 +200,15 @@ class TSCH_EXEC: tries = 1 while True: try: - self.logger.info(f"Attempting to read {self.__share}\\{self.__output}") - smbConnection.getFile(self.__share, self.__output, self.output_callback) + 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: Couldn't retrieve output file, maybe got detected by AV. Please increase the number of tries with the option '--get-output-tries'. If it's still failing, try the wmi protocol or another exec method") + 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: Get output failed, target has blocked {self.__share} access (maybe command executed!)") + 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) @@ -217,7 +217,7 @@ class TSCH_EXEC: self.logger.debug(str(e)) if self.__outputBuffer: - self.logger.debug(f"Deleting file {self.__share}\\{self.__output}") - smbConnection.deleteFile(self.__share, self.__output) + self.logger.debug(f"Deleting file {self.__share}\\{self.__output_filename}") + smbConnection.deleteFile(self.__share, self.__output_filename) dce.disconnect() diff --git a/cme/protocols/smb/mmcexec.py b/cme/protocols/smb/mmcexec.py index c6784564..44dd802f 100644 --- a/cme/protocols/smb/mmcexec.py +++ b/cme/protocols/smb/mmcexec.py @@ -252,10 +252,10 @@ class MMCEXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f"MMCEXEC: Couldn't retrieve output file, maybe got detected by AV. Please increase the number of tries with the option '--get-output-tries'. If it's still failing, try the wmi protocol or another exec method") + self.logger.fail(f"MMCEXEC: 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"MMCEXEC: Get output failed, target has blocked {self.__share} access (maybe command executed!)") + self.logger.fail(f"MMCEXEC: 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("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0: # Output not finished, let's wait diff --git a/cme/protocols/smb/smbexec.py b/cme/protocols/smb/smbexec.py index 260baf36..73c8a765 100755 --- a/cme/protocols/smb/smbexec.py +++ b/cme/protocols/smb/smbexec.py @@ -170,10 +170,10 @@ class SMBEXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f"SMBEXEC: Couldn't retrieve output file, maybe got detected by AV. Please increase the number of tries with the option '--get-output-tries'. If it's still failing, try the wmi protocol or another exec method") + self.logger.fail(f"SMBEXEC: 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"SMBEXEC: Get output failed, target has blocked {self.__share} access (maybe command executed!)") + self.logger.fail(f"SMBEXEC: 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("STATUS_SHARING_VIOLATION") >= 0 or str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0: # Output not finished, let's wait diff --git a/cme/protocols/smb/wmiexec.py b/cme/protocols/smb/wmiexec.py index 45e65495..6046df34 100755 --- a/cme/protocols/smb/wmiexec.py +++ b/cme/protocols/smb/wmiexec.py @@ -166,7 +166,7 @@ class WMIEXEC: break except Exception as e: if tries >= self.__tries: - self.logger.fail(f"WMIEXEC: Couldn't retrieve output file, maybe got detected by AV. Try the wmi protocol or another exec method") + self.logger.fail(f"WMIEXEC: Could not retrieve output file, it may have been detected by AV. 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"SMB connection: target has blocked {self.__share} access (maybe command executed!)") diff --git a/cme/protocols/wmi/wmiexec.py b/cme/protocols/wmi/wmiexec.py index db5d74e2..e32594dc 100644 --- a/cme/protocols/wmi/wmiexec.py +++ b/cme/protocols/wmi/wmiexec.py @@ -104,7 +104,7 @@ class WMIEXEC: retVal = descriptor.GetStringValue(2147483650, self.__registry_Path, keyName) self.__outputBuffer = base64.b64decode(retVal.sValue).decode(self.__codec, errors='replace').rstrip('\r\n') except Exception as e: - self.logger.fail(f"WMIEXEC: Couldn't retrieve output file. Either command timed out or got detected by AV. Try increasing the timeout with '--exec-timeout' option. If it's still failing, try the smb protocol or another exec method") + self.logger.fail(f"WMIEXEC: Could not retrieve output file, it may have been detected by AV. Please try increasing the timeout with the '--exec-timeout' option. If it is still failing, try the 'smb' protocol or another exec method") try: self.logger.debug(f"Removing temporary registry path: HKLM\\{self.__registry_Path}") diff --git a/cme/protocols/wmi/wmiexec_event.py b/cme/protocols/wmi/wmiexec_event.py index 5519c84e..3a7f1e3f 100644 --- a/cme/protocols/wmi/wmiexec_event.py +++ b/cme/protocols/wmi/wmiexec_event.py @@ -190,7 +190,7 @@ class WMIEXEC_EVENT: record = dict(command_ResultObject.getProperties()) self.__outputBuffer = base64.b64decode(record['ScriptText']['value']).decode(self.__codec, errors='replace') except Exception as e: - self.logger.fail(f"WMIEXEC-EVENT: Couldn't retrieve output file. Either command timed out or got detected by AV. Try increasing the timeout with '--exec-timeout' option. If it's still failing, try the smb protocol or another exec method") + self.logger.fail(f"WMIEXEC-EVENT: Could not retrieve output file, it may have been detected by AV. Please try increasing the timeout with the '--exec-timeout' option. If it is still failing, try the 'smb' protocol or another exec method") def remove_Instance(self): if self.__retOutput: From 20459ef3fda9d1ecc0b394ea70c281012ec055af Mon Sep 17 00:00:00 2001 From: XiaoliChan <2209553467@qq.com> Date: Thu, 14 Sep 2023 11:14:30 +0800 Subject: [PATCH 15/16] [wmi] Revert new check_admin function Signed-off-by: XiaoliChan <2209553467@qq.com> --- cme/connection.py | 6 +----- cme/protocols/wmi.py | 41 ++++++++++++++++++++++++----------------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/cme/connection.py b/cme/connection.py index 5c86b196..09aa0ef3 100755 --- a/cme/connection.py +++ b/cme/connection.py @@ -42,7 +42,7 @@ def requires_admin(func): return wraps(func)(_decorator) -def dcom_FirewallChecker(iInterface, timeout=None): +def dcom_FirewallChecker(iInterface, timeout): stringBindings = iInterface.get_cinstance().get_string_bindings() for strBinding in stringBindings: if strBinding['wTowerId'] == 7: @@ -60,10 +60,6 @@ def dcom_FirewallChecker(iInterface, timeout=None): stringBinding = 'ncacn_ip_tcp:%s%s' % (iInterface.get_target(), bindingPort) if "stringBinding" not in locals(): return True, None - - # if not timeout, which means not doing firewall check. - if not timeout: - return True, stringBinding try: rpctransport = transport.DCERPCTransportFactory(stringBinding) rpctransport.set_connect_timeout(timeout) diff --git a/cme/protocols/wmi.py b/cme/protocols/wmi.py index 72e2a5a6..dc38ecac 100644 --- a/cme/protocols/wmi.py +++ b/cme/protocols/wmi.py @@ -164,29 +164,36 @@ class wmi(connection): def check_if_admin(self): try: dcom = DCOMConnection(self.conn.getRemoteName(), self.username, self.password, self.domain, self.lmhash, self.nthash, oxidResolver=True, doKerberos=self.doKerberos ,kdcHost=self.kdcHost, aesKey=self.aesKey) - dcom.set_connect_timeout(self.args.rpc_timeout) iInterface = dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login, IID_IWbemLevel1Login) - _, self.stringBinding = dcom_FirewallChecker(iInterface) - iWbemLevel1Login = IWbemLevel1Login(iInterface) - iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL) + flag, self.stringBinding = dcom_FirewallChecker(iInterface, self.args.rpc_timeout) except Exception as e: if "dcom" in locals(): dcom.disconnect() - error_msg = str(e).lower() - - if error_msg.find("access_denied") > 0: - pass - else: - if error_msg.find("timed out") > 0 or error_msg.lower().find("connection refused") > 0: - error_msg = f'Check admin error: dcom initialization failed with stringbinding: "{self.stringBinding}", please try "--rpc-timeout" option. (probably is admin)' - elif not self.stringBinding: + if not str(e).lower().find("access_denied") >=0: + self.logger.fail(str(e)) + else: + if not flag or not self.stringBinding: + dcom.disconnect() + error_msg = f'Check admin error: dcom initialization failed with stringbinding: "{self.stringBinding}", please try "--rpc-timeout" option. (probably is admin)' + + if not self.stringBinding: error_msg = "Check admin error: dcom initialization failed: can't get target stringbinding, maybe cause by IPv6 or any other issues, please check your target again" - self.logger.fail(error_msg) - else: - dcom.disconnect() - self.logger.extra['protocol'] = "WMI" - self.admin_privs = True + + self.logger.fail(error_msg) if not flag else self.logger.debug(error_msg) + else: + try: + iWbemLevel1Login = IWbemLevel1Login(iInterface) + iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL) + except Exception as e: + dcom.disconnect() + + if not str(e).lower().find("access_denied") >=0: + self.logger.fail(str(e)) + else: + dcom.disconnect() + self.logger.extra['protocol'] = "WMI" + self.admin_privs = True return def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False): From 0eccd0e60d24bba86cb24371d0baaef241776606 Mon Sep 17 00:00:00 2001 From: XiaoliChan <2209553467@qq.com> Date: Thu, 14 Sep 2023 16:32:31 +0800 Subject: [PATCH 16/16] [wmi] Not 'find' anymore Signed-off-by: XiaoliChan <2209553467@qq.com> --- cme/protocols/wmi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cme/protocols/wmi.py b/cme/protocols/wmi.py index dc38ecac..da1b56f4 100644 --- a/cme/protocols/wmi.py +++ b/cme/protocols/wmi.py @@ -170,7 +170,7 @@ class wmi(connection): if "dcom" in locals(): dcom.disconnect() - if not str(e).lower().find("access_denied") >=0: + if "access_denied" not in str(e).lower(): self.logger.fail(str(e)) else: if not flag or not self.stringBinding: @@ -188,7 +188,7 @@ class wmi(connection): except Exception as e: dcom.disconnect() - if not str(e).lower().find("access_denied") >=0: + if "access_denied" not in str(e).lower(): self.logger.fail(str(e)) else: dcom.disconnect()