diff --git a/cme/connection.py b/cme/connection.py index 16e9232a..91e0d5ac 100755 --- a/cme/connection.py +++ b/cme/connection.py @@ -15,6 +15,8 @@ from cme.helpers.logger import highlight from cme.logger import cme_logger, CMEAdapter from cme.context import Context +from impacket.dcerpc.v5 import transport + sem = BoundedSemaphore(1) global_failed_logins = 0 user_failed_logins = {} @@ -31,7 +33,6 @@ def gethost_addrinfo(hostname): return sa[0] return canonname - def requires_admin(func): def _decorator(self, *args, **kwargs): if self.admin_privs is False: @@ -40,6 +41,31 @@ def requires_admin(func): return wraps(func)(_decorator) +def dcom_FirewallChecker(iInterface, timeout): + stringBindings = iInterface.get_cinstance().get_string_bindings() + for strBinding in stringBindings: + if strBinding['wTowerId'] == 7: + if strBinding['aNetworkAddr'].find('[') >= 0: + binding, _, bindingPort = strBinding['aNetworkAddr'].partition('[') + bindingPort = '[' + bindingPort + else: + binding = strBinding['aNetworkAddr'] + bindingPort = '' + + if binding.upper().find(iInterface.get_target().upper()) >= 0: + stringBinding = 'ncacn_ip_tcp:' + strBinding['aNetworkAddr'][:-1] + break + elif iInterface.is_fqdn() and binding.upper().find(iInterface.get_target().upper().partition('.')[0]) >= 0: + stringBinding = 'ncacn_ip_tcp:%s%s' % (iInterface.get_target(), bindingPort) + try: + rpctransport = transport.DCERPCTransportFactory(stringBinding) + rpctransport.set_connect_timeout(timeout) + rpctransport.connect() + rpctransport.disconnect() + except: + return False, stringBinding + else: + return True, stringBinding class connection(object): def __init__(self, args, db, host): diff --git a/cme/modules/enum_av.py b/cme/modules/enum_av.py index f0947276..c665cd4d 100644 --- a/cme/modules/enum_av.py +++ b/cme/modules/enum_av.py @@ -518,8 +518,36 @@ conf = { { "name": "exploitProtectionIPC", "processes": ["AVKWCtlx64.exe"], - } + }, + ], + }, + { + "name": "Panda Adaptive Defense 360", + "services": [ + { + "name": "PandaAetherAgent", + "description": "Panda Endpoint Agent", + }, + { + "name": "PSUAService", + "description": "Panda Product Service" + }, + { + "name": "NanoServiceMain", + "description": "Panda Cloud Antivirus Service", + }, + ], + "pipes": [ + { + "name": "NNS_API_IPC_SRV_ENDPOINT", + "processes": ["PSANHost.exe"], + }, + { + "name": "PSANMSrvcPpal", + "processes": ["PSUAService.exe"], + }, ], } + ] } diff --git a/cme/modules/get_netconnections.py b/cme/modules/get_netconnections.py index df8c636e..4eeb7a78 100755 --- a/cme/modules/get_netconnections.py +++ b/cme/modules/get_netconnections.py @@ -28,11 +28,12 @@ class CMEModule: def on_admin_login(self, context, connection): data = [] cards = connection.wmi(f"select DNSDomainSuffixSearchOrder, IPAddress from win32_networkadapterconfiguration") - for c in cards: - if c["IPAddress"].get("value"): - context.log.success(f"IP Address: {c['IPAddress']['value']}\tSearch Domain: {c['DNSDomainSuffixSearchOrder']['value']}") + if cards: + for c in cards: + if c["IPAddress"].get("value"): + context.log.success(f"IP Address: {c['IPAddress']['value']}\tSearch Domain: {c['DNSDomainSuffixSearchOrder']['value']}") - data.append(cards) + data.append(cards) log_name = "network-connections-{}-{}.log".format(connection.args.target[0], datetime.now().strftime("%Y-%m-%d_%H%M%S")) write_log(json.dumps(data), log_name) diff --git a/cme/modules/rdp.py b/cme/modules/rdp.py index 91f12183..ee9d69af 100644 --- a/cme/modules/rdp.py +++ b/cme/modules/rdp.py @@ -3,14 +3,21 @@ from sys import exit +from cme.connection import dcom_FirewallChecker + from impacket.dcerpc.v5 import rrp from impacket.examples.secretsdump import RemoteOperations +from impacket.dcerpc.v5.dcomrt import DCOMConnection +from impacket.dcerpc.v5.dcom import wmi +from impacket.dcerpc.v5.dtypes import NULL +from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY class CMEModule: name = "rdp" description = "Enables/Disables RDP" - supported_protocols = ["smb"] + #supported_protocols = ["smb"] + supported_protocols = ["smb" ,"wmi"] opsec_safe = True multiple_hosts = True @@ -21,26 +28,99 @@ class CMEModule: def options(self, context, module_options): """ - ACTION Enable/Disable RDP (choices: enable, disable) + ACTION Enable/Disable RDP (choices: enable, disable, enable-ram, disable-ram) + METHOD wmi(ncacn_ip_tcp)/smb(ncacn_np) (choices: wmi, smb, default is wmi) + OLD For old version system (under NT6, like: server 2003) + DCOM-TIMEOUT Set the Dcom connection timeout for WMI method (Default is 10 seconds) + cme smb 192.168.1.1 -u {user} -p {password} -M rdp -o ACTION={enable, disable, enable-ram, disable-ram} {OLD=true} {DCOM-TIMEOUT=5} + cme smb 192.168.1.1 -u {user} -p {password} -M rdp -o METHOD=smb ACTION={enable, disable, enable-ram, disable-ram} + cme smb 192.168.1.1 -u {user} -p {password} -M rdp -o METHOD=wmi ACTION={enable, disable, enable-ram, disable-ram} {OLD=true} {DCOM-TIMEOUT=5} """ if not "ACTION" in module_options: context.log.fail("ACTION option not specified!") exit(1) - if module_options["ACTION"].lower() not in ["enable", "disable"]: + if module_options["ACTION"].lower() not in ["enable", "disable", "enable-ram", "disable-ram"]: context.log.fail("Invalid value for ACTION option!") exit(1) self.action = module_options["ACTION"].lower() + + if not "METHOD" in module_options: + self.method = "wmi" + else: + self.method = module_options['METHOD'].lower() + + if context.protocol != "smb" and self.method == "smb": + context.log.fail(f"Protocol: {context.protocol} not support this method") + exit(1) + + if not "DCOM-TIMEOUT" in module_options: + self.dcom_timeout = 10 + else: + try: + self.dcom_timeout = int(module_options['DCOM-TIMEOUT']) + except: + context.log.fail("Wrong DCOM timeout value!") + exit(1) + + if not "OLD" in module_options: + self.oldSystem = False + else: + self.oldSystem = True def on_admin_login(self, context, connection): - if self.action == "enable": - self.rdp_enable(context, connection.conn) - elif self.action == "disable": - self.rdp_disable(context, connection.conn) + # Preparation for wmi protocol + if self.method == "smb": + context.log.info("Executing over SMB(ncacn_np)") + try: + smb_rdp = rdp_SMB(context, connection) + if "ram" in self.action: + smb_rdp.rdp_RAMWrapper(self.action) + else: + smb_rdp.rdp_Wrapper(self.action) + except Exception as e: + context.log.fail(f"Enable RDP via smb error: {str(e)}") + elif self.method == "wmi": + context.log.info("Executing over WMI(ncacn_ip_tcp)") + try: + wmi_rdp = rdp_WMI(context, connection, self.dcom_timeout) + except Exception as e: + context.log.fail(f"Unexpected wmi error: {str(e)}") + wmi_rdp._rdp_WMI__dcom.disconnect() - def rdp_enable(self, context, smbconnection): - remoteOps = RemoteOperations(smbconnection, False) + if hasattr(wmi_rdp, '_rdp_WMI__iWbemLevel1Login'): + if "ram" in self.action: + # Nt version under 6 not support RAM. + try: + wmi_rdp.rdp_RAMWrapper(self.action) + except Exception as e: + if "WBEM_E_NOT_FOUND" in str(e): + context.log.fail("System version under NT6 not support restricted admin mode") + else: + context.log.fail(str(e)) + pass + wmi_rdp._rdp_WMI__dcom.disconnect() + else: + try: + wmi_rdp.rdp_Wrapper(self.action, self.oldSystem) + except Exception as e: + if "WBEM_E_INVALID_NAMESPACE" in str(e): + context.log.fail('Looks like target system version is under NT6, please add "OLD=true" in module options.') + else: + context.log.fail(str(e)) + pass + wmi_rdp._rdp_WMI__dcom.disconnect() + +class rdp_SMB: + def __init__(self, context, connection): + self.context = context + self.__smbconnection = connection.conn + self.__execute = connection.execute + self.logger = context.log + + def rdp_Wrapper(self, action): + remoteOps = RemoteOperations(self.__smbconnection, False) remoteOps.enableRegistry() if remoteOps._RemoteOperations__rrp: @@ -54,26 +134,32 @@ class CMEModule: ) keyHandle = ans["phkResult"] - rrp.hBaseRegSetValue( + ans = rrp.hBaseRegSetValue( remoteOps._RemoteOperations__rrp, keyHandle, - "fDenyTSConnections\x00", + "fDenyTSConnections", rrp.REG_DWORD, - 0, + 0 if action == "enable" else 1, ) - rtype, data = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, "fDenyTSConnections\x00") + rtype, data = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, "fDenyTSConnections") if int(data) == 0: - context.log.success("RDP enabled successfully") + self.logger.success("Enable RDP via SMB(ncacn_np) successfully") + elif int(data) == 1: + self.logger.success("Disable RDP via SMB(ncacn_np) successfully") + + self.firewall_CMD(action) + if action == "enable": + self.query_RDPPort(remoteOps, regHandle) try: remoteOps.finish() except: pass - def rdp_disable(self, context, smbconnection): - remoteOps = RemoteOperations(smbconnection, False) + def rdp_RAMWrapper(self, action): + remoteOps = RemoteOperations(self.__smbconnection, False) remoteOps.enableRegistry() if remoteOps._RemoteOperations__rrp: @@ -83,24 +169,170 @@ class CMEModule: ans = rrp.hBaseRegOpenKey( remoteOps._RemoteOperations__rrp, regHandle, - "SYSTEM\\CurrentControlSet\\Control\\Terminal Server", + "System\\CurrentControlSet\\Control\\Lsa", ) keyHandle = ans["phkResult"] rrp.hBaseRegSetValue( remoteOps._RemoteOperations__rrp, keyHandle, - "fDenyTSConnections\x00", + "DisableRestrictedAdmin", rrp.REG_DWORD, - 1, + 0 if action == "enable-ram" else 1, ) - rtype, data = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, "fDenyTSConnections\x00") + rtype, data = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, "DisableRestrictedAdmin") - if int(data) == 1: - context.log.success("RDP disabled successfully") + if int(data) == 0: + self.logger.success("Enable RDP Restricted Admin Mode via SMB(ncacn_np) succeed") + elif int(data) == 1: + self.logger.success("Disable RDP Restricted Admin Mode via SMB(ncacn_np) succeed") try: remoteOps.finish() except: pass + + def query_RDPPort(self, remoteOps, regHandle): + if remoteOps: + ans = rrp.hBaseRegOpenKey( + remoteOps._RemoteOperations__rrp, + regHandle, + "SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp", + ) + keyHandle = ans["phkResult"] + + rtype, data = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, "PortNumber") + + self.logger.success(f"RDP Port: {str(data)}") + + # https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/manage/enable_rdp.rb + def firewall_CMD(self, action): + cmd = f"netsh firewall set service type = remotedesktop mode = {action}" + self.logger.info("Configure firewall via execute command.") + output = self.__execute(cmd, True) + if output: + self.logger.success(f"{action.capitalize()} RDP firewall rules via cmd succeed") + else: + self.logger.fail(f"{action.capitalize()} RDP firewall rules via cmd failed, maybe got detected by AV software.") + +class rdp_WMI: + def __init__(self, context, connection, timeout): + self.logger = context.log + self.__currentprotocol = context.protocol + # From dfscoerce.py + self.__username=connection.username + self.__password=connection.password + self.__domain=connection.domain + self.__lmhash=connection.lmhash + self.__nthash=connection.nthash + self.__target=connection.host if not connection.kerberos else connection.hostname + "." + connection.domain + self.__doKerberos=connection.kerberos + self.__kdcHost=connection.kdcHost + self.__aesKey=connection.aesKey + self.__timeout = timeout + + self.__dcom = DCOMConnection( + self.__target, + self.__username, + self.__password, + self.__domain, + self.__lmhash, + self.__nthash, + self.__aesKey, + oxidResolver=True, + doKerberos=self.__doKerberos, + kdcHost=self.__kdcHost, + ) + + iInterface = self.__dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login) + if self.__currentprotocol == "smb": + flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__timeout) + if not flag: + self.logger.fail(f'WMIEXEC: Dcom initialization failed on connection with stringbinding: "{self.__stringBinding}", please increase the timeout with the module option "DCOM-TIMEOUT=10". If it\'s still failing maybe something is blocking the RPC connection, try "METHOD=smb"') + # Make it force break function + self.__dcom.disconnect() + return + self.__iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface) + + def rdp_Wrapper(self, action, old=False): + if old == False: + # According to this document: https://learn.microsoft.com/en-us/windows/win32/termserv/win32-tslogonsetting + # Authentication level must set to RPC_C_AUTHN_LEVEL_PKT_PRIVACY when accessing namespace "//./root/cimv2/TerminalServices" + iWbemServices = self.__iWbemLevel1Login.NTLMLogin('//./root/cimv2/TerminalServices', NULL, NULL) + iWbemServices.get_dce_rpc().set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY) + self.__iWbemLevel1Login.RemRelease() + iEnumWbemClassObject = iWbemServices.ExecQuery("SELECT * FROM Win32_TerminalServiceSetting") + iWbemClassObject = iEnumWbemClassObject.Next(0xffffffff,1)[0] + if action == 'enable': + self.logger.info("Enabled RDP services and setting up firewall.") + iWbemClassObject.SetAllowTSConnections(1,1) + elif action == 'disable': + self.logger.info("Disabled RDP services and setting up firewall.") + iWbemClassObject.SetAllowTSConnections(0,0) + else: + iWbemServices = self.__iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL) + self.__iWbemLevel1Login.RemRelease() + iEnumWbemClassObject = iWbemServices.ExecQuery("SELECT * FROM Win32_TerminalServiceSetting") + iWbemClassObject = iEnumWbemClassObject.Next(0xffffffff,1)[0] + if action == 'enable': + self.logger.info("Enabling RDP services (old system not support setting up firewall)") + iWbemClassObject.SetAllowTSConnections(1) + elif action == 'disable': + self.logger.info("Disabling RDP services (old system not support setting up firewall)") + iWbemClassObject.SetAllowTSConnections(0) + + self.query_RDPResult(old) + + if action == 'enable': + self.query_RDPPort() + # Need to create new iWbemServices interface in order to flush results + + def query_RDPResult(self, old=False): + if old == False: + iWbemServices = self.__iWbemLevel1Login.NTLMLogin('//./root/cimv2/TerminalServices', NULL, NULL) + iWbemServices.get_dce_rpc().set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY) + self.__iWbemLevel1Login.RemRelease() + iEnumWbemClassObject = iWbemServices.ExecQuery("SELECT * FROM Win32_TerminalServiceSetting") + iWbemClassObject = iEnumWbemClassObject.Next(0xffffffff,1)[0] + result = dict(iWbemClassObject.getProperties()) + result = result['AllowTSConnections']['value'] + if result == 0: + self.logger.success("Disable RDP via WMI(ncacn_ip_tcp) successfully") + else: + self.logger.success("Enable RDP via WMI(ncacn_ip_tcp) successfully") + else: + iWbemServices = self.__iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL) + self.__iWbemLevel1Login.RemRelease() + iEnumWbemClassObject = iWbemServices.ExecQuery("SELECT * FROM Win32_TerminalServiceSetting") + iWbemClassObject = iEnumWbemClassObject.Next(0xffffffff,1)[0] + result = dict(iWbemClassObject.getProperties()) + result = result['AllowTSConnections']['value'] + if result == 0: + self.logger.success("Disable RDP via WMI(ncacn_ip_tcp) successfully (old system)") + else: + self.logger.success("Enable RDP via WMI(ncacn_ip_tcp) successfully (old system)") + + def query_RDPPort(self): + iWbemServices = self.__iWbemLevel1Login.NTLMLogin('//./root/DEFAULT', NULL, NULL) + self.__iWbemLevel1Login.RemRelease() + StdRegProv, resp = iWbemServices.GetObject("StdRegProv") + out = StdRegProv.GetDWORDValue(2147483650, 'SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp', 'PortNumber') + self.logger.success(f"RDP Port: {str(out.uValue)}") + + # Nt version under 6 not support RAM. + def rdp_RAMWrapper(self, action): + iWbemServices = self.__iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL) + self.__iWbemLevel1Login.RemRelease() + StdRegProv, resp = iWbemServices.GetObject("StdRegProv") + if action == 'enable-ram': + self.logger.info("Enabling Restricted Admin Mode.") + StdRegProv.SetDWORDValue(2147483650, 'System\\CurrentControlSet\\Control\\Lsa', 'DisableRestrictedAdmin', 0) + elif action == 'disable-ram': + self.logger.info("Disabling Restricted Admin Mode (Clear).") + StdRegProv.DeleteValue(2147483650, 'System\\CurrentControlSet\\Control\\Lsa', 'DisableRestrictedAdmin') + out = StdRegProv.GetDWORDValue(2147483650, 'System\\CurrentControlSet\\Control\\Lsa', 'DisableRestrictedAdmin') + if out.uValue == 0: + self.logger.success("Enable RDP Restricted Admin Mode via WMI(ncacn_ip_tcp) successfully") + elif out.uValue == None: + self.logger.success("Disable RDP Restricted Admin Mode via WMI(ncacn_ip_tcp) successfully") \ No newline at end of file diff --git a/cme/modules/wcc.py b/cme/modules/wcc.py index 30850fbc..690d806e 100644 --- a/cme/modules/wcc.py +++ b/cme/modules/wcc.py @@ -503,7 +503,7 @@ class HostChecker: def check_last_successful_update(self): records = self.connection.wmi(wmi_query='Select TimeGenerated FROM Win32_ReliabilityRecords Where EventIdentifier=19', namespace='root\\cimv2') - if len(records) == 0: + if isinstance(records, bool) or len(records) == 0: return False, ['No update found'] most_recent_update_date = records[0]['TimeGenerated']['value'] most_recent_update_date = most_recent_update_date.split('.')[0] diff --git a/cme/protocols/smb.py b/cme/protocols/smb.py index e405814b..f2743bb9 100755 --- a/cme/protocols/smb.py +++ b/cme/protocols/smb.py @@ -21,11 +21,13 @@ from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket.dcerpc.v5.transport import DCERPCTransportFactory, SMBTransport from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE from impacket.dcerpc.v5.epm import MSRPC_UUID_PORTMAP -from impacket.dcerpc.v5.dcom.wmi import WBEM_FLAG_FORWARD_ONLY from impacket.dcerpc.v5.samr import SID_NAME_USE from impacket.dcerpc.v5.dtypes import MAXIMUM_ALLOWED from impacket.krb5.kerberosv5 import SessionKeyDecryptionError from impacket.krb5.types import KerberosException +from impacket.dcerpc.v5.dtypes import NULL +from impacket.dcerpc.v5.dcomrt import DCOMConnection +from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Login, WBEM_FLAG_FORWARD_ONLY, IWbemLevel1Login from cme.config import process_secret, host_info_colors from cme.connection import * @@ -56,7 +58,6 @@ from dploot.lib.target import Target from dploot.lib.smb import DPLootSMBConnection from pywerview.cli.helpers import * -from pywerview.requester import RPCRequester from time import time from datetime import datetime @@ -690,7 +691,7 @@ class smb(connection): self.hash, self.args.share, logger=self.logger, - timeout=self.args.wmiexec_timeout, + timeout=self.args.dcom_timeout, tries=self.args.get_output_tries ) self.logger.info("Executed command via wmiexec") @@ -711,7 +712,8 @@ class smb(connection): self.args.share, self.hash, self.logger, - self.args.get_output_tries + self.args.get_output_tries, + self.args.dcom_timeout ) self.logger.info("Executed command via mmcexec") break @@ -1207,45 +1209,64 @@ class smb(connection): def pass_pol(self): return PassPolDump(self).dump() + @requires_admin def wmi(self, wmi_query=None, namespace=None): records = [] + if not wmi_query: + wmi_query = self.args.wmi.strip('\n') + if not namespace: namespace = self.args.wmi_namespace try: - rpc = RPCRequester( - self.host, - self.domain, + dcom = DCOMConnection( + self.host if not self.kerberos else self.hostname + "." + self.domain, self.username, self.password, + self.domain, self.lmhash, self.nthash, + oxidResolver=True, + doKerberos=self.kerberos, + kdcHost=self.kdcHost, + aesKey=self.aesKey ) - rpc._create_wmi_connection(namespace=namespace) - - if wmi_query: - query = rpc._wmi_connection.ExecQuery(wmi_query, lFlags=WBEM_FLAG_FORWARD_ONLY) - else: - query = rpc._wmi_connection.ExecQuery(self.args.wmi, lFlags=WBEM_FLAG_FORWARD_ONLY) + iInterface = dcom.CoCreateInstanceEx(CLSID_WbemLevel1Login,IID_IWbemLevel1Login) + flag, stringBinding = dcom_FirewallChecker(iInterface, self.args.dcom_timeout) + if not flag: + self.logger.fail(f'WMI Query: Dcom initialization failed on connection with stringbinding: "{stringBinding}", please increase the timeout with the option "--dcom-timeout". If it\'s still failing maybe something is blocking the RPC connection') + # Make it force break function + dcom.disconnect() + return False + iWbemLevel1Login = IWbemLevel1Login(iInterface) + iWbemServices= iWbemLevel1Login.NTLMLogin(namespace , NULL, NULL) + iWbemLevel1Login.RemRelease() + iEnumWbemClassObject = iWbemServices.ExecQuery(wmi_query) except Exception as e: - self.logger.fail(f"Error creating WMI connection: {e}") - return records - - while True: + self.logger.fail('Execute WQL error: {}'.format(e)) + dcom.disconnect() + return False + else: + self.logger.info(f"Executing WQL syntax: {wmi_query}") + while True: + try: + wmi_results = iEnumWbemClassObject.Next(0xffffffff, 1)[0] + record = wmi_results.getProperties() + records.append(record) + for k,v in record.items(): + self.logger.highlight(f"{k} => {v['value']}") + except Exception as e: + if str(e).find('S_FALSE') < 0: + raise e + else: + break try: - wmi_results = query.Next(0xFFFFFFFF, 1)[0] - record = wmi_results.getProperties() - records.append(record) - for k, v in record.items(): - self.logger.highlight(f"{k} => {v['value']}") - self.logger.highlight("") - except Exception as e: - if str(e).find("S_FALSE") < 0: - raise e - else: - break + iEnumWbemClassObject.RemRelease() + dcom.disconnect() + except: + pass - return records + return records def spider( self, diff --git a/cme/protocols/smb/atexec.py b/cme/protocols/smb/atexec.py index f1caa6cc..016374b0 100755 --- a/cme/protocols/smb/atexec.py +++ b/cme/protocols/smb/atexec.py @@ -141,8 +141,7 @@ class TSCH_EXEC: dce.set_credentials(*self.__rpctransport.get_credentials()) dce.connect() # dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY) - dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY) - dce.bind(tsch.MSRPC_UUID_TSCHS) + tmpName = gen_random_string(8) tmpFileName = tmpName + ".tmp" @@ -152,11 +151,18 @@ class TSCH_EXEC: 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: - self.logger.fail(str(e)) + if hex(e.error_code) == "0x80070005": + self.logger.fail("ATEXEC: Create schedule task got blocked.") + else: + self.logger.fail(str(e)) return - taskCreated = True + else: + taskCreated = True self.logger.info(f"Running task \\{tmpName}") tsch.hSchRpcRun(dce, f"\\{tmpName}") diff --git a/cme/protocols/smb/mmcexec.py b/cme/protocols/smb/mmcexec.py index b9171a63..f32004e3 100644 --- a/cme/protocols/smb/mmcexec.py +++ b/cme/protocols/smb/mmcexec.py @@ -29,6 +29,7 @@ from os.path import join as path_join from time import sleep +from cme.connection import dcom_FirewallChecker from cme.helpers.misc import gen_random_string from impacket.dcerpc.v5.dcom.oaut import ( @@ -59,7 +60,7 @@ from impacket.dcerpc.v5.dtypes import NULL class MMCEXEC: - def __init__(self, host, share_name, username, password, domain, smbconnection, share, hashes=None, logger=None, tries=None): + def __init__(self, host, share_name, username, password, domain, smbconnection, share, hashes=None, logger=None, tries=None, timeout=None): self.__host = host self.__username = username self.__password = password @@ -78,6 +79,7 @@ class MMCEXEC: self.__share = share self.__dcom = None self.__tries = tries + self.__timeout = timeout self.logger = logger if hashes is not None: @@ -98,6 +100,11 @@ class MMCEXEC: ) try: iInterface = self.__dcom.CoCreateInstanceEx(string_to_bin("49B2791A-B1AE-4C90-9B8E-E860BA07F889"), IID_IDispatch) + flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__timeout) + if flag is False: + self.logger.fail(f'MMCEXEC: Dcom initialization failed on connection with stringbinding: "{self.__stringBinding}", please increase the timeout with the option "--dcom-timeout". If it\'s still failing maybe something is blocking the RPC connection, try another exec method') + # Make it force break function + self.__dcom.disconnect() iMMC = IDispatch(iInterface) resp = iMMC.GetIDsOfNames(("Document",)) diff --git a/cme/protocols/smb/proto_args.py b/cme/protocols/smb/proto_args.py index 6d01b180..9dac1c8b 100644 --- a/cme/protocols/smb/proto_args.py +++ b/cme/protocols/smb/proto_args.py @@ -79,7 +79,7 @@ def proto_args(parser, std_parser, module_parser): cgroup = smb_parser.add_argument_group("Command Execution", "Options for executing commands") cgroup.add_argument("--exec-method", choices={"wmiexec", "mmcexec", "smbexec", "atexec"}, default=None, help="method to execute the command. Ignored if in MSSQL mode (default: wmiexec)") - cgroup.add_argument("--wmiexec-timeout", help="WMIEXEC connection timeout, default is 5 secondes", type=int, default=5) + cgroup.add_argument("--dcom-timeout", help="DCOM connection timeout, default is 5 secondes", type=int, default=5) cgroup.add_argument("--get-output-tries", help="Number of times atexec/smbexec/mmcexec tries to get results, default is 5", type=int, default=5) cgroup.add_argument("--codec", default="utf-8", help="Set encoding used (codec) from the target's output (default " diff --git a/cme/protocols/smb/smbexec.py b/cme/protocols/smb/smbexec.py index e63d8c2f..1f51acd2 100755 --- a/cme/protocols/smb/smbexec.py +++ b/cme/protocols/smb/smbexec.py @@ -141,18 +141,21 @@ class SMBEXEC: except Exception as e: if "rpc_s_access_denied" in str(e): self.logger.fail("SMBEXEC: Create services got blocked.") - return self.__outputBuffer else: - pass + self.logger.fail(str(e)) + return self.__outputBuffer + try: self.logger.debug(f"Remote service {self.__serviceName} started.") scmr.hRStartServiceW(self.__scmr, service) - except: + + self.logger.debug(f"Remote service {self.__serviceName} deleted.") + scmr.hRDeleteService(self.__scmr, service) + scmr.hRCloseServiceHandle(self.__scmr, service) + except Exception as e: pass - self.logger.debug(f"Remote service {self.__serviceName} deleted.") - scmr.hRDeleteService(self.__scmr, service) - scmr.hRCloseServiceHandle(self.__scmr, service) + self.get_output_remote() def get_output_remote(self): diff --git a/cme/protocols/smb/wmiexec.py b/cme/protocols/smb/wmiexec.py index 2e762730..216ae467 100755 --- a/cme/protocols/smb/wmiexec.py +++ b/cme/protocols/smb/wmiexec.py @@ -4,6 +4,7 @@ import ntpath import os from time import sleep +from cme.connection import dcom_FirewallChecker from cme.helpers.misc import gen_random_string from impacket.dcerpc.v5 import transport from impacket.dcerpc.v5.dcomrt import DCOMConnection @@ -73,39 +74,16 @@ class WMIEXEC: kdcHost=self.__kdcHost, ) iInterface = self.__dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login) - try: - self.firewall_check(iInterface, self.__timeout) - except: - self.logger.fail(f'WMIEXEC: Dcom initialization failed on connection with stringbinding: "{self.__stringBinding}", please increase the timeout with the option "--wmiexec-timeout". If it\'s still failing maybe something is blocking the RPC connection, try another exec method') + flag, self.__stringBinding = dcom_FirewallChecker(iInterface, self.__timeout) + if flag is False: + self.logger.fail(f'WMIEXEC: Dcom initialization failed on connection with stringbinding: "{self.__stringBinding}", please increase the timeout with the option "--dcom-timeout". If it\'s still failing maybe something is blocking the RPC connection, try another exec method') + # Make it force break function self.__dcom.disconnect() iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface) iWbemServices = iWbemLevel1Login.NTLMLogin("//./root/cimv2", NULL, NULL) iWbemLevel1Login.RemRelease() self.__win32Process, _ = iWbemServices.GetObject("Win32_Process") - def firewall_check(self, iInterface ,timeout): - stringBindings = iInterface.get_cinstance().get_string_bindings() - for strBinding in stringBindings: - if strBinding['wTowerId'] == 7: - if strBinding['aNetworkAddr'].find('[') >= 0: - binding, _, bindingPort = strBinding['aNetworkAddr'].partition('[') - bindingPort = '[' + bindingPort - else: - binding = strBinding['aNetworkAddr'] - bindingPort = '' - - if binding.upper().find(iInterface.get_target().upper()) >= 0: - stringBinding = 'ncacn_ip_tcp:' + strBinding['aNetworkAddr'][:-1] - break - elif iInterface.is_fqdn() and binding.upper().find(iInterface.get_target().upper().partition('.')[0]) >= 0: - stringBinding = 'ncacn_ip_tcp:%s%s' % (iInterface.get_target(), bindingPort) - - self.__stringBinding = stringBinding - rpctransport = transport.DCERPCTransportFactory(stringBinding) - rpctransport.set_connect_timeout(timeout) - rpctransport.connect() - rpctransport.disconnect() - def execute(self, command, output=False): self.__retOutput = output if self.__retOutput: diff --git a/poetry.lock b/poetry.lock index b4c181b5..5065c3d1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. [[package]] name = "aardwolf" @@ -1517,18 +1517,22 @@ files = [ [[package]] name = "oscrypto" version = "1.3.0" -description = "TLS (SSL) sockets, key generation, encryption, decryption, signing, verification and KDFs using the OS crypto libraries. Does not require a compiler, and relies on the OS for patching. Works on Windows, OS X and Linux/BSD." +description = "" category = "main" optional = false python-versions = "*" -files = [ - {file = "oscrypto-1.3.0-py2.py3-none-any.whl", hash = "sha256:2b2f1d2d42ec152ca90ccb5682f3e051fb55986e1b170ebde472b133713e7085"}, - {file = "oscrypto-1.3.0.tar.gz", hash = "sha256:6f5fef59cb5b3708321db7cca56aed8ad7e662853351e7991fcf60ec606d47a4"}, -] +files = [] +develop = false [package.dependencies] asn1crypto = ">=1.5.1" +[package.source] +type = "git" +url = "https://github.com/NeffIsBack/oscrypto" +reference = "HEAD" +resolved_reference = "d5f3437ed24257895ae1edd9e503cfb352e635a8" + [[package]] name = "packaging" version = "23.1" @@ -2825,4 +2829,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = "^3.7.0" -content-hash = "08ee44e127854163d05ae0473213bc2ce67f52fde734c7dde21f1c4a6535e124" +content-hash = "9dc5181178139fe742c1b9d18de9613e544a11e221b30299aabc8ab04b68cc09" diff --git a/pyproject.toml b/pyproject.toml index a5c9e62f..9c016f5b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,6 +54,7 @@ pyasn1-modules = "^0.3.0" rich = "^13.3.5" python-libnmap = "^0.7.3" resource = "^0.2.1" +oscrypto = { git = "https://github.com/NeffIsBack/oscrypto" } [tool.poetry.group.dev.dependencies] flake8 = "*"