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 3f86401e7..a8ec2a78c 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).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 4ef764251..0ad0d692f 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, UsageEnum +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,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 + status = ScanStatus.USED except Exception: LOG.exception("Error initializing mimikatz collector") + status = ScanStatus.SCANNED + T1129Telem(status, UsageEnum.MIMIKATZ).send() def get_logon_info(self): """ @@ -67,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): @@ -102,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 3b0846609..4ca9dc93c 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 + :param usage: Enum of UsageEnum type """ - 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..4e7a12ce8 --- /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: 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 new file mode 100644 index 000000000..4b47d8be3 --- /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): + """ + :param technique: Id of technique + :param status: ScanStatus of technique + :param usage: Enum of UsageEnum type + """ + super(UsageTelem, self).__init__(technique, status) + self.usage = usage.name + + 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 dbe778936..11cadb068 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,7 +1,7 @@ import logging from monkey_island.cc.models import Monkey 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, T1107, T1065, T1035 +from monkey_island.cc.services.attack.technique_reports import T1145, T1107, T1065, T1035, T1129 from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.database import mongo @@ -19,9 +19,10 @@ TECHNIQUES = {'T1210': T1210.T1210, 'T1086': T1086.T1086, 'T1082': T1082.T1082, 'T1145': T1145.T1145, + 'T1035': T1035.T1035, + 'T1129': T1129.T1129, 'T1107': T1107.T1107, - 'T1065': T1065.T1065, - 'T1035': T1035.T1035} + 'T1065': T1065.T1065} 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 3cb5e32d1..1e4ba5467 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 = { "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..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,31 +1,19 @@ 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." 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))}) + 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 new file mode 100644 index 000000000..17abd4dd3 --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py @@ -0,0 +1,19 @@ +from monkey_island.cc.database import mongo +from monkey_island.cc.services.attack.technique_reports import UsageTechnique + +__author__ = "VakarisZ" + + +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." + used_msg = "Monkey successfully loaded DLL's using Windows module loader." + + @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}) + 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 3e92417d3..30c73f609 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 """ @@ -112,3 +115,42 @@ class AttackTechnique(object): data = cls.get_message_and_status(status) 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: Usage telemetry that contains fields: {'usage': 'SMB', 'status': 1} + :return: usage string + """ + 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): + """ + :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'}}}, + {"$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 0950b2a63..89721429d 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 @@ -20,6 +20,22 @@ 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.machine), + style: { 'whiteSpace': 'unset' }, + width: 300}, + {Header: 'Usage', + id: 'usage', + accessor: x => x.usage, + style: { 'whiteSpace': 'unset' }}] + }])} + export const ScanStatus = { UNSCANNED: 0, SCANNED: 1, 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 6a1e04c28..05b890c6c 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 @@ -18,6 +18,7 @@ import T1145 from "../attack/techniques/T1145"; import T1107 from "../attack/techniques/T1107"; import T1065 from "../attack/techniques/T1065"; import T1035 from "../attack/techniques/T1035"; +import T1129 from "../attack/techniques/T1129"; const tech_components = { 'T1210': T1210, @@ -29,9 +30,10 @@ const tech_components = { 'T1086': T1086, 'T1082': T1082, 'T1145': T1145, + 'T1035': T1035, + 'T1129': T1129, 'T1107': T1107, - 'T1065': T1065, - 'T1035': T1035 + 'T1065': T1065 }; const classNames = require('classnames');