From d1f8e522664b38031c9ca904715a433a03149d86 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 1 Jul 2019 11:30:49 +0300 Subject: [PATCH 1/5] Implemented execution trough module load attack technique --- .../system_info/mimikatz_collector.py | 5 +++- .../telemetry/attack/t1035_telem.py | 14 ++------- .../telemetry/attack/t1129_telem.py | 11 +++++++ .../telemetry/attack/usage_telem.py | 20 +++++++++++++ .../cc/services/attack/attack_report.py | 7 +++-- .../cc/services/attack/attack_schema.py | 9 ++++++ .../attack/technique_reports/T1035.py | 16 +--------- .../attack/technique_reports/T1129.py | 17 +++++++++++ .../attack/technique_reports/__init__.py | 20 +++++++++++++ .../components/attack/techniques/Helpers.js | 16 ++++++++++ .../src/components/attack/techniques/T1035.js | 18 ++---------- .../src/components/attack/techniques/T1129.js | 29 +++++++++++++++++++ .../report-components/AttackReport.js | 4 ++- 13 files changed, 139 insertions(+), 47 deletions(-) create mode 100644 monkey/infection_monkey/telemetry/attack/t1129_telem.py create mode 100644 monkey/infection_monkey/telemetry/attack/usage_telem.py create mode 100644 monkey/monkey_island/cc/services/attack/technique_reports/T1129.py create mode 100644 monkey/monkey_island/cc/ui/src/components/attack/techniques/T1129.js diff --git a/monkey/infection_monkey/system_info/mimikatz_collector.py b/monkey/infection_monkey/system_info/mimikatz_collector.py index 4ef764251..4f2348531 100644 --- a/monkey/infection_monkey/system_info/mimikatz_collector.py +++ b/monkey/infection_monkey/system_info/mimikatz_collector.py @@ -5,7 +5,8 @@ import socket 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.pyinstaller_utils import get_binary_file_path, get_binaries_dir_path __author__ = 'itay.mizeretz' @@ -49,8 +50,10 @@ class MimikatzCollector(object): 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() except Exception: LOG.exception("Error initializing mimikatz collector") + T1129Telem(ScanStatus.SCANNED, "Monkey tried to load Mimikatz DLL, but failed.").send() def get_logon_info(self): """ diff --git a/monkey/infection_monkey/telemetry/attack/t1035_telem.py b/monkey/infection_monkey/telemetry/attack/t1035_telem.py index 3b0846609..b3d7c90de 100644 --- a/monkey/infection_monkey/telemetry/attack/t1035_telem.py +++ b/monkey/infection_monkey/telemetry/attack/t1035_telem.py @@ -1,19 +1,11 @@ -from infection_monkey.telemetry.attack.attack_telem import AttackTelem +from infection_monkey.telemetry.attack.usage_telem import UsageTelem -class T1035Telem(AttackTelem): +class T1035Telem(UsageTelem): def __init__(self, status, usage): """ T1035 telemetry. :param status: ScanStatus of technique :param usage: Usage string """ - super(T1035Telem, self).__init__('T1035', status) - self.usage = usage - - def get_data(self): - data = super(T1035Telem, self).get_data() - data.update({ - 'usage': self.usage - }) - return data + super(T1035Telem, self).__init__('T1035', status, usage) diff --git a/monkey/infection_monkey/telemetry/attack/t1129_telem.py b/monkey/infection_monkey/telemetry/attack/t1129_telem.py new file mode 100644 index 000000000..fb7d776c6 --- /dev/null +++ b/monkey/infection_monkey/telemetry/attack/t1129_telem.py @@ -0,0 +1,11 @@ +from infection_monkey.telemetry.attack.usage_telem import UsageTelem + + +class T1129Telem(UsageTelem): + def __init__(self, status, usage): + """ + T1129 telemetry. + :param status: ScanStatus of technique + :param usage: Usage string + """ + 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 new file mode 100644 index 000000000..48ff5431c --- /dev/null +++ b/monkey/infection_monkey/telemetry/attack/usage_telem.py @@ -0,0 +1,20 @@ +from infection_monkey.telemetry.attack.attack_telem import AttackTelem + + +class UsageTelem(AttackTelem): + + def __init__(self, technique, status, usage): + """ + T1035 telemetry. + :param status: ScanStatus of technique + :param usage: Usage string + """ + super(UsageTelem, self).__init__(technique, status) + self.usage = usage + + def get_data(self): + data = super(UsageTelem, self).get_data() + data.update({ + 'usage': self.usage + }) + return data diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index d33ad125e..ab5b82245 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 +from monkey_island.cc.services.attack.technique_reports import T1145, T1035, T1129 from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.database import mongo @@ -18,7 +18,8 @@ TECHNIQUES = {'T1210': T1210.T1210, 'T1086': T1086.T1086, 'T1082': T1082.T1082, 'T1145': T1145.T1145, - 'T1035': T1035.T1035} + 'T1035': T1035.T1035, + 'T1129': T1129.T1129} REPORT_NAME = 'new_report' @@ -58,12 +59,12 @@ class AttackReportService: Gets latest report (by retrieving it from db or generating a new one). :return: report dict. """ + return AttackReportService.generate_new_report() if AttackReportService.is_report_generated(): telem_time = AttackReportService.get_latest_attack_telem_time() latest_report = mongo.db.attack_report.find_one({'name': REPORT_NAME}) if telem_time and latest_report['latest_telem_time'] and telem_time == latest_report['latest_telem_time']: return latest_report - return AttackReportService.generate_new_report() @staticmethod def is_report_generated(): diff --git a/monkey/monkey_island/cc/services/attack/attack_schema.py b/monkey/monkey_island/cc/services/attack/attack_schema.py index 9262b7536..957731251 100644 --- a/monkey/monkey_island/cc/services/attack/attack_schema.py +++ b/monkey/monkey_island/cc/services/attack/attack_schema.py @@ -107,6 +107,15 @@ SCHEMA = { "that interacts with Windows services, such as the Service Control Manager.", "depends_on": ["T1210"] }, + "T1129": { + "title": "T1129 Execution through module load", + "type": "bool", + "value": True, + "necessary": False, + "description": "The Windows module loader can be instructed to load DLLs from arbitrary " + "local paths and arbitrary Universal Naming Convention (UNC) network paths.", + "depends_on": ["T1078", "T1003"] + }, "T1059": { "title": "T1059 Command line interface", "type": "bool", 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 4dd2b7652..a651a8288 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py @@ -10,22 +10,8 @@ class T1035(AttackTechnique): scanned_msg = "Monkey tried to interact with Windows services, but failed." used_msg = "Monkey successfully interacted with Windows services." - query = [{'$match': {'telem_category': 'attack', - 'data.technique': tech_id}}, - {'$lookup': {'from': 'monkey', - 'localField': 'monkey_guid', - 'foreignField': 'guid', - 'as': 'monkey'}}, - {'$project': {'monkey': {'$arrayElemAt': ['$monkey', 0]}, - 'status': '$data.status', - 'usage': '$data.usage'}}, - {'$addFields': {'_id': 0, - 'machine': {'hostname': '$monkey.hostname', 'ips': '$monkey.ip_addresses'}, - 'monkey': 0}}, - {'$group': {'_id': {'machine': '$machine', 'status': '$status', 'usage': '$usage'}}}] - @staticmethod def get_report_data(): data = T1035.get_tech_base_data() - data.update({'services': list(mongo.db.telemetry.aggregate(T1035.query))}) + data.update({'services': list(mongo.db.telemetry.aggregate(T1035.get_usage_query()))}) 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 new file mode 100644 index 000000000..7d9fa9dd0 --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1129.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 T1129(AttackTechnique): + tech_id = "T1129" + unscanned_msg = "Monkey didn't try to load any DLL's." + scanned_msg = "Monkey tried to load DLL's, but failed." + used_msg = "Monkey successfully loaded DLL's using Windows module loader." + + @staticmethod + def get_report_data(): + data = T1129.get_tech_base_data() + data.update({'dlls': list(mongo.db.telemetry.aggregate(T1129.get_usage_query()))}) + 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 81b7dd6bf..b3b3ce77b 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -112,3 +112,23 @@ class AttackTechnique(object): data = cls.get_message_and_status(status) data.update({'title': cls.technique_title()}) return data + + @classmethod + def get_usage_query(cls): + """ + :return: Query that parses attack telems for simple report component + (gets machines and attack technique usage). + """ + return [{'$match': {'telem_category': 'attack', + 'data.technique': cls.tech_id}}, + {'$lookup': {'from': 'monkey', + 'localField': 'monkey_guid', + 'foreignField': 'guid', + 'as': 'monkey'}}, + {'$project': {'monkey': {'$arrayElemAt': ['$monkey', 0]}, + 'status': '$data.status', + 'usage': '$data.usage'}}, + {'$addFields': {'_id': 0, + 'machine': {'hostname': '$monkey.hostname', 'ips': '$monkey.ip_addresses'}, + 'monkey': 0}}, + {'$group': {'_id': {'machine': '$machine', 'status': '$status', 'usage': '$usage'}}}] 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 1060f4b2d..fad2087d9 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 @@ -19,3 +19,19 @@ export function renderMachineFromSystemData(data) { }); return machineStr + ")" } + +/* Formats telemetry data that contains _id.machine and _id.usage fields into columns + for react table. */ +export function getUsageColumns() { + return ([{ + columns: [ + {Header: 'Machine', + id: 'machine', + accessor: x => renderMachineFromSystemData(x._id.machine), + style: { 'whiteSpace': 'unset' }, + width: 300}, + {Header: 'Usage', + id: 'usage', + accessor: x => x._id.usage, + style: { 'whiteSpace': 'unset' }}] + }])} diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1035.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1035.js index b760226b8..7345ca497 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1035.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1035.js @@ -1,7 +1,7 @@ import React from 'react'; import '../../../styles/Collapse.scss' import ReactTable from "react-table"; -import { renderMachineFromSystemData } from "./Helpers" +import { getUsageColumns } from "./Helpers" class T1035 extends React.Component { @@ -10,20 +10,6 @@ class T1035 extends React.Component { super(props); } - static getServiceColumns() { - return ([{ - columns: [ - {Header: 'Machine', - id: 'machine', - accessor: x => renderMachineFromSystemData(x._id.machine), - style: { 'whiteSpace': 'unset' }, - width: 300}, - {Header: 'Usage', - id: 'usage', - accessor: x => x._id.usage, - style: { 'whiteSpace': 'unset' }}] - }])}; - render() { return (
@@ -31,7 +17,7 @@ class T1035 extends React.Component {
{this.props.data.services.length !== 0 ? +
{this.props.data.message}
+
+ {this.props.data.dlls.length !== 0 ? + : ""} +
+ ); + } +} + +export default T1129; 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 a18754f1d..57ac66f87 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 @@ -15,6 +15,7 @@ import T1086 from "../attack/techniques/T1086"; import T1082 from "../attack/techniques/T1082"; import T1145 from "../attack/techniques/T1145"; import T1035 from "../attack/techniques/T1035"; +import T1129 from "../attack/techniques/T1129"; const tech_components = { 'T1210': T1210, @@ -26,7 +27,8 @@ const tech_components = { 'T1086': T1086, 'T1082': T1082, 'T1145': T1145, - 'T1035': T1035 + 'T1035': T1035, + 'T1129': T1129 }; const classNames = require('classnames'); From 25efdef7d3359b28c0a20b613514227bb12a89fc Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 8 Jul 2019 17:50:48 +0300 Subject: [PATCH 2/5] Changes report components to use ScanStatus value --- .../cc/services/attack/technique_reports/T1003.py | 4 ++-- .../cc/services/attack/technique_reports/T1059.py | 4 ++-- .../cc/services/attack/technique_reports/T1065.py | 2 +- .../cc/services/attack/technique_reports/T1075.py | 6 +++--- .../cc/services/attack/technique_reports/T1082.py | 4 ++-- .../cc/services/attack/technique_reports/T1086.py | 4 ++-- .../cc/services/attack/technique_reports/T1110.py | 6 +++--- .../cc/services/attack/technique_reports/T1145.py | 4 ++-- .../cc/services/attack/technique_reports/T1210.py | 6 +++--- .../cc/services/attack/technique_reports/__init__.py | 8 ++++---- 10 files changed, 24 insertions(+), 24 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py index a92758cbc..d9aaeaa47 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -20,8 +20,8 @@ class T1003(AttackTechnique): def get_report_data(): data = {'title': T1003.technique_title()} if mongo.db.telemetry.count_documents(T1003.query): - status = ScanStatus.USED + status = ScanStatus.USED.value else: - status = ScanStatus.UNSCANNED + status = ScanStatus.UNSCANNED.value data.update(T1003.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py index 328c11112..ef15dd9fd 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -27,8 +27,8 @@ class T1059(AttackTechnique): cmd_data = list(mongo.db.telemetry.aggregate(T1059.query)) data = {'title': T1059.technique_title(), 'cmds': cmd_data} if cmd_data: - status = ScanStatus.USED + status = ScanStatus.USED.value else: - status = ScanStatus.UNSCANNED + status = ScanStatus.UNSCANNED.value data.update(T1059.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py index fd34e80e9..7d8ceb93e 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py @@ -17,4 +17,4 @@ class T1065(AttackTechnique): def get_report_data(): port = ConfigService.get_config_value(['cnc', 'servers', 'current_server']).split(':')[1] T1065.used_msg = T1065.message % port - return T1065.get_base_data_by_status(ScanStatus.USED) + return T1065.get_base_data_by_status(ScanStatus.USED.value) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py index fa65a66c2..623d157ae 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py @@ -35,10 +35,10 @@ class T1075(AttackTechnique): successful_logins = list(mongo.db.telemetry.aggregate(T1075.query)) data.update({'successful_logins': successful_logins}) if successful_logins: - status = ScanStatus.USED + status = ScanStatus.USED.value elif mongo.db.telemetry.count_documents(T1075.login_attempt_query): - status = ScanStatus.SCANNED + status = ScanStatus.SCANNED.value else: - status = ScanStatus.UNSCANNED + status = ScanStatus.UNSCANNED.value data.update(T1075.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py index 79020c048..f59b63286 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py @@ -40,8 +40,8 @@ class T1082(AttackTechnique): system_info = list(mongo.db.telemetry.aggregate(T1082.query)) data.update({'system_info': system_info}) if system_info: - status = ScanStatus.USED + status = ScanStatus.USED.value else: - status = ScanStatus.UNSCANNED + status = ScanStatus.UNSCANNED.value data.update(T1082.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py index 4114047c5..dd5d64d25 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py @@ -29,8 +29,8 @@ class T1086(AttackTechnique): cmd_data = list(mongo.db.telemetry.aggregate(T1086.query)) data = {'title': T1086.technique_title(), 'cmds': cmd_data} if cmd_data: - status = ScanStatus.USED + status = ScanStatus.USED.value else: - status = ScanStatus.UNSCANNED + status = ScanStatus.UNSCANNED.value data.update(T1086.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py index 91d785bc3..b918de7f4 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py @@ -35,11 +35,11 @@ class T1110(AttackTechnique): result['successful_creds'].append(T1110.parse_creds(attempt)) if succeeded: - status = ScanStatus.USED + status = ScanStatus.USED.value elif attempts: - status = ScanStatus.SCANNED + status = ScanStatus.SCANNED.value else: - status = ScanStatus.UNSCANNED + status = ScanStatus.UNSCANNED.value data = T1110.get_base_data_by_status(status) # Remove data with no successful brute force attempts attempts = [attempt for attempt in attempts if attempt['attempts']] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py index 9b525873f..89ac44117 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py @@ -23,9 +23,9 @@ class T1145(AttackTechnique): ssh_info = list(mongo.db.telemetry.aggregate(T1145.query)) if ssh_info: - status = ScanStatus.USED + status = ScanStatus.USED.value else: - status = ScanStatus.UNSCANNED + status = ScanStatus.UNSCANNED.value data = T1145.get_base_data_by_status(status) data.update({'ssh_info': ssh_info}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py index 6e89bc6ab..eeae183f5 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py @@ -18,11 +18,11 @@ class T1210(AttackTechnique): scanned_services = T1210.get_scanned_services() exploited_services = T1210.get_exploited_services() if exploited_services: - status = ScanStatus.USED + status = ScanStatus.USED.value elif scanned_services: - status = ScanStatus.SCANNED + status = ScanStatus.SCANNED.value else: - status = ScanStatus.UNSCANNED + status = ScanStatus.UNSCANNED.value data.update(T1210.get_message_and_status(status)) data.update({'scanned_services': scanned_services, 'exploited_services': exploited_services}) 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 d765b5f09..a18444b4e 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -67,7 +67,7 @@ class AttackTechnique(object): def get_message_and_status(cls, status): """ Returns a dict with attack technique's message and status. - :param status: Enum type value from common/attack_utils.py + :param status: Enum from common/attack_utils.py integer value :return: Dict with message and status """ return {'message': cls.get_message_by_status(status), 'status': status.value} @@ -76,12 +76,12 @@ class AttackTechnique(object): def get_message_by_status(cls, status): """ Picks a message to return based on status. - :param status: Enum type value from common/attack_utils.py + :param status: Enum from common/attack_utils.py integer value :return: message string """ - if status == ScanStatus.UNSCANNED: + if status == ScanStatus.UNSCANNED.value: return cls.unscanned_msg - elif status == ScanStatus.SCANNED: + elif status == ScanStatus.SCANNED.value: return cls.scanned_msg else: return cls.used_msg From 30c7b99e95ab7631f3f0777882bf90221b2f3da8 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 15 Jul 2019 15:58:22 +0300 Subject: [PATCH 3/5] PR fixes --- monkey/common/utils/attack_utils.py | 7 +++++ monkey/infection_monkey/exploit/smbexec.py | 8 +++--- .../system_info/mimikatz_collector.py | 11 ++++---- .../telemetry/attack/t1035_telem.py | 2 +- .../telemetry/attack/t1129_telem.py | 2 +- .../telemetry/attack/usage_telem.py | 4 +-- .../attack/technique_reports/T1035.py | 8 +++--- .../attack/technique_reports/T1129.py | 8 +++--- .../attack/technique_reports/__init__.py | 26 +++++++++++++++++-- .../components/attack/techniques/Helpers.js | 4 +-- 10 files changed, 57 insertions(+), 23 deletions(-) diff --git a/monkey/common/utils/attack_utils.py b/monkey/common/utils/attack_utils.py index cb3c8f029..3edb0dc28 100644 --- a/monkey/common/utils/attack_utils.py +++ b/monkey/common/utils/attack_utils.py @@ -10,6 +10,13 @@ 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."} + + # 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/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index 6f1667c64..74354108e 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -11,7 +11,7 @@ from infection_monkey.network.tools import check_tcp_port from infection_monkey.exploit.tools import build_monkey_commandline 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 4f2348531..47fa0d431 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.pyinstaller_utils import get_binary_file_path, get_binaries_dir_path @@ -50,10 +50,11 @@ class MimikatzCollector(object): 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() + status = ScanStatus.SCANNED + T1129Telem(status, UsageEnum.MIMIKATZ.name).send() def get_logon_info(self): """ @@ -70,7 +71,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 +106,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/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/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..4651f3c25 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,7 @@ 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()))}) + services = list(mongo.db.telemetry.aggregate(T1035.get_usage_query())) + services = list(map(T1035.parse_usages, services)) + data.update({'services': services}) 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..17abd4dd3 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,7 @@ 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()))}) + dlls = list(mongo.db.telemetry.aggregate(T1129.get_usage_query())) + dlls = list(map(T1129.parse_usages, dlls)) + data.update({'dlls': dlls}) 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..9b0c23d50 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,24 @@ 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_query(cls): """ @@ -131,4 +152,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 775d453da..9cb0de635 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' }}] }])} From 32e930559d02412e353f0a257081bb0704106c1a Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 1 Aug 2019 10:09:40 +0300 Subject: [PATCH 4/5] Improved docs of "parse_usages" method --- .../cc/services/attack/technique_reports/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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..30c73f609 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -124,8 +124,8 @@ class UsageTechnique(AttackTechnique): def parse_usages(usage): """ Parses data from database and translates usage enums into strings - :param usage: - :return: + :param usage: Usage telemetry that contains fields: {'usage': 'SMB', 'status': 1} + :return: usage string """ try: usage['usage'] = UsageEnum[usage['usage']].value[usage['status']] From 7470427feb36cda0ab1e6185b46800e0abdcbcef Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 1 Aug 2019 14:26:01 +0300 Subject: [PATCH 5/5] Changed UsageTelemetry calls to no longer require .name when specifying usage enum --- monkey/infection_monkey/exploit/smbexec.py | 2 +- monkey/infection_monkey/system_info/mimikatz_collector.py | 2 +- monkey/infection_monkey/telemetry/attack/t1035_telem.py | 2 +- monkey/infection_monkey/telemetry/attack/t1129_telem.py | 2 +- monkey/infection_monkey/telemetry/attack/usage_telem.py | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index 9d96b355b..a8ec2a78c 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -137,7 +137,7 @@ class SmbExploiter(HostExploiter): except: status = ScanStatus.SCANNED pass - T1035Telem(status, UsageEnum.SMB.name).send() + T1035Telem(status, UsageEnum.SMB).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 47fa0d431..0ad0d692f 100644 --- a/monkey/infection_monkey/system_info/mimikatz_collector.py +++ b/monkey/infection_monkey/system_info/mimikatz_collector.py @@ -54,7 +54,7 @@ class MimikatzCollector(object): except Exception: LOG.exception("Error initializing mimikatz collector") status = ScanStatus.SCANNED - T1129Telem(status, UsageEnum.MIMIKATZ.name).send() + T1129Telem(status, UsageEnum.MIMIKATZ).send() def get_logon_info(self): """ diff --git a/monkey/infection_monkey/telemetry/attack/t1035_telem.py b/monkey/infection_monkey/telemetry/attack/t1035_telem.py index 13d0bcc59..4ca9dc93c 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: Enum name of UsageEnum + :param usage: Enum of UsageEnum type """ super(T1035Telem, self).__init__('T1035', status, usage) diff --git a/monkey/infection_monkey/telemetry/attack/t1129_telem.py b/monkey/infection_monkey/telemetry/attack/t1129_telem.py index a04834959..4e7a12ce8 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: Enum name of UsageEnum + :param usage: Enum of UsageEnum type """ 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 d493c64d8..4b47d8be3 100644 --- a/monkey/infection_monkey/telemetry/attack/usage_telem.py +++ b/monkey/infection_monkey/telemetry/attack/usage_telem.py @@ -7,10 +7,10 @@ class UsageTelem(AttackTelem): """ :param technique: Id of technique :param status: ScanStatus of technique - :param usage: Enum name of UsageEnum + :param usage: Enum of UsageEnum type """ super(UsageTelem, self).__init__(technique, status) - self.usage = usage + self.usage = usage.name def get_data(self): data = super(UsageTelem, self).get_data()