From 9415f6e73c6996f1d3dc22e3a629cd1c0c73928f Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 1 Jul 2019 16:52:57 +0300 Subject: [PATCH 1/2] Execution trough WinAPI attack technique implemented --- monkey/infection_monkey/dropper.py | 4 +++ .../system_info/mimikatz_collector.py | 7 +++-- monkey/infection_monkey/system_singleton.py | 6 +++- .../telemetry/attack/t1106_telem.py | 11 +++++++ .../cc/services/attack/attack_report.py | 5 ++-- .../cc/services/attack/attack_schema.py | 9 ++++++ .../attack/technique_reports/T1106.py | 17 +++++++++++ .../cc/services/config_schema.py | 4 +-- .../src/components/attack/techniques/T1106.js | 30 +++++++++++++++++++ .../report-components/AttackReport.js | 4 ++- 10 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 monkey/infection_monkey/telemetry/attack/t1106_telem.py create mode 100644 monkey/monkey_island/cc/services/attack/technique_reports/T1106.py create mode 100644 monkey/monkey_island/cc/ui/src/components/attack/techniques/T1106.js diff --git a/monkey/infection_monkey/dropper.py b/monkey/infection_monkey/dropper.py index cc065a745..e39860d50 100644 --- a/monkey/infection_monkey/dropper.py +++ b/monkey/infection_monkey/dropper.py @@ -14,6 +14,8 @@ from infection_monkey.config import WormConfiguration from infection_monkey.exploit.tools import build_monkey_commandline_explicitly 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 if "win32" == sys.platform: from win32process import DETACHED_PROCESS @@ -156,5 +158,7 @@ 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() except AttributeError: LOG.error("Invalid configuration options. Failing") diff --git a/monkey/infection_monkey/system_info/mimikatz_collector.py b/monkey/infection_monkey/system_info/mimikatz_collector.py index 4f2348531..96b53be35 100644 --- a/monkey/infection_monkey/system_info/mimikatz_collector.py +++ b/monkey/infection_monkey/system_info/mimikatz_collector.py @@ -7,6 +7,7 @@ import zipfile import infection_monkey.config from common.utils.attack_utils import ScanStatus 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 __author__ = 'itay.mizeretz' @@ -46,6 +47,7 @@ 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)) @@ -54,6 +56,7 @@ class MimikatzCollector(object): 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() def get_logon_info(self): """ @@ -70,7 +73,7 @@ class MimikatzCollector(object): logon_data_dictionary = {} hostname = socket.gethostname() - + self.mimikatz_text = self._get_text_output_proto() for i in range(entry_count): @@ -105,7 +108,7 @@ class MimikatzCollector(object): except Exception: LOG.exception("Error getting logon info") return {} - + def get_mimikatz_text(self): return self.mimikatz_text diff --git a/monkey/infection_monkey/system_singleton.py b/monkey/infection_monkey/system_singleton.py index 9f56c238e..06a2ea689 100644 --- a/monkey/infection_monkey/system_singleton.py +++ b/monkey/infection_monkey/system_singleton.py @@ -4,6 +4,8 @@ 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' @@ -46,6 +48,8 @@ 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 @@ -56,7 +60,7 @@ class WindowsSystemSingleton(_SystemSingleton): 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/t1106_telem.py b/monkey/infection_monkey/telemetry/attack/t1106_telem.py new file mode 100644 index 000000000..30cad6072 --- /dev/null +++ b/monkey/infection_monkey/telemetry/attack/t1106_telem.py @@ -0,0 +1,11 @@ +from infection_monkey.telemetry.attack.usage_telem import UsageTelem + + +class T1106Telem(UsageTelem): + def __init__(self, status, usage): + """ + T1129 telemetry. + :param status: ScanStatus of technique + :param usage: Usage string + """ + super(T1106Telem, self).__init__("T1106", status, usage) diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index ab5b82245..8e7d44905 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,6 +1,6 @@ import logging from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003, T1059, T1086, T1082 -from monkey_island.cc.services.attack.technique_reports import T1145, T1035, T1129 +from monkey_island.cc.services.attack.technique_reports import T1145, T1035, T1129, T1106 from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.database import mongo @@ -19,7 +19,8 @@ TECHNIQUES = {'T1210': T1210.T1210, 'T1082': T1082.T1082, 'T1145': T1145.T1145, 'T1035': T1035.T1035, - 'T1129': T1129.T1129} + 'T1129': T1129.T1129, + 'T1106': T1106.T1106} REPORT_NAME = 'new_report' diff --git a/monkey/monkey_island/cc/services/attack/attack_schema.py b/monkey/monkey_island/cc/services/attack/attack_schema.py index 957731251..4a4402dc1 100644 --- a/monkey/monkey_island/cc/services/attack/attack_schema.py +++ b/monkey/monkey_island/cc/services/attack/attack_schema.py @@ -116,6 +116,15 @@ SCHEMA = { "local paths and arbitrary Universal Naming Convention (UNC) network paths.", "depends_on": ["T1078", "T1003"] }, + "T1106": { + "title": "T1106 Execution through API", + "type": "bool", + "value": True, + "necessary": False, + "description": "Adversary tools may directly use the Windows application " + "programming interface (API) to execute binaries.", + "depends_on": ["T1210"] + }, "T1059": { "title": "T1059 Command line interface", "type": "bool", diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1106.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1106.py new file mode 100644 index 000000000..b24d10bd9 --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1106.py @@ -0,0 +1,17 @@ +from monkey_island.cc.database import mongo +from monkey_island.cc.services.attack.technique_reports import AttackTechnique + +__author__ = "VakarisZ" + + +class T1106(AttackTechnique): + tech_id = "T1106" + unscanned_msg = "Monkey didn't try to directly use WinAPI." + scanned_msg = "Monkey tried to use WinAPI, but failed." + used_msg = "Monkey successfully used WinAPI." + + @staticmethod + def get_report_data(): + data = T1106.get_tech_base_data() + data.update({'api_uses': list(mongo.db.telemetry.aggregate(T1106.get_usage_query()))}) + return data diff --git a/monkey/monkey_island/cc/services/config_schema.py b/monkey/monkey_island/cc/services/config_schema.py index 34dea9a14..c79b14eaa 100644 --- a/monkey/monkey_island/cc/services/config_schema.py +++ b/monkey/monkey_island/cc/services/config_schema.py @@ -22,7 +22,7 @@ SCHEMA = { "WmiExploiter" ], "title": "WMI Exploiter", - "attack_techniques": ["T1110"] + "attack_techniques": ["T1110", "T1106"] }, { "type": "string", @@ -54,7 +54,7 @@ SCHEMA = { "SSHExploiter" ], "title": "SSH Exploiter", - "attack_techniques": ["T1110", "T1145"] + "attack_techniques": ["T1110", "T1145", "T1106"] }, { "type": "string", diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1106.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1106.js new file mode 100644 index 000000000..a3210b73c --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1106.js @@ -0,0 +1,30 @@ +import React from 'react'; +import '../../../styles/Collapse.scss' +import ReactTable from "react-table"; +import { getUsageColumns } from "./Helpers" + + +class T1106 extends React.Component { + + constructor(props) { + super(props); + } + + render() { + return ( +
+
{this.props.data.message}
+
+ {this.props.data.api_uses.length !== 0 ? + : ""} +
+ ); + } +} + +export default T1106; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js index 57ac66f87..58e234b82 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js @@ -16,6 +16,7 @@ import T1082 from "../attack/techniques/T1082"; import T1145 from "../attack/techniques/T1145"; import T1035 from "../attack/techniques/T1035"; import T1129 from "../attack/techniques/T1129"; +import T1106 from "../attack/techniques/T1106"; const tech_components = { 'T1210': T1210, @@ -28,7 +29,8 @@ const tech_components = { 'T1082': T1082, 'T1145': T1145, 'T1035': T1035, - 'T1129': T1129 + 'T1129': T1129, + 'T1106': T1106 }; const classNames = require('classnames'); From d880c19910c3ce497f7f4e8c7fe66def2f377a7f Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 16 Jul 2019 10:47:16 +0300 Subject: [PATCH 2/2] PR fixes for t1106 --- monkey/common/utils/attack_utils.py | 3 +++ monkey/infection_monkey/control.py | 1 + monkey/infection_monkey/dropper.py | 5 ++--- monkey/infection_monkey/system_info/mimikatz_collector.py | 3 +-- monkey/infection_monkey/system_singleton.py | 7 ------- monkey/infection_monkey/telemetry/attack/t1106_telem.py | 4 ++-- .../cc/services/attack/technique_reports/T1035.py | 4 +--- .../cc/services/attack/technique_reports/T1106.py | 7 +++---- .../cc/services/attack/technique_reports/T1129.py | 4 +--- .../cc/services/attack/technique_reports/__init__.py | 5 +++++ 10 files changed, 19 insertions(+), 24 deletions(-) diff --git a/monkey/common/utils/attack_utils.py b/monkey/common/utils/attack_utils.py index 3edb0dc28..77d813351 100644 --- a/monkey/common/utils/attack_utils.py +++ b/monkey/common/utils/attack_utils.py @@ -15,6 +15,9 @@ class UsageEnum(Enum): 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 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 e39860d50..230ebc567 100644 --- a/monkey/infection_monkey/dropper.py +++ b/monkey/infection_monkey/dropper.py @@ -15,7 +15,7 @@ from infection_monkey.exploit.tools import build_monkey_commandline_explicitly 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/system_info/mimikatz_collector.py b/monkey/infection_monkey/system_info/mimikatz_collector.py index 61d3a1c56..0e6a913c9 100644 --- a/monkey/infection_monkey/system_info/mimikatz_collector.py +++ b/monkey/infection_monkey/system_info/mimikatz_collector.py @@ -47,7 +47,6 @@ 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)) @@ -56,7 +55,7 @@ class MimikatzCollector(object): except Exception: LOG.exception("Error initializing mimikatz collector") status = ScanStatus.SCANNED - T1106Telem(ScanStatus.SCANNED, "Monkey tried to call WinAPI to load mimikatz.").send() + T1106Telem(status, UsageEnum.MIMIKATZ_WINAPI.name).send() T1129Telem(status, UsageEnum.MIMIKATZ.name).send() 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/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/monkey_island/cc/services/attack/technique_reports/T1035.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py index 4651f3c25..fcc230be5 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py @@ -13,7 +13,5 @@ class T1035(UsageTechnique): @staticmethod def get_report_data(): data = T1035.get_tech_base_data() - services = list(mongo.db.telemetry.aggregate(T1035.get_usage_query())) - services = list(map(T1035.parse_usages, services)) - data.update({'services': services}) + 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 17abd4dd3..f1a4d1b83 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py @@ -13,7 +13,5 @@ class T1129(UsageTechnique): @staticmethod def get_report_data(): data = T1129.get_tech_base_data() - dlls = list(mongo.db.telemetry.aggregate(T1129.get_usage_query())) - dlls = list(map(T1129.parse_usages, dlls)) - data.update({'dlls': dlls}) + 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 9b0c23d50..cc702de62 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -134,6 +134,11 @@ class UsageTechnique(AttackTechnique): "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): """