From 44e5020123e9a05b5570c6a16a6fe1482d66fed0 Mon Sep 17 00:00:00 2001 From: mpgn Date: Tue, 21 Feb 2023 16:28:53 -0500 Subject: [PATCH 1/3] Add enum_av from @tothi --- cme/modules/enum_av.py | 362 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 cme/modules/enum_av.py diff --git a/cme/modules/enum_av.py b/cme/modules/enum_av.py new file mode 100644 index 00000000..581ef400 --- /dev/null +++ b/cme/modules/enum_av.py @@ -0,0 +1,362 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# All credit to @an0n_r0 +# project : https://github.com/tothi/serviceDetector + +from impacket.dcerpc.v5 import lsat, lsad +from impacket.dcerpc.v5.rpcrt import DCERPCException +from impacket.dcerpc.v5.dtypes import NULL, MAXIMUM_ALLOWED, RPC_UNICODE_STRING +from impacket.dcerpc.v5 import transport, epm + +import json +import pathlib + +class CMEModule: + ''' + Uses LsarLookupNames and NamedPipes to gather information on all endpoint protection solutions installed on the the remote host(s) + Module by @mpgn_x64 + + ''' + + name = 'enum_av' + description = 'Gathers information on all endpoint protection solutions installed on the the remote host(s) via LsarLookupNames (no privilege needed)' + supported_protocols = ['smb'] + opsec_safe = True + multiple_hosts = True + + def options(self, context, module_options): + pass + + def on_login(self, context, connection): + + success = 0 + results = {} + context.log.debug("Detecting installed services on {} using LsarLookupNames()...".format(connection.host)) + try: + lsa = LsaLookupNames(connection.domain, connection.username, connection.password, connection.hostname if connection.kerberos else connection.host, connection.kerberos, connection.domain, connection.lmhash, connection.nthash) + dce, rpctransport = lsa.connect() + policyHandle = lsa.open_policy(dce) + + for i, product in enumerate(conf['products']): + for service in product['services']: + try: + lsa.LsarLookupNames(dce, policyHandle, service['name']) + context.log.debug("Detected installed service on {}: {} {}".format(connection.host, product['name'], service['description'])) + if product['name'] not in results: + results[product['name']] = {"services": []} + results[product['name']]['services'].append(service) + except Exception as e: + pass + success += 1 + except Exception as e: + context.log.error(str(e)) + + context.log.debug("Detecting running processes on {} by enumerating pipes...".format(connection.host)) + try: + for f in connection.conn.listPath('IPC$', '\\*'): + fl = f.get_longname() + for i, product in enumerate(conf['products']): + for pipe in product['pipes']: + if pathlib.PurePath(fl).match(pipe['name']): + context.log.debug("{} running claim found on {} by existing pipe {} (likely processes: {})".format(product['name'], connection.host, fl, pipe['processes'])) + if product['name'] not in results: + results[product['name']] = {} + if "pipes" not in results[product['name']]: + results[product['name']]['pipes'] = [] + results[product['name']]['pipes'].append(pipe) + success += 1 + except Exception as e: + context.log.error(str(e)) + + self.dump_results(results, connection.host, success, context) + + def dump_results(self, results, remoteName, success, context): + out1 = "On host {} found".format(remoteName) + for item in results: + out = out1 + if 'services' in results[item]: + out += " {} INSTALLED".format(item) + if 'pipes' in results[item]: + out += " and it seems to be RUNNING".format() + else: + for product in conf['products']: + if (item == product['name']) and (len(product['pipes']) == 0): + out += " (NamedPipe for this service was not provided in config)" + elif 'pipes' in results[item]: + out += " {} RUNNING".format(item) + context.log.highlight(out) + if (len(results) < 1) and (success > 1): + out = out1 + " NOTHING!".format() + context.log.highlight(out) + +class LsaLookupNames(): + timeout = None + authn_level = None + protocol = None + transfer_syntax = None + machine_account = False + + iface_uuid = lsat.MSRPC_UUID_LSAT + authn = True + + def __init__(self, domain="", username="", password="", remoteName="", k=False, kdcHost="", lmhash="", nthash=""): + self.domain = domain + self.username = username + self.password = password + self.remoteName = remoteName + self.string_binding = r"ncacn_np:{}[\PIPE\lsarpc]".format(remoteName) + self.doKerberos = k + self.lmhash = lmhash + self.nthash = nthash + self.dcHost = kdcHost + + def connect(self, string_binding=None, iface_uuid=None): + """Obtains a RPC Transport and a DCE interface according to the bindings and + transfer syntax specified. + :return: tuple of DCE/RPC and RPC Transport objects + :rtype: (DCERPC_v5, DCERPCTransport) + """ + string_binding = string_binding or self.string_binding + if not string_binding: + raise NotImplemented("String binding must be defined") + + rpc_transport = transport.DCERPCTransportFactory(string_binding) + + # Set timeout if defined + if self.timeout: + rpc_transport.set_connect_timeout(self.timeout) + + # Authenticate if specified + if self.authn and hasattr(rpc_transport, 'set_credentials'): + # This method exists only for selected protocol sequences. + rpc_transport.set_credentials(self.username, self.password, self.domain, self.lmhash, self.nthash) + + if self.doKerberos: + rpc_transport.set_kerberos(self.doKerberos, kdcHost=self.dcHost) + + # Gets the DCE RPC object + dce = rpc_transport.get_dce_rpc() + + # Set the authentication level + if self.authn_level: + dce.set_auth_level(self.authn_level) + + # Connect + dce.connect() + + # Bind if specified + iface_uuid = iface_uuid or self.iface_uuid + if iface_uuid and self.transfer_syntax: + dce.bind(iface_uuid, transfer_syntax=self.transfer_syntax) + elif iface_uuid: + dce.bind(iface_uuid) + + return dce, rpc_transport + + + def open_policy(self, dce): + request = lsad.LsarOpenPolicy2() + request['SystemName'] = NULL + request['ObjectAttributes']['RootDirectory'] = NULL + request['ObjectAttributes']['ObjectName'] = NULL + request['ObjectAttributes']['SecurityDescriptor'] = NULL + request['ObjectAttributes']['SecurityQualityOfService'] = NULL + request['DesiredAccess'] = MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES + resp = dce.request(request) + return resp['PolicyHandle'] + + + def LsarLookupNames(self, dce, policyHandle, service): + request = lsat.LsarLookupNames() + request['PolicyHandle'] = policyHandle + request['Count'] = 1 + name1 = RPC_UNICODE_STRING() + name1['Data'] = 'NT Service\{}'.format(service) + request['Names'].append(name1) + request['TranslatedSids']['Sids'] = NULL + request['LookupLevel'] = lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta + resp = dce.request(request) + return resp + + +conf = { + "products": [ + { + "name": "Bitdefender", + "services": [ + { + "name": "bdredline_agent", + "description": "Bitdefender Agent RedLine Service" + }, + { + "name": "BDAuxSrv", + "description": "Bitdefender Auxiliary Service" + }, + { + "name": "UPDATESRV", + "description": "Bitdefender Desktop Update Service" + }, + { + "name": "VSSERV", + "description": "Bitdefender Virus Shield" + }, + { + "name": "bdredline", + "description": "Bitdefender RedLine Service" + } + ], + "pipes": [ + { + "name": "local\\msgbus\\antitracker.low\\*", + "processes": [ + "bdagent.exe" + ] + }, + { + "name": "local\\msgbus\\aspam.actions.low\\*", + "processes": [ + "bdagent.exe" + ] + }, + { + "name": "local\\msgbus\\bd.process.broker.pipe", + "processes": [ + "bdagent.exe", + "bdservicehost.exe", + "updatesrv.exe" + ] + }, + { + "name": "local\\msgbus\\bdagent*", + "processes": [ + "bdagent.exe" + ] + }, + { + "name": "local\\msgbus\\bdauxsrv", + "processes": [ + "bdagent.exe", + "bdntwrk.exe" + ] + } + ] + }, + { + "name": "Windows Defender", + "services": [ + { + "name": "WinDefend", + "description": "Windows Defender Antivirus Service" + }, + { + "name": "Sense", + "description": "Windows Defender Advanced Threat Protection Service" + }, + { + "name": "WdNisSvc", + "description": "Windows Defender Antivirus Network Inspection Service" + } + ], + "pipes": [] + }, + { + "name": "ESET", + "services": [ + { + "name": "ekm", + "description": "ESET" + }, + { + "name": "epfw", + "description": "ESET" + }, + { + "name": "epfwlwf", + "description": "ESET" + }, + { + "name": "epfwwfp", + "description": "ESET" + }, + { + "name": "EraAgentSvc", + "description": "ESET" + } + ], + "pipes": [ + { + "name": "nod_scriptmon_pipe", + "processes": [ + "" + ] + } + ] + }, + { + "name": "CrowdStrike", + "services": [ + { + "name": "CSFalconService", + "description": "CrowdStrike Falcon Sensor Service" + } + ], + "pipes": [ + { + "name": "CrowdStrike\\{*", + "processes": [ + "CSFalconContainer.exe", + "CSFalconService.exe" + ] + } + ] + }, + { + "name": "SentinelOne", + "services": [ + { + "name": "SentinelAgent", + "description": "SentinelOne Endpoint Protection Agent" + }, + { + "name": "SentinelStaticEngine", + "description": "Manage static engines for SentinelOne Endpoint Protection" + }, + { + "name": "LogProcessorService", + "description": "Manage logs for SentinelOne Endpoint Protection" + } + ], + "pipes": [ + { + "name": "SentinelAgentWorkerCert.*", + "processes": [ + "" + ] + }, + { + "name": "DFIScanner.Etw.*", + "processes": [ + "SentinelStaticEngine.exe" + ] + }, + { + "name": "DFIScanner.Inline.*", + "processes": [ + "SentinelAgent.exe" + ] + } + ] + }, + { + "name": "Carbon Black App Control", + "services": [ + { + "name": "Parity", + "description": "Carbon Black App Control Agent" + } + ], + "pipes": [] + } + ] +} From c294f87725bbc37e3f180ad230d26dd4117c745f Mon Sep 17 00:00:00 2001 From: mpgn Date: Tue, 21 Feb 2023 16:57:14 -0500 Subject: [PATCH 2/3] Improve output --- cme/modules/enum_av.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cme/modules/enum_av.py b/cme/modules/enum_av.py index 581ef400..2510c003 100644 --- a/cme/modules/enum_av.py +++ b/cme/modules/enum_av.py @@ -69,20 +69,21 @@ class CMEModule: except Exception as e: context.log.error(str(e)) - self.dump_results(results, connection.host, success, context) + self.dump_results(results, connection.hostname, success, context) def dump_results(self, results, remoteName, success, context): - out1 = "On host {} found".format(remoteName) + # out1 = "On host {} found".format(remoteName) + out1 = "" for item in results: out = out1 if 'services' in results[item]: - out += " {} INSTALLED".format(item) + out += "{} INSTALLED".format(item) if 'pipes' in results[item]: out += " and it seems to be RUNNING".format() - else: - for product in conf['products']: - if (item == product['name']) and (len(product['pipes']) == 0): - out += " (NamedPipe for this service was not provided in config)" + # else: + # for product in conf['products']: + # if (item == product['name']) and (len(product['pipes']) == 0): + # out += " (NamedPipe for this service was not provided in config)" elif 'pipes' in results[item]: out += " {} RUNNING".format(item) context.log.highlight(out) From b03e87b96e50872620d4f4a4bd8454134544d4f0 Mon Sep 17 00:00:00 2001 From: mpgn Date: Tue, 28 Feb 2023 10:46:13 -0500 Subject: [PATCH 3/3] update av json --- cme/modules/enum_av.py | 49 ++++++++++++++++++++++++++++++++++ cme/modules/enum_avproducts.py | 33 ----------------------- 2 files changed, 49 insertions(+), 33 deletions(-) delete mode 100644 cme/modules/enum_avproducts.py diff --git a/cme/modules/enum_av.py b/cme/modules/enum_av.py index 2510c003..9de2c43b 100644 --- a/cme/modules/enum_av.py +++ b/cme/modules/enum_av.py @@ -358,6 +358,55 @@ conf = { } ], "pipes": [] + }, + { + "name": "Cybereason", + "services": [ + { + "name": "CybereasonActiveProbe", + "description": "Cybereason Active Probe" + }, + { + "name": "CybereasonCRS", + "description": "Cybereason Anti-Ransomware" + }, + { + "name": "CybereasonBlocki", + "description": "Cybereason Execution Prevention" + } + ], + "pipes": [ + { + "name": "CybereasonAPConsoleMinionHostIpc_*", + "processes": [ + "minionhost.exe" + ] + }, + { + "name": "CybereasonAPServerProxyIpc_*", + "processes": [ + "minionhost.exe" + ] + } + ] + }, + { + "name": "Symantec Endpoint Protection", + "services": [ + { + "name": "SepMasterService", + "description": "Symantec Endpoint Protection" + }, + { + "name": "SepScanService", + "description": "Symantec Endpoint Protection Scan Services" + }, + { + "name": "SNAC", + "description": "Symantec Network Access Control" + } + ], + "pipes": [] } ] } diff --git a/cme/modules/enum_avproducts.py b/cme/modules/enum_avproducts.py deleted file mode 100644 index e1c51526..00000000 --- a/cme/modules/enum_avproducts.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -class CMEModule: - ''' - Uses WMI to gather information on all endpoint protection solutions installed on the the remote host(s) - Module by @byt3bl33d3r - - ''' - - name = 'enum_avproducts' - description = 'Gathers information on all endpoint protection solutions installed on the the remote host(s) via WMI' - supported_protocols = ['smb'] - opsec_safe= True - multiple_hosts = True - - def options(self, context, module_options): - pass - - def on_admin_login(self, context, connection): - output = connection.wmi('Select * From AntiSpywareProduct', 'root\\SecurityCenter2') - if output: - context.log.success('Found Anti-Spyware product:') - for entry in output: - for k,v in entry.items(): - context.log.highlight('{} => {}'.format(k,v['value'])) - - output = connection.wmi('Select * from AntiVirusProduct', 'root\\SecurityCenter2') - if output: - context.log.success('Found Anti-Virus product:') - for entry in output: - for k,v in entry.items(): - context.log.highlight('{} => {}'.format(k,v['value'])) \ No newline at end of file