diff --git a/monkey/common/utils/attack_utils.py b/monkey/common/utils/attack_utils.py index cb3c8f029..77d813351 100644 --- a/monkey/common/utils/attack_utils.py +++ b/monkey/common/utils/attack_utils.py @@ -10,6 +10,16 @@ class ScanStatus(Enum): USED = 2 +class UsageEnum(Enum): + SMB = {ScanStatus.USED.value: "SMB exploiter ran the monkey by creating a service via MS-SCMR.", + ScanStatus.SCANNED.value: "SMB exploiter failed to run the monkey by creating a service via MS-SCMR."} + MIMIKATZ = {ScanStatus.USED.value: "Windows module loader was used to load Mimikatz DLL.", + ScanStatus.SCANNED.value: "Monkey tried to load Mimikatz DLL, but failed."} + MIMIKATZ_WINAPI = {ScanStatus.USED.value: "WinAPI was called to load mimikatz.", + ScanStatus.SCANNED.value: "Monkey tried to call WinAPI to load mimikatz."} + DROPPER = {ScanStatus.USED.value: "WinAPI was used to mark monkey files for deletion on next boot."} + + # Dict that describes what BITS job was used for BITS_UPLOAD_STRING = "BITS job was used to upload monkey to a remote system." diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index f34784041..ba30bf515 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -125,6 +125,7 @@ class ControlClient(object): @staticmethod def send_telemetry(telem_category, data): if not WormConfiguration.current_server: + LOG.error("Trying to send %s telemetry before current server is established, aborting." % telem_category) return try: telemetry = {'monkey_guid': GUID, 'telem_category': telem_category, 'data': data} diff --git a/monkey/infection_monkey/dropper.py b/monkey/infection_monkey/dropper.py index 6bd9dab84..e41ad8681 100644 --- a/monkey/infection_monkey/dropper.py +++ b/monkey/infection_monkey/dropper.py @@ -15,7 +15,7 @@ from infection_monkey.exploit.tools.helpers import build_monkey_commandline_expl from infection_monkey.model import MONKEY_CMDLINE_WINDOWS, MONKEY_CMDLINE_LINUX, GENERAL_CMDLINE_LINUX from infection_monkey.system_info import SystemInfoCollector, OperatingSystem from infection_monkey.telemetry.attack.t1106_telem import T1106Telem -from common.utils.attack_utils import ScanStatus +from common.utils.attack_utils import ScanStatus, UsageEnum if "win32" == sys.platform: from win32process import DETACHED_PROCESS @@ -158,7 +158,6 @@ class MonkeyDrops(object): else: LOG.debug("Dropper source file '%s' is marked for deletion on next boot", self._config['source_path']) - T1106Telem(ScanStatus.USED, "WinAPI was used to mark monkey files" - " for deletion on next boot.").send() + T1106Telem(ScanStatus.USED, UsageEnum.DROPPER.name).send() except AttributeError: LOG.error("Invalid configuration options. Failing") diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index dc7b38e89..3b09cbf48 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -11,7 +11,7 @@ from infection_monkey.network import SMBFinger from infection_monkey.network.tools import check_tcp_port from common.utils.exploit_enum import ExploitType from infection_monkey.telemetry.attack.t1035_telem import T1035Telem -from common.utils.attack_utils import ScanStatus +from common.utils.attack_utils import ScanStatus, UsageEnum LOG = getLogger(__name__) @@ -133,11 +133,11 @@ class SmbExploiter(HostExploiter): service = resp['lpServiceHandle'] try: scmr.hRStartServiceW(scmr_rpc, service) - T1035Telem(ScanStatus.USED, "SMB exploiter ran the monkey by creating a service via MS-SCMR.").send() + status = ScanStatus.USED except: - T1035Telem(ScanStatus.SCANNED, - "SMB exploiter failed to run the monkey by creating a service via MS-SCMR.").send() + status = ScanStatus.SCANNED pass + T1035Telem(status, UsageEnum.SMB.name).send() scmr.hRDeleteService(scmr_rpc, service) scmr.hRCloseServiceHandle(scmr_rpc, service) diff --git a/monkey/infection_monkey/system_info/mimikatz_collector.py b/monkey/infection_monkey/system_info/mimikatz_collector.py index 96b53be35..0e6a913c9 100644 --- a/monkey/infection_monkey/system_info/mimikatz_collector.py +++ b/monkey/infection_monkey/system_info/mimikatz_collector.py @@ -5,7 +5,7 @@ import socket import zipfile import infection_monkey.config -from common.utils.attack_utils import ScanStatus +from common.utils.attack_utils import ScanStatus, UsageEnum from infection_monkey.telemetry.attack.t1129_telem import T1129Telem from infection_monkey.telemetry.attack.t1106_telem import T1106Telem from infection_monkey.pyinstaller_utils import get_binary_file_path, get_binaries_dir_path @@ -47,16 +47,17 @@ class MimikatzCollector(object): collect_proto = ctypes.WINFUNCTYPE(ctypes.c_int) get_proto = ctypes.WINFUNCTYPE(MimikatzCollector.LogonData) get_text_output_proto = ctypes.WINFUNCTYPE(ctypes.c_wchar_p) - T1106Telem(ScanStatus.USED, "WinAPI was called to load mimikatz.").send() self._collect = collect_proto(("collect", self._dll)) self._get = get_proto(("get", self._dll)) self._get_text_output_proto = get_text_output_proto(("getTextOutput", self._dll)) self._isInit = True - T1129Telem(ScanStatus.USED, "Windows module loader was used to load Mimikatz DLL.").send() + status = ScanStatus.USED except Exception: LOG.exception("Error initializing mimikatz collector") - T1129Telem(ScanStatus.SCANNED, "Monkey tried to load Mimikatz DLL, but failed.").send() - T1106Telem(ScanStatus.SCANNED, "Monkey tried to call WinAPI to load mimikatz.").send() + status = ScanStatus.SCANNED + T1106Telem(status, UsageEnum.MIMIKATZ_WINAPI.name).send() + T1129Telem(status, UsageEnum.MIMIKATZ.name).send() + def get_logon_info(self): """ diff --git a/monkey/infection_monkey/system_singleton.py b/monkey/infection_monkey/system_singleton.py index 06a2ea689..d8c3ec51c 100644 --- a/monkey/infection_monkey/system_singleton.py +++ b/monkey/infection_monkey/system_singleton.py @@ -4,8 +4,6 @@ import sys from abc import ABCMeta, abstractmethod from infection_monkey.config import WormConfiguration -from infection_monkey.telemetry.attack.t1106_telem import T1106Telem -from common.utils.attack_utils import ScanStatus __author__ = 'itamar' @@ -48,19 +46,14 @@ class WindowsSystemSingleton(_SystemSingleton): if not handle: LOG.error("Cannot acquire system singleton %r, unknown error %d", self._mutex_name, last_error) - T1106Telem(ScanStatus.SCANNED, "WinAPI call to acquire system singleton " - "for monkey process wasn't successful.").send() - return False if winerror.ERROR_ALREADY_EXISTS == last_error: LOG.debug("Cannot acquire system singleton %r, mutex already exist", self._mutex_name) - return False self._mutex_handle = handle - T1106Telem(ScanStatus.USED, "WinAPI was called to acquire system singleton for monkey's process.").send() LOG.debug("Global singleton mutex %r acquired", self._mutex_name) diff --git a/monkey/infection_monkey/telemetry/attack/t1035_telem.py b/monkey/infection_monkey/telemetry/attack/t1035_telem.py index b3d7c90de..13d0bcc59 100644 --- a/monkey/infection_monkey/telemetry/attack/t1035_telem.py +++ b/monkey/infection_monkey/telemetry/attack/t1035_telem.py @@ -6,6 +6,6 @@ class T1035Telem(UsageTelem): """ T1035 telemetry. :param status: ScanStatus of technique - :param usage: Usage string + :param usage: Enum name of UsageEnum """ super(T1035Telem, self).__init__('T1035', status, usage) diff --git a/monkey/infection_monkey/telemetry/attack/t1106_telem.py b/monkey/infection_monkey/telemetry/attack/t1106_telem.py index 30cad6072..422313540 100644 --- a/monkey/infection_monkey/telemetry/attack/t1106_telem.py +++ b/monkey/infection_monkey/telemetry/attack/t1106_telem.py @@ -4,8 +4,8 @@ from infection_monkey.telemetry.attack.usage_telem import UsageTelem class T1106Telem(UsageTelem): def __init__(self, status, usage): """ - T1129 telemetry. + T1106 telemetry. :param status: ScanStatus of technique - :param usage: Usage string + :param usage: Enum name of UsageEnum """ super(T1106Telem, self).__init__("T1106", status, usage) diff --git a/monkey/infection_monkey/telemetry/attack/t1129_telem.py b/monkey/infection_monkey/telemetry/attack/t1129_telem.py index fb7d776c6..a04834959 100644 --- a/monkey/infection_monkey/telemetry/attack/t1129_telem.py +++ b/monkey/infection_monkey/telemetry/attack/t1129_telem.py @@ -6,6 +6,6 @@ class T1129Telem(UsageTelem): """ T1129 telemetry. :param status: ScanStatus of technique - :param usage: Usage string + :param usage: Enum name of UsageEnum """ super(T1129Telem, self).__init__("T1129", status, usage) diff --git a/monkey/infection_monkey/telemetry/attack/usage_telem.py b/monkey/infection_monkey/telemetry/attack/usage_telem.py index 48ff5431c..d493c64d8 100644 --- a/monkey/infection_monkey/telemetry/attack/usage_telem.py +++ b/monkey/infection_monkey/telemetry/attack/usage_telem.py @@ -5,9 +5,9 @@ class UsageTelem(AttackTelem): def __init__(self, technique, status, usage): """ - T1035 telemetry. + :param technique: Id of technique :param status: ScanStatus of technique - :param usage: Usage string + :param usage: Enum name of UsageEnum """ super(UsageTelem, self).__init__(technique, status) self.usage = usage diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py index a651a8288..fcc230be5 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py @@ -1,10 +1,10 @@ from monkey_island.cc.database import mongo -from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from monkey_island.cc.services.attack.technique_reports import UsageTechnique __author__ = "VakarisZ" -class T1035(AttackTechnique): +class T1035(UsageTechnique): tech_id = "T1035" unscanned_msg = "Monkey didn't try to interact with Windows services." scanned_msg = "Monkey tried to interact with Windows services, but failed." @@ -13,5 +13,5 @@ class T1035(AttackTechnique): @staticmethod def get_report_data(): data = T1035.get_tech_base_data() - data.update({'services': list(mongo.db.telemetry.aggregate(T1035.get_usage_query()))}) + data.update({'services': T1035.get_usage_data()}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1106.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1106.py index b24d10bd9..b50b19883 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1106.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1106.py @@ -1,10 +1,9 @@ -from monkey_island.cc.database import mongo -from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from monkey_island.cc.services.attack.technique_reports import UsageTechnique __author__ = "VakarisZ" -class T1106(AttackTechnique): +class T1106(UsageTechnique): tech_id = "T1106" unscanned_msg = "Monkey didn't try to directly use WinAPI." scanned_msg = "Monkey tried to use WinAPI, but failed." @@ -13,5 +12,5 @@ class T1106(AttackTechnique): @staticmethod def get_report_data(): data = T1106.get_tech_base_data() - data.update({'api_uses': list(mongo.db.telemetry.aggregate(T1106.get_usage_query()))}) + data.update({'api_uses': T1106.get_usage_data()}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py index 7d9fa9dd0..f1a4d1b83 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py @@ -1,10 +1,10 @@ from monkey_island.cc.database import mongo -from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from monkey_island.cc.services.attack.technique_reports import UsageTechnique __author__ = "VakarisZ" -class T1129(AttackTechnique): +class T1129(UsageTechnique): tech_id = "T1129" unscanned_msg = "Monkey didn't try to load any DLL's." scanned_msg = "Monkey tried to load DLL's, but failed." @@ -13,5 +13,5 @@ class T1129(AttackTechnique): @staticmethod def get_report_data(): data = T1129.get_tech_base_data() - data.update({'dlls': list(mongo.db.telemetry.aggregate(T1129.get_usage_query()))}) + data.update({'dlls': T1129.get_usage_data()}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py index de55cd08c..cc702de62 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -1,10 +1,13 @@ import abc +import logging from monkey_island.cc.database import mongo -from common.utils.attack_utils import ScanStatus +from common.utils.attack_utils import ScanStatus, UsageEnum from monkey_island.cc.services.attack.attack_config import AttackConfig from common.utils.code_utils import abstractstatic +logger = logging.getLogger(__name__) + class AttackTechnique(object): """ Abstract class for ATT&CK report components """ @@ -113,6 +116,29 @@ class AttackTechnique(object): data.update({'title': cls.technique_title()}) return data + +class UsageTechnique(AttackTechnique): + __metaclass__ = abc.ABCMeta + + @staticmethod + def parse_usages(usage): + """ + Parses data from database and translates usage enums into strings + :param usage: + :return: + """ + try: + usage['usage'] = UsageEnum[usage['usage']].value[usage['status']] + except KeyError: + logger.error("Error translating usage enum. into string. " + "Check if usage enum field exists and covers all telem. statuses.") + return usage + + @classmethod + def get_usage_data(cls): + data = list(mongo.db.telemetry.aggregate(cls.get_usage_query())) + return list(map(cls.parse_usages, data)) + @classmethod def get_usage_query(cls): """ @@ -131,4 +157,5 @@ class AttackTechnique(object): {'$addFields': {'_id': 0, 'machine': {'hostname': '$monkey.hostname', 'ips': '$monkey.ip_addresses'}, 'monkey': 0}}, - {'$group': {'_id': {'machine': '$machine', 'status': '$status', 'usage': '$usage'}}}] + {'$group': {'_id': {'machine': '$machine', 'status': '$status', 'usage': '$usage'}}}, + {"$replaceRoot": {"newRoot": "$_id"}}] diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js index fd11e3cbc..3331fbeaf 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js @@ -27,12 +27,12 @@ export function getUsageColumns() { columns: [ {Header: 'Machine', id: 'machine', - accessor: x => renderMachineFromSystemData(x._id.machine), + accessor: x => renderMachineFromSystemData(x.machine), style: { 'whiteSpace': 'unset' }, width: 300}, {Header: 'Usage', id: 'usage', - accessor: x => x._id.usage, + accessor: x => x.usage, style: { 'whiteSpace': 'unset' }}] }])}