diff --git a/monkey/monkey_island/cc/models/monkey.py b/monkey/monkey_island/cc/models/monkey.py index f1bd12905..5b2fff620 100644 --- a/monkey/monkey_island/cc/models/monkey.py +++ b/monkey/monkey_island/cc/models/monkey.py @@ -80,6 +80,17 @@ class Monkey(Document): os = "windows" return os + def get_network_info(self): + """ + Formats network info from monkey's model + :return: dictionary with an array of IP's and a hostname + """ + return {'ips': self.ip_addresses, 'hostname': self.hostname} + + @staticmethod + def get_tunneled_monkeys(): + return Monkey.objects(tunnel__exists=True) + def renew_ttl(self, duration=DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS): self.ttl_ref = create_monkey_ttl_document(duration) self.save() diff --git a/monkey/monkey_island/cc/models/test_monkey.py b/monkey/monkey_island/cc/models/test_monkey.py index 717fb309a..6115386ea 100644 --- a/monkey/monkey_island/cc/models/test_monkey.py +++ b/monkey/monkey_island/cc/models/test_monkey.py @@ -9,11 +9,11 @@ from monkey_ttl import MonkeyTtl class TestMonkey(IslandTestCase): """ - Make sure to set server environment to `testing` in server.json! Otherwise this will mess up your mongo instance and + Make sure to set server environment to `testing` in server_config.json! Otherwise this will mess up your mongo instance and won't work. Also, the working directory needs to be the working directory from which you usually run the island so the - server.json file is found and loaded. + server_config.json file is found and loaded. """ def test_is_dead(self): @@ -90,3 +90,25 @@ class TestMonkey(IslandTestCase): self.assertEquals(1, len(filter(lambda m: m.get_os() == "windows", Monkey.objects()))) self.assertEquals(1, len(filter(lambda m: m.get_os() == "linux", Monkey.objects()))) self.assertEquals(1, len(filter(lambda m: m.get_os() == "unknown", Monkey.objects()))) + + def test_get_tunneled_monkeys(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + + linux_monkey = Monkey(guid=str(uuid.uuid4()), + description="Linux shay-Virtual-Machine") + windows_monkey = Monkey(guid=str(uuid.uuid4()), + description="Windows bla bla bla", + tunnel=linux_monkey) + unknown_monkey = Monkey(guid=str(uuid.uuid4()), + description="bla bla bla", + tunnel=windows_monkey) + linux_monkey.save() + windows_monkey.save() + unknown_monkey.save() + tunneled_monkeys = Monkey.get_tunneled_monkeys() + test = bool(windows_monkey in tunneled_monkeys + and unknown_monkey in tunneled_monkeys + and linux_monkey not in tunneled_monkeys + and len(tunneled_monkeys) == 2) + self.assertTrue(test, "Tunneling test") diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 1711059a0..719463ba5 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,7 +1,8 @@ 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, T1105, T1065, T1035, T1129, T1106, T1107 +from monkey_island.cc.services.attack.technique_reports import T1145, T1105, T1065, T1035, T1129, T1106, T1107, T1188 +from monkey_island.cc.services.attack.technique_reports import T1090 from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.database import mongo @@ -24,7 +25,9 @@ TECHNIQUES = {'T1210': T1210.T1210, 'T1035': T1035.T1035, 'T1129': T1129.T1129, 'T1106': T1106.T1106, - 'T1107': T1107.T1107} + 'T1107': T1107.T1107, + 'T1188': T1188.T1188, + 'T1090': T1090.T1090} 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 363541fdd..891db84e8 100644 --- a/monkey/monkey_island/cc/services/attack/attack_schema.py +++ b/monkey/monkey_island/cc/services/attack/attack_schema.py @@ -186,6 +186,22 @@ SCHEMA = { "necessary": True, "description": "Adversaries may conduct C2 communications over a non-standard " "port to bypass proxies and firewalls that have been improperly configured." + }, + "T1090": { + "title": "T1090 Connection proxy", + "type": "bool", + "value": True, + "necessary": True, + "description": "A connection proxy is used to direct network traffic between systems " + "or act as an intermediary for network communications." + }, + "T1188": { + "title": "T1188 Multi-hop proxy", + "type": "bool", + "value": True, + "necessary": True, + "description": "To disguise the source of malicious traffic, " + "adversaries may chain together multiple proxies." } } }, diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py new file mode 100644 index 000000000..f0835aff9 --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py @@ -0,0 +1,24 @@ +from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from common.utils.attack_utils import ScanStatus +from monkey_island.cc.models import Monkey + +__author__ = "VakarisZ" + + +class T1090(AttackTechnique): + + tech_id = "T1090" + unscanned_msg = "Monkey didn't use connection proxy." + scanned_msg = "" + used_msg = "Monkey used connection proxy." + + @staticmethod + def get_report_data(): + monkeys = Monkey.get_tunneled_monkeys() + monkeys = [monkey.get_network_info() for monkey in monkeys] + status = ScanStatus.USED.value if monkeys else ScanStatus.UNSCANNED.value + data = T1090.get_base_data_by_status(status) + data.update({'proxies': monkeys}) + return data + + diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py new file mode 100644 index 000000000..32187696a --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py @@ -0,0 +1,32 @@ +from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from monkey_island.cc.models.monkey import Monkey +from common.utils.attack_utils import ScanStatus + +__author__ = "VakarisZ" + + +class T1188(AttackTechnique): + + tech_id = "T1188" + unscanned_msg = "Monkey didn't use multi-hop proxy." + scanned_msg = "" + used_msg = "Monkey used multi-hop proxy." + + @staticmethod + def get_report_data(): + monkeys = Monkey.get_tunneled_monkeys() + hops = [] + for monkey in monkeys: + proxy_count = 0 + proxy = initial = monkey + while proxy.tunnel: + proxy_count += 1 + proxy = proxy.tunnel + if proxy_count > 1: + hops.append({'from': initial.get_network_info(), + 'to': proxy.get_network_info(), + 'count': proxy_count}) + status = ScanStatus.USED.value if hops else ScanStatus.UNSCANNED.value + data = T1188.get_base_data_by_status(status) + data.update({'hops': hops}) + return data 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 89721429d..07f289150 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 @@ -12,12 +12,13 @@ export function renderMachineFromSystemData(data) { let machineStr = data['hostname'] + " ( "; data['ips'].forEach(function(ipInfo){ if(typeof ipInfo === "object"){ - machineStr += ipInfo['addr'] + " "; + machineStr += ipInfo['addr'] + ", "; } else { - machineStr += ipInfo + " "; + machineStr += ipInfo + ", "; } }); - return machineStr + ")" + // Replaces " ," with " )" to finish a list of IP's + return machineStr.slice(0, -2) + " )" } /* Formats telemetry data that contains _id.machine and _id.usage fields into columns diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1090.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1090.js new file mode 100644 index 000000000..d5fed289f --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1090.js @@ -0,0 +1,42 @@ +import React from 'react'; +import '../../../styles/Collapse.scss' +import ReactTable from "react-table"; +import { renderMachineFromSystemData, scanStatus } from "./Helpers" + + +class T1090 extends React.Component { + + constructor(props) { + super(props); + } + + static getProxyColumns() { + return ([{ + columns: [ + {Header: 'Machines', + id: 'machine', + accessor: x => renderMachineFromSystemData(x), + style: { 'whiteSpace': 'unset', textAlign: 'center' }}]}]) + }; + + render() { + return ( +
+
{this.props.data.message}
+
+ {this.props.data.status === scanStatus.USED ? +
+

Proxies were used to communicate with:

+ +
: ""} +
+ ); + } +} + +export default T1090; diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1188.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1188.js new file mode 100644 index 000000000..c28a8092c --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1188.js @@ -0,0 +1,49 @@ +import React from 'react'; +import '../../../styles/Collapse.scss' +import ReactTable from "react-table"; +import { renderMachineFromSystemData, scanStatus } from "./Helpers" + + +class T1188 extends React.Component { + + constructor(props) { + super(props); + } + + static getHopColumns() { + return ([{ + Header: "Communications through multi-hop proxies", + columns: [ + {Header: 'From', + id: 'from', + accessor: x => renderMachineFromSystemData(x.from), + style: { 'whiteSpace': 'unset' }}, + {Header: 'To', + id: 'to', + accessor: x => renderMachineFromSystemData(x.to), + style: { 'whiteSpace': 'unset' }}, + {Header: 'Hops', + id: 'hops', + accessor: x => x.count, + style: { 'whiteSpace': 'unset' }}, + ] + }])}; + + render() { + return ( +
+
{this.props.data.message}
+
+ {this.props.data.status === scanStatus.USED ? + : ""} +
+ ); + } +} + +export default T1188; 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 c26eb7110..5eab45bf4 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 @@ -21,6 +21,8 @@ import T1065 from "../attack/techniques/T1065"; import T1035 from "../attack/techniques/T1035"; import T1129 from "../attack/techniques/T1129"; import T1106 from "../attack/techniques/T1106"; +import T1188 from "../attack/techniques/T1188"; +import T1090 from "../attack/techniques/T1090"; const tech_components = { 'T1210': T1210, @@ -37,7 +39,9 @@ const tech_components = { 'T1035': T1035, 'T1129': T1129, 'T1106': T1106, - 'T1107': T1107 + 'T1107': T1107, + 'T1188': T1188, + 'T1090': T1090 }; const classNames = require('classnames');