diff --git a/.travis.yml b/.travis.yml index 5228275d4..d1178458b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -83,7 +83,7 @@ script: - cd monkey_island/cc/ui - npm ci # See https://docs.npmjs.com/cli/ci.html - eslint ./src --quiet # Test for errors -- JS_WARNINGS_AMOUNT_UPPER_LIMIT=70 +- JS_WARNINGS_AMOUNT_UPPER_LIMIT=28 - eslint ./src --max-warnings $JS_WARNINGS_AMOUNT_UPPER_LIMIT # Test for max warnings # Build documentation diff --git a/docs/content/reference/exploiters/WMIExec.md b/docs/content/reference/exploiters/WMIExec.md index 490c33fcf..346bc6eed 100644 --- a/docs/content/reference/exploiters/WMIExec.md +++ b/docs/content/reference/exploiters/WMIExec.md @@ -5,4 +5,4 @@ draft: false tags: ["exploit", "windows"] --- -Brute forces WMI (Windows Management Instrumentation) using credentials provided by user ((see ["Configuration"](../usage/configuration))) and hashes gathered by mimikatz. +Brute forces WMI (Windows Management Instrumentation) using credentials provided by user (see ["Configuration"](../usage/configuration)) and hashes gathered by mimikatz. diff --git a/monkey/common/data/system_info_collectors_names.py b/monkey/common/data/system_info_collectors_names.py index 831bbe142..175a054e1 100644 --- a/monkey/common/data/system_info_collectors_names.py +++ b/monkey/common/data/system_info_collectors_names.py @@ -2,3 +2,5 @@ AWS_COLLECTOR = "AwsCollector" HOSTNAME_COLLECTOR = "HostnameCollector" ENVIRONMENT_COLLECTOR = "EnvironmentCollector" PROCESS_LIST_COLLECTOR = "ProcessListCollector" +MIMIKATZ_COLLECTOR = "MimikatzCollector" +AZURE_CRED_COLLECTOR = "AzureCollector" diff --git a/monkey/common/data/validation_formats.py b/monkey/common/data/validation_formats.py new file mode 100644 index 000000000..2f04dbe21 --- /dev/null +++ b/monkey/common/data/validation_formats.py @@ -0,0 +1,3 @@ +# Defined in UI on ValidationFormats.js +IP_RANGE = "ip-range" +IP = "ip" diff --git a/monkey/common/utils/attack_utils.py b/monkey/common/utils/attack_utils.py index 708bc8f3c..0eadbedcc 100644 --- a/monkey/common/utils/attack_utils.py +++ b/monkey/common/utils/attack_utils.py @@ -8,6 +8,8 @@ class ScanStatus(Enum): SCANNED = 1 # Technique was attempted and succeeded USED = 2 + # Techique was disabled + DISABLED = 3 class UsageEnum(Enum): diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py index 910b0abd7..1fbcb876b 100644 --- a/monkey/infection_monkey/config.py +++ b/monkey/infection_monkey/config.py @@ -129,13 +129,13 @@ class Configuration(object): finger_classes = [] exploiter_classes = [] - system_info_collectors_classes = [] + system_info_collector_classes = [] # how many victims to look for in a single scan iteration victims_max_find = 100 # how many victims to exploit before stopping - victims_max_exploit = 15 + victims_max_exploit = 100 # depth of propagation depth = 2 @@ -267,16 +267,6 @@ class Configuration(object): # Shares to not check if they're writable. sambacry_shares_not_to_check = ["IPC$", "print$"] - # system info collection - collect_system_info = True - should_use_mimikatz = True - - ########################### - # systeminfo config - ########################### - - extract_azure_creds = True - ########################### # post breach actions ########################### diff --git a/monkey/infection_monkey/example.conf b/monkey/infection_monkey/example.conf index 4fb0200c8..cf9d2ed70 100644 --- a/monkey/infection_monkey/example.conf +++ b/monkey/infection_monkey/example.conf @@ -99,7 +99,7 @@ ], "timeout_between_iterations": 10, "use_file_logging": true, - "victims_max_exploit": 15, + "victims_max_exploit": 100, "victims_max_find": 100, "post_breach_actions": [] custom_PBA_linux_cmd = "" diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index ec63fd6a8..444bde452 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -190,23 +190,23 @@ class InfectionMonkey(object): if self._default_server: if self._network.on_island(self._default_server): machine.set_default_server(get_interface_to_target(machine.ip_addr) + - (':' + self._default_server_port if self._default_server_port else '')) + ( + ':' + self._default_server_port if self._default_server_port else '')) else: machine.set_default_server(self._default_server) LOG.debug("Default server for machine: %r set to %s" % (machine, machine.default_server)) # Order exploits according to their type - if WormConfiguration.should_exploit: - self._exploiters = sorted(self._exploiters, key=lambda exploiter_: exploiter_.EXPLOIT_TYPE.value) - host_exploited = False - for exploiter in [exploiter(machine) for exploiter in self._exploiters]: - if self.try_exploiting(machine, exploiter): - host_exploited = True - VictimHostTelem('T1210', ScanStatus.USED, machine=machine).send() - break - if not host_exploited: - self._fail_exploitation_machines.add(machine) - VictimHostTelem('T1210', ScanStatus.SCANNED, machine=machine).send() + self._exploiters = sorted(self._exploiters, key=lambda exploiter_: exploiter_.EXPLOIT_TYPE.value) + host_exploited = False + for exploiter in [exploiter(machine) for exploiter in self._exploiters]: + if self.try_exploiting(machine, exploiter): + host_exploited = True + VictimHostTelem('T1210', ScanStatus.USED, machine=machine).send() + break + if not host_exploited: + self._fail_exploitation_machines.add(machine) + VictimHostTelem('T1210', ScanStatus.SCANNED, machine=machine).send() if not self._keep_running: break @@ -242,11 +242,10 @@ class InfectionMonkey(object): LOG.debug("Running with depth: %d" % WormConfiguration.depth) def collect_system_info_if_configured(self): - if WormConfiguration.collect_system_info: - LOG.debug("Calling system info collection") - system_info_collector = SystemInfoCollector() - system_info = system_info_collector.get_info() - SystemInfoTelem(system_info).send() + LOG.debug("Calling system info collection") + system_info_collector = SystemInfoCollector() + system_info = system_info_collector.get_info() + SystemInfoTelem(system_info).send() def shutdown_by_not_alive_config(self): if not WormConfiguration.alive: @@ -387,7 +386,8 @@ class InfectionMonkey(object): :raises PlannedShutdownException if couldn't find the server. """ if not ControlClient.find_server(default_tunnel=self._default_tunnel): - raise PlannedShutdownException("Monkey couldn't find server with {} default tunnel.".format(self._default_tunnel)) + raise PlannedShutdownException( + "Monkey couldn't find server with {} default tunnel.".format(self._default_tunnel)) self._default_server = WormConfiguration.current_server LOG.debug("default server set to: %s" % self._default_server) diff --git a/monkey/infection_monkey/system_info/__init__.py b/monkey/infection_monkey/system_info/__init__.py index 7761a412b..05bb3a4d0 100644 --- a/monkey/infection_monkey/system_info/__init__.py +++ b/monkey/infection_monkey/system_info/__init__.py @@ -4,6 +4,7 @@ from enum import IntEnum import psutil +from common.data.system_info_collectors_names import AZURE_CRED_COLLECTOR from infection_monkey.network.info import get_host_subnets from infection_monkey.system_info.azure_cred_collector import AzureCollector from infection_monkey.system_info.netstat_collector import NetstatCollector @@ -91,7 +92,7 @@ class InfoCollector(object): # noinspection PyBroadException try: from infection_monkey.config import WormConfiguration - if not WormConfiguration.extract_azure_creds: + if AZURE_CRED_COLLECTOR not in WormConfiguration.system_info_collector_classes: return LOG.debug("Harvesting creds if on an Azure machine") azure_collector = AzureCollector() diff --git a/monkey/infection_monkey/system_info/system_info_collector.py b/monkey/infection_monkey/system_info/system_info_collector.py index a16560930..ee4bb21e8 100644 --- a/monkey/infection_monkey/system_info/system_info_collector.py +++ b/monkey/infection_monkey/system_info/system_info_collector.py @@ -19,7 +19,7 @@ class SystemInfoCollector(Plugin, metaclass=ABCMeta): @staticmethod def should_run(class_name) -> bool: - return class_name in WormConfiguration.system_info_collectors_classes + return class_name in WormConfiguration.system_info_collector_classes @staticmethod def base_package_file(): diff --git a/monkey/infection_monkey/system_info/windows_info_collector.py b/monkey/infection_monkey/system_info/windows_info_collector.py index 7eb80377d..d6b3cbec8 100644 --- a/monkey/infection_monkey/system_info/windows_info_collector.py +++ b/monkey/infection_monkey/system_info/windows_info_collector.py @@ -2,6 +2,7 @@ import logging import os import sys +from common.data.system_info_collectors_names import MIMIKATZ_COLLECTOR from infection_monkey.system_info.windows_cred_collector.mimikatz_cred_collector import \ MimikatzCredentialCollector @@ -44,7 +45,7 @@ class WindowsInfoCollector(InfoCollector): # TODO: Think about returning self.get_wmi_info() self.get_installed_packages() from infection_monkey.config import WormConfiguration - if WormConfiguration.should_use_mimikatz: + if MIMIKATZ_COLLECTOR in WormConfiguration.system_info_collector_classes: self.get_mimikatz_info() return self.info 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 f5844e2c0..d8ee9de26 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -18,11 +18,17 @@ class T1003(AttackTechnique): @staticmethod def get_report_data(): + @T1003.is_status_disabled + def get_technique_status_and_data(): + if mongo.db.telemetry.count_documents(T1003.query): + status = ScanStatus.USED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, []) + data = {'title': T1003.technique_title()} - if mongo.db.telemetry.count_documents(T1003.query): - status = ScanStatus.USED.value - else: - status = ScanStatus.UNSCANNED.value + status, _ = get_technique_status_and_data() + data.update(T1003.get_message_and_status(status)) data.update(T1003.get_mitigation_by_status(status)) data['stolen_creds'] = ReportService.get_stolen_creds() diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py index cfda065f1..a1162b109 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py @@ -27,8 +27,14 @@ class T1016(AttackTechnique): @staticmethod def get_report_data(): - network_info = list(mongo.db.telemetry.aggregate(T1016.query)) - status = ScanStatus.USED.value if network_info else ScanStatus.UNSCANNED.value + @T1016.is_status_disabled + def get_technique_status_and_data(): + network_info = list(mongo.db.telemetry.aggregate(T1016.query)) + status = ScanStatus.USED.value if network_info else ScanStatus.UNSCANNED.value + return (status, network_info) + + status, network_info = get_technique_status_and_data() + data = T1016.get_base_data_by_status(status) data.update({'network_info': network_info}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py index 65972265d..3ea49603c 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py @@ -28,11 +28,17 @@ class T1018(AttackTechnique): @staticmethod def get_report_data(): - scan_info = list(mongo.db.telemetry.aggregate(T1018.query)) - if scan_info: - status = ScanStatus.USED.value - else: - status = ScanStatus.UNSCANNED.value + @T1018.is_status_disabled + def get_technique_status_and_data(): + scan_info = list(mongo.db.telemetry.aggregate(T1018.query)) + if scan_info: + status = ScanStatus.USED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, scan_info) + + status, scan_info = get_technique_status_and_data() + data = T1018.get_base_data_by_status(status) data.update({'scan_info': scan_info}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py index f197724dd..a43c76479 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py @@ -33,19 +33,25 @@ class T1021(AttackTechnique): @staticmethod def get_report_data(): - attempts = [] - if mongo.db.telemetry.count_documents(T1021.scanned_query): - attempts = list(mongo.db.telemetry.aggregate(T1021.query)) - if attempts: - status = ScanStatus.USED.value - for result in attempts: - result['successful_creds'] = [] - for attempt in result['attempts']: - result['successful_creds'].append(parse_creds(attempt)) + @T1021.is_status_disabled + def get_technique_status_and_data(): + attempts = [] + if mongo.db.telemetry.count_documents(T1021.scanned_query): + attempts = list(mongo.db.telemetry.aggregate(T1021.query)) + if attempts: + status = ScanStatus.USED.value + for result in attempts: + result['successful_creds'] = [] + for attempt in result['attempts']: + result['successful_creds'].append(parse_creds(attempt)) + else: + status = ScanStatus.SCANNED.value else: - status = ScanStatus.SCANNED.value - else: - status = ScanStatus.UNSCANNED.value + status = ScanStatus.UNSCANNED.value + return (status, attempts) + + status, attempts = get_technique_status_and_data() + data = T1021.get_base_data_by_status(status) data.update({'services': attempts}) return data 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 df4ae4ca5..f56b3e23f 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py @@ -6,7 +6,7 @@ __author__ = "VakarisZ" class T1035(UsageTechnique): tech_id = "T1035" - unscanned_msg = "Monkey didn't try to interact with Windows services." + unscanned_msg = "Monkey didn't try to interact with Windows services since it didn't run on any Windows machines." scanned_msg = "Monkey tried to interact with Windows services, but failed." used_msg = "Monkey successfully interacted with Windows services." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py index 737004111..b4548dac8 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py @@ -13,14 +13,20 @@ class T1041(AttackTechnique): @staticmethod def get_report_data(): - monkeys = list(Monkey.objects()) - info = [{'src': monkey['command_control_channel']['src'], - 'dst': monkey['command_control_channel']['dst']} - for monkey in monkeys if monkey['command_control_channel']] - if info: - status = ScanStatus.USED.value - else: - status = ScanStatus.UNSCANNED.value + @T1041.is_status_disabled + def get_technique_status_and_data(): + monkeys = list(Monkey.objects()) + info = [{'src': monkey['command_control_channel']['src'], + 'dst': monkey['command_control_channel']['dst']} + for monkey in monkeys if monkey['command_control_channel']] + if info: + status = ScanStatus.USED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, info) + + status, info = get_technique_status_and_data() + data = T1041.get_base_data_by_status(status) data.update({'command_control_channel': info}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1053.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1053.py index 7af3978d5..511f819e3 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1053.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1053.py @@ -1,34 +1,13 @@ from common.data.post_breach_consts import POST_BREACH_JOB_SCHEDULING -from common.utils.attack_utils import ScanStatus -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.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1053(AttackTechnique): +class T1053(PostBreachTechnique): tech_id = "T1053" - unscanned_msg = "Monkey did not try scheduling a job on Windows." + unscanned_msg = "Monkey didn't try scheduling a job on Windows since it didn't run on any Windows machines." scanned_msg = "Monkey tried scheduling a job on the Windows system but failed." used_msg = "Monkey scheduled a job on the Windows system." - - query = [{'$match': {'telem_category': 'post_breach', - 'data.name': POST_BREACH_JOB_SCHEDULING, - 'data.command': {'$regex': 'schtasks'}}}, - {'$project': {'_id': 0, - 'machine': {'hostname': '$data.hostname', - 'ips': ['$data.ip']}, - 'result': '$data.result'}}] - - @staticmethod - def get_report_data(): - data = {'title': T1053.technique_title()} - - job_scheduling_info = list(mongo.db.telemetry.aggregate(T1053.query)) - - status = (ScanStatus.USED.value if job_scheduling_info[0]['result'][1] - else ScanStatus.SCANNED.value) if job_scheduling_info else ScanStatus.UNSCANNED.value - - data.update(T1053.get_base_data_by_status(status)) - data.update({'info': job_scheduling_info}) - return data + pba_names = [POST_BREACH_JOB_SCHEDULING] 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 987c24d09..b702ddd58 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -23,12 +23,18 @@ class T1059(AttackTechnique): @staticmethod def get_report_data(): - cmd_data = list(mongo.db.telemetry.aggregate(T1059.query)) + @T1059.is_status_disabled + def get_technique_status_and_data(): + cmd_data = list(mongo.db.telemetry.aggregate(T1059.query)) + if cmd_data: + status = ScanStatus.USED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, cmd_data) + + status, cmd_data = get_technique_status_and_data() data = {'title': T1059.technique_title(), 'cmds': cmd_data} - if cmd_data: - status = ScanStatus.USED.value - else: - status = ScanStatus.UNSCANNED.value + data.update(T1059.get_message_and_status(status)) data.update(T1059.get_mitigation_by_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 8df625284..cd4d69ae8 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py @@ -14,6 +14,6 @@ class T1065(AttackTechnique): @staticmethod def get_report_data(): - port = ConfigService.get_config_value(['cnc', 'servers', 'current_server']).split(':')[1] + port = ConfigService.get_config_value(['internal', 'island_server', 'current_server']).split(':')[1] T1065.used_msg = T1065.message % port 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 29bbc366c..5d3f270e7 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py @@ -7,7 +7,7 @@ __author__ = "VakarisZ" class T1075(AttackTechnique): tech_id = "T1075" - unscanned_msg = "Monkey didn't try to use pass the hash attack." + unscanned_msg = "Monkey didn't try to use pass the hash attack since it didn't run on any Windows machines." scanned_msg = "Monkey tried to use hashes while logging in but didn't succeed." used_msg = "Monkey successfully used hashed credentials." @@ -30,15 +30,21 @@ class T1075(AttackTechnique): @staticmethod def get_report_data(): + @T1075.is_status_disabled + def get_technique_status_and_data(): + successful_logins = list(mongo.db.telemetry.aggregate(T1075.query)) + if successful_logins: + status = ScanStatus.USED.value + elif mongo.db.telemetry.count_documents(T1075.login_attempt_query): + status = ScanStatus.SCANNED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, successful_logins) + + status, successful_logins = get_technique_status_and_data() data = {'title': T1075.technique_title()} - successful_logins = list(mongo.db.telemetry.aggregate(T1075.query)) data.update({'successful_logins': successful_logins}) - if successful_logins: - status = ScanStatus.USED.value - elif mongo.db.telemetry.count_documents(T1075.login_attempt_query): - status = ScanStatus.SCANNED.value - else: - status = ScanStatus.UNSCANNED.value + data.update(T1075.get_message_and_status(status)) data.update(T1075.get_mitigation_by_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 7e8801000..1a9ff94f8 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py @@ -38,13 +38,19 @@ class T1082(AttackTechnique): @staticmethod def get_report_data(): + @T1082.is_status_disabled + def get_technique_status_and_data(): + system_info = list(mongo.db.telemetry.aggregate(T1082.query)) + if system_info: + status = ScanStatus.USED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, system_info) + + status, system_info = get_technique_status_and_data() data = {'title': T1082.technique_title()} - system_info = list(mongo.db.telemetry.aggregate(T1082.query)) data.update({'system_info': system_info}) - if system_info: - status = ScanStatus.USED.value - else: - status = ScanStatus.UNSCANNED.value + data.update(T1082.get_mitigation_by_status(status)) 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 ad5ddc974..d6237a3f7 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py @@ -7,7 +7,7 @@ __author__ = "VakarisZ" class T1086(AttackTechnique): tech_id = "T1086" - unscanned_msg = "Monkey didn't run powershell." + unscanned_msg = "Monkey didn't run powershell since it didn't run on any Windows machines." scanned_msg = "" used_msg = "Monkey successfully ran powershell commands on exploited machines in the network." @@ -25,12 +25,17 @@ class T1086(AttackTechnique): @staticmethod def get_report_data(): - cmd_data = list(mongo.db.telemetry.aggregate(T1086.query)) + @T1086.is_status_disabled + def get_technique_status_and_data(): + cmd_data = list(mongo.db.telemetry.aggregate(T1086.query)) + if cmd_data: + status = ScanStatus.USED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, cmd_data) + + status, cmd_data = get_technique_status_and_data() data = {'title': T1086.technique_title(), 'cmds': cmd_data} - if cmd_data: - status = ScanStatus.USED.value - else: - status = ScanStatus.UNSCANNED.value data.update(T1086.get_mitigation_by_status(status)) data.update(T1086.get_message_and_status(status)) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py index f0980637f..f68ab1166 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py @@ -13,9 +13,15 @@ class T1090(AttackTechnique): @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 + @T1090.is_status_disabled + def get_technique_status_and_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 + return (status, monkeys) + + status, monkeys = get_technique_status_and_data() + 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/T1110.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py index 63ba68d6f..c2d6fc8d5 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py @@ -26,21 +26,27 @@ class T1110(AttackTechnique): @staticmethod def get_report_data(): - attempts = list(mongo.db.telemetry.aggregate(T1110.query)) - succeeded = False + @T1110.is_status_disabled + def get_technique_status_and_data(): + attempts = list(mongo.db.telemetry.aggregate(T1110.query)) + succeeded = False - for result in attempts: - result['successful_creds'] = [] - for attempt in result['attempts']: - succeeded = True - result['successful_creds'].append(parse_creds(attempt)) + for result in attempts: + result['successful_creds'] = [] + for attempt in result['attempts']: + succeeded = True + result['successful_creds'].append(parse_creds(attempt)) + + if succeeded: + status = ScanStatus.USED.value + elif attempts: + status = ScanStatus.SCANNED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, attempts) + + status, attempts = get_technique_status_and_data() - if succeeded: - status = ScanStatus.USED.value - elif attempts: - status = ScanStatus.SCANNED.value - else: - 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/T1129.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py index fac76fb47..e84698058 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1129.py @@ -6,9 +6,9 @@ __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." + unscanned_msg = "Monkey didn't try to load any DLLs since it didn't run on any Windows machines." + scanned_msg = "Monkey tried to load DLLs, but failed." + used_msg = "Monkey successfully loaded DLLs using Windows module loader." @staticmethod def get_report_data(): diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py index 042fd3c77..086a1c139 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py @@ -1,39 +1,14 @@ from common.data.post_breach_consts import ( POST_BREACH_BACKDOOR_USER, POST_BREACH_COMMUNICATE_AS_NEW_USER) -from common.utils.attack_utils import ScanStatus -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.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1136(AttackTechnique): +class T1136(PostBreachTechnique): tech_id = "T1136" unscanned_msg = "Monkey didn't try creating a new user on the network's systems." scanned_msg = "Monkey tried creating a new user on the network's systems, but failed." used_msg = "Monkey created a new user on the network's systems." - - query = [{'$match': {'telem_category': 'post_breach', - '$or': [{'data.name': POST_BREACH_BACKDOOR_USER}, - {'data.name': POST_BREACH_COMMUNICATE_AS_NEW_USER}]}}, - {'$project': {'_id': 0, - 'machine': {'hostname': '$data.hostname', - 'ips': '$data.ip'}, - 'result': '$data.result'}}] - - @staticmethod - def get_report_data(): - data = {'title': T1136.technique_title()} - - create_user_info = list(mongo.db.telemetry.aggregate(T1136.query)) - - status = ScanStatus.UNSCANNED.value - if create_user_info: - successful_PBAs = mongo.db.telemetry.count({'$or': [{'data.name': POST_BREACH_BACKDOOR_USER}, - {'data.name': POST_BREACH_COMMUNICATE_AS_NEW_USER}], - 'data.result.1': True}) - status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value - - data.update(T1136.get_base_data_by_status(status)) - data.update({'info': create_user_info}) - return data + pba_names = [POST_BREACH_BACKDOOR_USER, POST_BREACH_COMMUNICATE_AS_NEW_USER] 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 736192b1f..5d96d863e 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py @@ -20,12 +20,17 @@ class T1145(AttackTechnique): @staticmethod def get_report_data(): - ssh_info = list(mongo.db.telemetry.aggregate(T1145.query)) + @T1145.is_status_disabled + def get_technique_status_and_data(): + ssh_info = list(mongo.db.telemetry.aggregate(T1145.query)) + if ssh_info: + status = ScanStatus.USED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, ssh_info) + + status, ssh_info = get_technique_status_and_data() - if ssh_info: - status = ScanStatus.USED.value - else: - 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/T1154.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1154.py index a48f3ebbd..c905fc9ca 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1154.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1154.py @@ -1,36 +1,13 @@ from common.data.post_breach_consts import POST_BREACH_TRAP_COMMAND -from common.utils.attack_utils import ScanStatus -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.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1154(AttackTechnique): +class T1154(PostBreachTechnique): tech_id = "T1154" - unscanned_msg = "Monkey did not use the trap command." + unscanned_msg = "Monkey didn't use the trap command since it didn't run on any Linux machines." scanned_msg = "Monkey tried using the trap command but failed." used_msg = "Monkey used the trap command successfully." - - query = [{'$match': {'telem_category': 'post_breach', - 'data.name': POST_BREACH_TRAP_COMMAND}}, - {'$project': {'_id': 0, - 'machine': {'hostname': '$data.hostname', - 'ips': '$data.ip'}, - 'result': '$data.result'}}] - - @staticmethod - def get_report_data(): - data = {'title': T1154.technique_title(), 'info': []} - - trap_command_info = list(mongo.db.telemetry.aggregate(T1154.query)) - - status = ScanStatus.UNSCANNED.value - if trap_command_info: - successful_PBAs = mongo.db.telemetry.count({'data.name': POST_BREACH_TRAP_COMMAND, - 'data.result.1': True}) - status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value - - data.update(T1154.get_base_data_by_status(status)) - data.update({'info': trap_command_info}) - return data + pba_names = [POST_BREACH_TRAP_COMMAND] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py index 64c401e8f..2841ed0ad 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1156.py @@ -1,40 +1,26 @@ from common.data.post_breach_consts import \ POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION -from common.utils.attack_utils import ScanStatus -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.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1156(AttackTechnique): +class T1156(PostBreachTechnique): tech_id = "T1156" - unscanned_msg = "Monkey did not try modifying bash startup files on the system." - scanned_msg = "Monkey tried modifying bash startup files on the system but failed." - used_msg = "Monkey modified bash startup files on the system." - - query = [{'$match': {'telem_category': 'post_breach', - 'data.name': POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION}}, - {'$project': {'_id': 0, - 'machine': {'hostname': {'$arrayElemAt': ['$data.hostname', 0]}, - 'ips': [{'$arrayElemAt': ['$data.ip', 0]}]}, - 'result': '$data.result'}}, - {'$unwind': '$result'}, - {'$match': {'$or': [{'result': {'$regex': r'\.bash'}}, - {'result': {'$regex': r'\.profile'}}]}}] + unscanned_msg = "Monkey didn't try modifying bash startup files since it didn't run on any Linux machines." + scanned_msg = "Monkey tried modifying bash startup files but failed." + used_msg = "Monkey successfully modified bash startup files." + pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION] @staticmethod - def get_report_data(): - data = {'title': T1156.technique_title(), 'info': []} - - bash_startup_modification_info = list(mongo.db.telemetry.aggregate(T1156.query)) - - status = ScanStatus.UNSCANNED.value - if bash_startup_modification_info: - successful_PBAs = mongo.db.telemetry.count({'data.name': POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION, - 'data.result.1': True}) - status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value - - data.update(T1156.get_base_data_by_status(status)) - data.update({'info': bash_startup_modification_info}) - return data + def get_pba_query(*args): + return [{'$match': {'telem_category': 'post_breach', + 'data.name': POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION}}, + {'$project': {'_id': 0, + 'machine': {'hostname': {'$arrayElemAt': ['$data.hostname', 0]}, + 'ips': [{'$arrayElemAt': ['$data.ip', 0]}]}, + 'result': '$data.result'}}, + {'$unwind': '$result'}, + {'$match': {'$or': [{'result': {'$regex': r'\.bash'}}, + {'result': {'$regex': r'\.profile'}}]}}] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1158.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1158.py index 6cf9faeb8..7b0f87358 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1158.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1158.py @@ -1,36 +1,13 @@ from common.data.post_breach_consts import POST_BREACH_HIDDEN_FILES -from common.utils.attack_utils import ScanStatus -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.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1158(AttackTechnique): +class T1158(PostBreachTechnique): tech_id = "T1158" - unscanned_msg = "Monkey did not try creating hidden files or folders." + unscanned_msg = "Monkey didn't try creating hidden files or folders." scanned_msg = "Monkey tried creating hidden files and folders on the system but failed." used_msg = "Monkey created hidden files and folders on the system." - - query = [{'$match': {'telem_category': 'post_breach', - 'data.name': POST_BREACH_HIDDEN_FILES}}, - {'$project': {'_id': 0, - 'machine': {'hostname': '$data.hostname', - 'ips': '$data.ip'}, - 'result': '$data.result'}}] - - @staticmethod - def get_report_data(): - data = {'title': T1158.technique_title(), 'info': []} - - hidden_file_info = list(mongo.db.telemetry.aggregate(T1158.query)) - - status = ScanStatus.UNSCANNED.value - if hidden_file_info: - successful_PBAs = mongo.db.telemetry.count({'data.name': POST_BREACH_HIDDEN_FILES, - 'data.result.1': True}) - status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value - - data.update(T1158.get_base_data_by_status(status)) - data.update({'info': hidden_file_info}) - return data + pba_names = [POST_BREACH_HIDDEN_FILES] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py index 84e213607..e3b74e5c5 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py @@ -1,36 +1,13 @@ from common.data.post_breach_consts import POST_BREACH_SETUID_SETGID -from common.utils.attack_utils import ScanStatus -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.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1166(AttackTechnique): +class T1166(PostBreachTechnique): tech_id = "T1166" - unscanned_msg = "Monkey did not try creating hidden files or folders." - scanned_msg = "Monkey tried creating hidden files and folders on the system but failed." - used_msg = "Monkey created hidden files and folders on the system." - - query = [{'$match': {'telem_category': 'post_breach', - 'data.name': POST_BREACH_SETUID_SETGID}}, - {'$project': {'_id': 0, - 'machine': {'hostname': '$data.hostname', - 'ips': '$data.ip'}, - 'result': '$data.result'}}] - - @staticmethod - def get_report_data(): - data = {'title': T1166.technique_title(), 'info': []} - - setuid_setgid_info = list(mongo.db.telemetry.aggregate(T1166.query)) - - status = ScanStatus.UNSCANNED.value - if setuid_setgid_info: - successful_PBAs = mongo.db.telemetry.count({'data.name': POST_BREACH_SETUID_SETGID, - 'data.result.1': True}) - status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value - - data.update(T1166.get_base_data_by_status(status)) - data.update({'info': setuid_setgid_info}) - return data + unscanned_msg = "Monkey didn't try setting the setuid or setgid bits since it didn't run on any Linux machines." + scanned_msg = "Monkey tried setting the setuid or setgid bits but failed." + used_msg = "Monkey successfully set the setuid or setgid bits." + pba_names = [POST_BREACH_SETUID_SETGID] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1168.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1168.py index bda495845..76806806c 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1168.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1168.py @@ -1,37 +1,13 @@ from common.data.post_breach_consts import POST_BREACH_JOB_SCHEDULING -from common.utils.attack_utils import ScanStatus -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.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1168(AttackTechnique): +class T1168(PostBreachTechnique): tech_id = "T1168" - unscanned_msg = "Monkey did not try scheduling a job on Linux." + unscanned_msg = "Monkey didn't try scheduling a job on Linux since it didn't run on any Linux machines." scanned_msg = "Monkey tried scheduling a job on the Linux system but failed." used_msg = "Monkey scheduled a job on the Linux system." - - query = [{'$match': {'telem_category': 'post_breach', - 'data.name': POST_BREACH_JOB_SCHEDULING, - 'data.command': {'$regex': 'crontab'}}}, - {'$project': {'_id': 0, - 'machine': {'hostname': '$data.hostname', - 'ips': '$data.ip'}, - 'result': '$data.result'}}] - - @staticmethod - def get_report_data(): - data = {'title': T1168.technique_title()} - - job_scheduling_info = list(mongo.db.telemetry.aggregate(T1168.query)) - - status = ScanStatus.UNSCANNED.value - if job_scheduling_info: - successful_PBAs = mongo.db.telemetry.count({'data.name': POST_BREACH_JOB_SCHEDULING, - 'data.result.1': True}) - status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value - - data.update(T1168.get_base_data_by_status(status)) - data.update({'info': job_scheduling_info}) - return data + pba_names = [POST_BREACH_JOB_SCHEDULING] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py index 09e0edcdf..2dbf87638 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py @@ -13,19 +13,25 @@ class T1188(AttackTechnique): @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 + @T1188.is_status_disabled + def get_technique_status_and_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 + return (status, hops) + + status, hops = get_technique_status_and_data() + data = T1188.get_base_data_by_status(status) data.update({'hops': hops}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py index b6bd316af..b87aeb275 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py @@ -6,7 +6,7 @@ __author__ = "VakarisZ" class T1197(AttackTechnique): tech_id = "T1197" - unscanned_msg = "Monkey didn't try to use any bits jobs." + unscanned_msg = "Monkey didn't try to use any bits jobs since it didn't run on any Windows machines." scanned_msg = "Monkey tried to use bits jobs but failed." used_msg = "Monkey successfully used bits jobs at least once in the network." 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 8fe86ed61..baefcba8e 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py @@ -13,15 +13,26 @@ class T1210(AttackTechnique): @staticmethod def get_report_data(): - data = {'title': T1210.technique_title()} - scanned_services = T1210.get_scanned_services() - exploited_services = T1210.get_exploited_services() - if exploited_services: - status = ScanStatus.USED.value - elif scanned_services: - status = ScanStatus.SCANNED.value + @T1210.is_status_disabled + def get_technique_status_and_data(): + scanned_services = T1210.get_scanned_services() + exploited_services = T1210.get_exploited_services() + if exploited_services: + status = ScanStatus.USED.value + elif scanned_services: + status = ScanStatus.SCANNED.value + else: + status = ScanStatus.UNSCANNED.value + return (status, scanned_services, exploited_services) + + status_and_data = get_technique_status_and_data() + status = status_and_data[0] + if status == ScanStatus.DISABLED.value: + scanned_services, exploited_services = [], [] else: - status = ScanStatus.UNSCANNED.value + scanned_services, exploited_services = status_and_data[1], status_and_data[2] + data = {'title': T1210.technique_title()} + data.update(T1210.get_message_and_status(status)) data.update(T1210.get_mitigation_by_status(status)) data.update({'scanned_services': scanned_services, 'exploited_services': exploited_services}) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py index 05568fcb8..8d8956e6b 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py @@ -1,39 +1,25 @@ from common.data.post_breach_consts import \ POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION -from common.utils.attack_utils import ScanStatus -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.pba_technique import \ + PostBreachTechnique __author__ = "shreyamalviya" -class T1504(AttackTechnique): +class T1504(PostBreachTechnique): tech_id = "T1504" - unscanned_msg = "Monkey did not try modifying powershell startup files on the system." - scanned_msg = "Monkey tried modifying powershell startup files on the system but failed." - used_msg = "Monkey modified powershell startup files on the system." - - query = [{'$match': {'telem_category': 'post_breach', - 'data.name': POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION}}, - {'$project': {'_id': 0, - 'machine': {'hostname': {'$arrayElemAt': ['$data.hostname', 0]}, - 'ips': [{'$arrayElemAt': ['$data.ip', 0]}]}, - 'result': '$data.result'}}, - {'$unwind': '$result'}, - {'$match': {'result': {'$regex': r'profile\.ps1'}}}] + unscanned_msg = "Monkey didn't try modifying powershell startup files since it didn't run on any Windows machines." + scanned_msg = "Monkey tried modifying powershell startup files but failed." + used_msg = "Monkey successfully modified powershell startup files." + pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION] @staticmethod - def get_report_data(): - data = {'title': T1504.technique_title(), 'info': []} - - powershell_startup_modification_info = list(mongo.db.telemetry.aggregate(T1504.query)) - - status = ScanStatus.UNSCANNED.value - if powershell_startup_modification_info: - successful_PBAs = mongo.db.telemetry.count({'data.name': POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION, - 'data.result.1': True}) - status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value - - data.update(T1504.get_base_data_by_status(status)) - data.update({'info': powershell_startup_modification_info}) - return data + def get_pba_query(*args): + return [{'$match': {'telem_category': 'post_breach', + 'data.name': POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION}}, + {'$project': {'_id': 0, + 'machine': {'hostname': {'$arrayElemAt': ['$data.hostname', 0]}, + 'ips': [{'$arrayElemAt': ['$data.ip', 0]}]}, + 'result': '$data.result'}}, + {'$unwind': '$result'}, + {'$match': {'result': {'$regex': r'profile\.ps1'}}}] 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 9b39f30ef..61c1f89bd 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -10,6 +10,10 @@ from monkey_island.cc.services.attack.attack_config import AttackConfig logger = logging.getLogger(__name__) +disabled_msg = "This technique has been disabled. " +\ + "You can enable it from the [configuration page](../../configure)." + + class AttackTechnique(object, metaclass=abc.ABCMeta): """ Abstract class for ATT&CK report components """ @@ -59,9 +63,11 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): Gets the status of a certain attack technique. :return: ScanStatus numeric value """ - if mongo.db.telemetry.find_one({'telem_category': 'attack', - 'data.status': ScanStatus.USED.value, - 'data.technique': cls.tech_id}): + if not cls._is_enabled_in_config(): + return ScanStatus.DISABLED.value + elif mongo.db.telemetry.find_one({'telem_category': 'attack', + 'data.status': ScanStatus.USED.value, + 'data.technique': cls.tech_id}): return ScanStatus.USED.value elif mongo.db.telemetry.find_one({'telem_category': 'attack', 'data.status': ScanStatus.SCANNED.value, @@ -86,6 +92,8 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): :param status: Enum from common/attack_utils.py integer value :return: message string """ + if status == ScanStatus.DISABLED.value: + return disabled_msg if status == ScanStatus.UNSCANNED.value: return cls.unscanned_msg elif status == ScanStatus.SCANNED.value: @@ -129,3 +137,13 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): return {'mitigations': mitigation_document.to_mongo().to_dict()['mitigations']} else: return {} + + @classmethod + def is_status_disabled(cls, get_technique_status_and_data) -> bool: + def check_if_disabled_in_config(): + return (ScanStatus.DISABLED.value, []) if not cls._is_enabled_in_config() else get_technique_status_and_data() + return check_if_disabled_in_config + + @classmethod + def _is_enabled_in_config(cls) -> bool: + return AttackConfig.get_technique_values()[cls.tech_id] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py new file mode 100644 index 000000000..da475c697 --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py @@ -0,0 +1,57 @@ +import abc +from typing import List + +from common.utils.attack_utils import ScanStatus +from monkey_island.cc.database import mongo +from monkey_island.cc.services.attack.technique_reports import AttackTechnique + + +class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta): + """ Class for ATT&CK report components of post-breach actions """ + + @property + @abc.abstractmethod + def pba_names(self) -> List[str]: + """ + :return: names of post breach action + """ + pass + + @classmethod + def get_pba_query(cls, post_breach_action_names): + """ + :param post_breach_action_names: Names of post-breach actions with which the technique is associated + (example - `["Communicate as new user", "Backdoor user"]` for T1136) + :return: Mongo query that parses attack telemetries for a simple report component + (gets machines and post-breach action usage). + """ + return [{'$match': {'telem_category': 'post_breach', + '$or': [{'data.name': pba_name} for pba_name in post_breach_action_names]}}, + {'$project': {'_id': 0, + 'machine': {'hostname': '$data.hostname', + 'ips': ['$data.ip']}, + 'result': '$data.result'}}] + + @classmethod + def get_report_data(cls): + """ + :return: Technique's report data aggregated from the database + """ + @cls.is_status_disabled + def get_technique_status_and_data(): + info = list(mongo.db.telemetry.aggregate(cls.get_pba_query(cls.pba_names))) + status = ScanStatus.UNSCANNED.value + if info: + successful_PBAs = mongo.db.telemetry.count({ + '$or': [{'data.name': pba_name} for pba_name in cls.pba_names], + 'data.result.1': True + }) + status = ScanStatus.USED.value if successful_PBAs else ScanStatus.SCANNED.value + return (status, info) + + data = {'title': cls.technique_title()} + status, info = get_technique_status_and_data() + + data.update(cls.get_base_data_by_status(status)) + data.update({'info': info}) + return data diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py index e8453a052..ac1ee1417 100644 --- a/monkey/monkey_island/cc/services/config.py +++ b/monkey/monkey_island/cc/services/config.py @@ -10,8 +10,7 @@ import monkey_island.cc.services.post_breach_files from monkey_island.cc.database import mongo from monkey_island.cc.encryptor import encryptor from monkey_island.cc.network_utils import local_ip_addresses - -from .config_schema import SCHEMA +from monkey_island.cc.services.config_schema.config_schema import SCHEMA __author__ = "itay.mizeretz" @@ -218,8 +217,8 @@ class ConfigService: @staticmethod def set_server_ips_in_config(config): ips = local_ip_addresses() - config["cnc"]["servers"]["command_servers"] = ["%s:%d" % (ip, env_singleton.env.get_island_port()) for ip in ips] - config["cnc"]["servers"]["current_server"] = "%s:%d" % (ips[0], env_singleton.env.get_island_port()) + config["internal"]["island_server"]["command_servers"] = ["%s:%d" % (ip, env_singleton.env.get_island_port()) for ip in ips] + config["internal"]["island_server"]["current_server"] = "%s:%d" % (ips[0], env_singleton.env.get_island_port()) @staticmethod def save_initial_config_if_needed(): diff --git a/monkey/monkey_island/cc/services/config_schema.py b/monkey/monkey_island/cc/services/config_schema.py deleted file mode 100644 index 1e6740fe5..000000000 --- a/monkey/monkey_island/cc/services/config_schema.py +++ /dev/null @@ -1,1070 +0,0 @@ -from common.data.system_info_collectors_names import (AWS_COLLECTOR, - ENVIRONMENT_COLLECTOR, - HOSTNAME_COLLECTOR, - PROCESS_LIST_COLLECTOR) - -WARNING_SIGN = " \u26A0" - -SCHEMA = { - "title": "Monkey", - "type": "object", - "definitions": { - "exploiter_classes": { - "title": "Exploit class", - "type": "string", - "anyOf": [ - { - "type": "string", - "enum": [ - "SmbExploiter" - ], - "title": "SMB Exploiter", - "attack_techniques": ["T1110", "T1075", "T1035"] - }, - { - "type": "string", - "enum": [ - "WmiExploiter" - ], - "title": "WMI Exploiter", - "attack_techniques": ["T1110", "T1106"] - }, - { - "type": "string", - "enum": [ - "MSSQLExploiter" - ], - "title": "MSSQL Exploiter", - "attack_techniques": ["T1110"] - }, - { - "type": "string", - "enum": [ - "Ms08_067_Exploiter" - ], - "title": "MS08-067 Exploiter (UNSAFE)", - "attack_techniques": [] - }, - { - "type": "string", - "enum": [ - "SSHExploiter" - ], - "title": "SSH Exploiter", - "attack_techniques": ["T1110", "T1145", "T1106"] - }, - { - "type": "string", - "enum": [ - "ShellShockExploiter" - ], - "title": "ShellShock Exploiter" - }, - { - "type": "string", - "enum": [ - "SambaCryExploiter" - ], - "title": "SambaCry Exploiter" - }, - { - "type": "string", - "enum": [ - "ElasticGroovyExploiter" - ], - "title": "ElasticGroovy Exploiter" - }, - { - "type": "string", - "enum": [ - "Struts2Exploiter" - ], - "title": "Struts2 Exploiter" - }, - { - "type": "string", - "enum": [ - "WebLogicExploiter" - ], - "title": "WebLogic Exploiter" - }, - { - "type": "string", - "enum": [ - "HadoopExploiter" - ], - "title": "Hadoop/Yarn Exploiter" - }, - { - "type": "string", - "enum": [ - "VSFTPDExploiter" - ], - "title": "VSFTPD Exploiter" - } - ] - }, - "system_info_collectors_classes": { - "title": "System Information Collectors", - "type": "string", - "anyOf": [ - { - "type": "string", - "enum": [ - ENVIRONMENT_COLLECTOR - ], - "title": "Collect which environment this machine is on (on prem/cloud)", - "attack_techniques": [] - }, - { - "type": "string", - "enum": [ - AWS_COLLECTOR - ], - "title": "If on AWS, collect more information about the instance", - "attack_techniques": [] - }, - { - "type": "string", - "enum": [ - HOSTNAME_COLLECTOR - ], - "title": "Collect the machine's hostname", - "attack_techniques": [] - }, - { - "type": "string", - "enum": [ - PROCESS_LIST_COLLECTOR - ], - "title": "Collect running processes on the machine", - "attack_techniques": [] - }, - ], - }, - "post_breach_actions": { - "title": "Post breach actions", - "type": "string", - "anyOf": [ - { - "type": "string", - "enum": [ - "BackdoorUser" - ], - "title": "Back door user", - "attack_techniques": ["T1136"] - }, - { - "type": "string", - "enum": [ - "CommunicateAsNewUser" - ], - "title": "Communicate as new user", - "attack_techniques": ["T1136"] - }, - { - "type": "string", - "enum": [ - "ModifyShellStartupFiles" - ], - "title": "Modify shell startup files", - "attack_techniques": ["T1156", "T1504"] - }, - { - "type": "string", - "enum": [ - "HiddenFiles" - ], - "title": "Hidden files and directories", - "attack_techniques": ["T1158"] - }, - { - "type": "string", - "enum": [ - "TrapCommand" - ], - "title": "Trap", - "attack_techniques": ["T1154"] - }, - { - "type": "string", - "enum": [ - "ChangeSetuidSetgid" - ], - "title": "Setuid and Setgid", - "attack_techniques": ["T1166"] - }, - { - "type": "string", - "enum": [ - "ScheduleJobs" - ], - "title": "Job scheduling", - "attack_techniques": ["T1168", "T1053"] - } - ], - }, - "finger_classes": { - "title": "Fingerprint class", - "type": "string", - "anyOf": [ - { - "type": "string", - "enum": [ - "SMBFinger" - ], - "title": "SMBFinger", - "attack_techniques": ["T1210"] - }, - { - "type": "string", - "enum": [ - "SSHFinger" - ], - "title": "SSHFinger", - "attack_techniques": ["T1210"] - }, - { - "type": "string", - "enum": [ - "PingScanner" - ], - "title": "PingScanner" - }, - { - "type": "string", - "enum": [ - "HTTPFinger" - ], - "title": "HTTPFinger" - }, - { - "type": "string", - "enum": [ - "MySQLFinger" - ], - "title": "MySQLFinger", - "attack_techniques": ["T1210"] - }, - { - "type": "string", - "enum": [ - "MSSQLFinger" - ], - "title": "MSSQLFinger", - "attack_techniques": ["T1210"] - }, - - { - "type": "string", - "enum": [ - "ElasticFinger" - ], - "title": "ElasticFinger", - "attack_techniques": ["T1210"] - } - ] - } - }, - "properties": { - "basic": { - "title": "Basic - Exploits", - "type": "object", - "properties": { - "general": { - "title": "General", - "type": "object", - "properties": { - "should_exploit": { - "title": "Exploit network machines", - "type": "boolean", - "default": True, - "attack_techniques": ["T1210"], - "description": "Determines if monkey should try to safely exploit machines on the network" - } - } - }, - "credentials": { - "title": "Credentials", - "type": "object", - "properties": { - "exploit_user_list": { - "title": "Exploit user list", - "type": "array", - "uniqueItems": True, - "items": { - "type": "string" - }, - "default": [ - "Administrator", - "root", - "user" - ], - "description": "List of usernames to use on exploits using credentials" - }, - "exploit_password_list": { - "title": "Exploit password list", - "type": "array", - "uniqueItems": True, - "items": { - "type": "string" - }, - "default": [ - "Password1!", - "1234", - "password", - "12345678" - ], - "description": "List of password to use on exploits using credentials" - } - } - } - } - }, - "basic_network": { - "title": "Basic - Network", - "type": "object", - "properties": { - "general": { - "title": "General", - "type": "object", - "properties": { - "blocked_ips": { - "title": "Blocked IPs", - "type": "array", - "uniqueItems": True, - "items": { - "type": "string" - }, - "default": [ - ], - "description": "List of IPs to not scan" - }, - "local_network_scan": { - "title": "Local network scan", - "type": "boolean", - "default": True, - "description": "Determines whether the monkey should scan its subnets additionally" - }, - "depth": { - "title": "Distance from island", - "type": "integer", - "default": 2, - "description": - "Amount of hops allowed for the monkey to spread from the island. " - + WARNING_SIGN - + " Note that setting this value too high may result in the monkey propagating too far" - }, - "subnet_scan_list": { - "title": "Scan IP/subnet list", - "type": "array", - "uniqueItems": True, - "items": { - "type": "string" - }, - "default": [ - ], - "description": - "List of IPs/subnets/hosts the monkey should scan." - " Examples: \"192.168.0.1\", \"192.168.0.5-192.168.0.20\", \"192.168.0.5/24\"," - " \"printer.example\"" - } - } - }, - "network_analysis": { - "title": "Network Analysis", - "type": "object", - "properties": { - "inaccessible_subnets": { - "title": "Network segmentation testing", - "type": "array", - "uniqueItems": True, - "items": { - "type": "string" - }, - "default": [ - ], - "description": - "Test for network segmentation by providing a list of" - " subnets that should NOT be accessible to each other." - " For example, given the following configuration:" - " '10.0.0.0/24, 11.0.0.2/32, 12.2.3.0/24'" - " a Monkey running on 10.0.0.5 will try to access machines in the following" - " subnets: 11.0.0.2/32, 12.2.3.0/24." - " An alert on successful connections will be shown in the report" - " Additional subnet formats include: 13.0.0.1, 13.0.0.1-13.0.0.5" - } - } - } - } - }, - "monkey": { - "title": "Monkey", - "type": "object", - "properties": { - "general": { - "title": "General", - "type": "object", - "properties": { - "alive": { - "title": "Alive", - "type": "boolean", - "default": True, - "description": "Is the monkey alive" - }, - "post_breach_actions": { - "title": "Post breach actions", - "type": "array", - "uniqueItems": True, - "items": { - "$ref": "#/definitions/post_breach_actions" - }, - "default": [ - "BackdoorUser", - "CommunicateAsNewUser", - "ModifyShellStartupFiles", - "HiddenFiles", - "TrapCommand", - "ChangeSetuidSetgid", - "ScheduleJobs" - ], - "description": "List of actions the Monkey will run post breach" - }, - } - }, - "behaviour": { - "title": "Behaviour", - "type": "object", - "properties": { - "custom_PBA_linux_cmd": { - "title": "Linux post breach command", - "type": "string", - "default": "", - "description": "Linux command to be executed after breaching." - }, - "PBA_linux_file": { - "title": "Linux post breach file", - "type": "string", - "format": "data-url", - "description": "File to be executed after breaching. " - "If you want custom execution behavior, " - "specify it in 'Linux post breach command' field. " - "Reference your file by filename." - }, - "custom_PBA_windows_cmd": { - "title": "Windows post breach command", - "type": "string", - "default": "", - "description": "Windows command to be executed after breaching." - }, - "PBA_windows_file": { - "title": "Windows post breach file", - "type": "string", - "format": "data-url", - "description": "File to be executed after breaching. " - "If you want custom execution behavior, " - "specify it in 'Windows post breach command' field. " - "Reference your file by filename." - }, - "PBA_windows_filename": { - "title": "Windows PBA filename", - "type": "string", - "default": "" - }, - "PBA_linux_filename": { - "title": "Linux PBA filename", - "type": "string", - "default": "" - }, - "self_delete_in_cleanup": { - "title": "Self delete on cleanup", - "type": "boolean", - "default": True, - "description": "Should the monkey delete its executable when going down" - }, - "use_file_logging": { - "title": "Use file logging", - "type": "boolean", - "default": True, - "description": "Should the monkey dump to a log file" - }, - "serialize_config": { - "title": "Serialize config", - "type": "boolean", - "default": False, - "description": "Should the monkey dump its config on startup" - } - } - }, - "system_info": { - "title": "System info", - "type": "object", - "properties": { - "extract_azure_creds": { - "title": "Harvest Azure Credentials", - "type": "boolean", - "default": True, - "attack_techniques": ["T1003"], - "description": - "Determine if the Monkey should try to harvest password credentials from Azure VMs" - }, - "collect_system_info": { - "title": "Collect system info", - "type": "boolean", - "default": True, - "attack_techniques": ["T1082", "T1005", "T1016"], - "description": "Determines whether to collect system info" - }, - "should_use_mimikatz": { - "title": "Should use Mimikatz", - "type": "boolean", - "default": True, - "attack_techniques": ["T1003"], - "description": "Determines whether to use Mimikatz" - }, - "system_info_collectors_classes": { - "title": "System info collectors", - "type": "array", - "uniqueItems": True, - "items": { - "$ref": "#/definitions/system_info_collectors_classes" - }, - "default": [ - ENVIRONMENT_COLLECTOR, - AWS_COLLECTOR, - HOSTNAME_COLLECTOR, - PROCESS_LIST_COLLECTOR - ], - "description": "Determines which system information collectors will collect information." - }, - } - }, - "life_cycle": { - "title": "Life cycle", - "type": "object", - "properties": { - "max_iterations": { - "title": "Max iterations", - "type": "integer", - "default": 1, - "description": "Determines how many iterations of the monkey's full lifecycle should occur" - }, - "victims_max_find": { - "title": "Max victims to find", - "type": "integer", - "default": 100, - "description": "Determines the maximum number of machines the monkey is allowed to scan" - }, - "victims_max_exploit": { - "title": "Max victims to exploit", - "type": "integer", - "default": 15, - "description": - "Determines the maximum number of machines the monkey" - " is allowed to successfully exploit. " + WARNING_SIGN - + " Note that setting this value too high may result in the monkey propagating to " - "a high number of machines" - }, - "timeout_between_iterations": { - "title": "Wait time between iterations", - "type": "integer", - "default": 100, - "description": - "Determines for how long (in seconds) should the monkey wait between iterations" - }, - "retry_failed_explotation": { - "title": "Retry failed exploitation", - "type": "boolean", - "default": True, - "description": - "Determines whether the monkey should retry exploiting machines" - " it didn't successfully exploit on previous iterations" - } - } - } - } - }, - "internal": { - "title": "Internal", - "type": "object", - "properties": { - "general": { - "title": "General", - "type": "object", - "properties": { - "singleton_mutex_name": { - "title": "Singleton mutex name", - "type": "string", - "default": "{2384ec59-0df8-4ab9-918c-843740924a28}", - "description": - "The name of the mutex used to determine whether the monkey is already running" - }, - "keep_tunnel_open_time": { - "title": "Keep tunnel open time", - "type": "integer", - "default": 60, - "description": "Time to keep tunnel open before going down after last exploit (in seconds)" - }, - "monkey_dir_name": { - "title": "Monkey's directory name", - "type": "string", - "default": r"monkey_dir", - "description": "Directory name for the directory which will contain all of the monkey files" - }, - "started_on_island": { - "title": "Started on island", - "type": "boolean", - "default": False, - "description": "Was exploitation started from island" - "(did monkey with max depth ran on island)" - }, - } - }, - "classes": { - "title": "Classes", - "type": "object", - "properties": { - "finger_classes": { - "title": "Fingerprint classes", - "type": "array", - "uniqueItems": True, - "items": { - "$ref": "#/definitions/finger_classes" - }, - "default": [ - "SMBFinger", - "SSHFinger", - "PingScanner", - "HTTPFinger", - "MySQLFinger", - "MSSQLFinger", - "ElasticFinger" - ], - "description": "Determines which classes to use for fingerprinting" - } - } - }, - "kill_file": { - "title": "Kill file", - "type": "object", - "properties": { - "kill_file_path_windows": { - "title": "Kill file path on Windows", - "type": "string", - "default": "%windir%\\monkey.not", - "description": "Path of file which kills monkey if it exists (on Windows)" - }, - "kill_file_path_linux": { - "title": "Kill file path on Linux", - "type": "string", - "default": "/var/run/monkey.not", - "description": "Path of file which kills monkey if it exists (on Linux)" - } - } - }, - "dropper": { - "title": "Dropper", - "type": "object", - "properties": { - "dropper_set_date": { - "title": "Dropper sets date", - "type": "boolean", - "default": True, - "description": - "Determines whether the dropper should set the monkey's file date to be the same as" - " another file" - }, - "dropper_date_reference_path_windows": { - "title": "Dropper date reference path (Windows)", - "type": "string", - "default": "%windir%\\system32\\kernel32.dll", - "description": - "Determines which file the dropper should copy the date from if it's configured to do" - " so on Windows (use fullpath)" - }, - "dropper_date_reference_path_linux": { - "title": "Dropper date reference path (Linux)", - "type": "string", - "default": "/bin/sh", - "description": - "Determines which file the dropper should copy the date from if it's configured to do" - " so on Linux (use fullpath)" - }, - "dropper_target_path_linux": { - "title": "Dropper target path on Linux", - "type": "string", - "default": "/tmp/monkey", - "description": "Determines where should the dropper place the monkey on a Linux machine" - }, - "dropper_target_path_win_32": { - "title": "Dropper target path on Windows (32bit)", - "type": "string", - "default": "C:\\Windows\\temp\\monkey32.exe", - "description": "Determines where should the dropper place the monkey on a Windows machine " - "(32bit)" - }, - "dropper_target_path_win_64": { - "title": "Dropper target path on Windows (64bit)", - "type": "string", - "default": "C:\\Windows\\temp\\monkey64.exe", - "description": "Determines where should the dropper place the monkey on a Windows machine " - "(64 bit)" - }, - "dropper_try_move_first": { - "title": "Try to move first", - "type": "boolean", - "default": True, - "description": - "Determines whether the dropper should try to move itself instead of copying itself" - " to target path" - } - } - }, - "logging": { - "title": "Logging", - "type": "object", - "properties": { - "dropper_log_path_linux": { - "title": "Dropper log file path on Linux", - "type": "string", - "default": "/tmp/user-1562", - "description": "The fullpath of the dropper log file on Linux" - }, - "dropper_log_path_windows": { - "title": "Dropper log file path on Windows", - "type": "string", - "default": "%temp%\\~df1562.tmp", - "description": "The fullpath of the dropper log file on Windows" - }, - "monkey_log_path_linux": { - "title": "Monkey log file path on Linux", - "type": "string", - "default": "/tmp/user-1563", - "description": "The fullpath of the monkey log file on Linux" - }, - "monkey_log_path_windows": { - "title": "Monkey log file path on Windows", - "type": "string", - "default": "%temp%\\~df1563.tmp", - "description": "The fullpath of the monkey log file on Windows" - }, - "send_log_to_server": { - "title": "Send log to server", - "type": "boolean", - "default": True, - "description": "Determines whether the monkey sends its log to the Monkey Island server" - } - } - }, - "exploits": { - "title": "Exploits", - "type": "object", - "properties": { - "exploit_lm_hash_list": { - "title": "Exploit LM hash list", - "type": "array", - "uniqueItems": True, - "items": { - "type": "string" - }, - "default": [], - "description": "List of LM hashes to use on exploits using credentials" - }, - "exploit_ntlm_hash_list": { - "title": "Exploit NTLM hash list", - "type": "array", - "uniqueItems": True, - "items": { - "type": "string" - }, - "default": [], - "description": "List of NTLM hashes to use on exploits using credentials" - }, - "exploit_ssh_keys": { - "title": "SSH key pairs list", - "type": "array", - "uniqueItems": True, - "default": [], - "items": { - "type": "string" - }, - "description": "List of SSH key pairs to use, when trying to ssh into servers" - } - } - }, - "testing": { - "title": "Testing", - "type": "object", - "properties": { - "export_monkey_telems": { - "title": "Export monkey telemetries", - "type": "boolean", - "default": False, - "description": "Exports unencrypted telemetries that can be used for tests in development." - " Do not turn on!" - } - } - } - } - }, - "cnc": { - "title": "Monkey Island", - "type": "object", - "properties": { - "servers": { - "title": "Servers", - "type": "object", - "properties": { - "command_servers": { - "title": "Command servers", - "type": "array", - "uniqueItems": True, - "items": { - "type": "string" - }, - "default": [ - "192.0.2.0:5000" - ], - "description": "List of command servers to try and communicate with (format is :)" - }, - "internet_services": { - "title": "Internet services", - "type": "array", - "uniqueItems": True, - "items": { - "type": "string" - }, - "default": [ - "monkey.guardicore.com", - "www.google.com" - ], - "description": - "List of internet services to try and communicate with to determine internet" - " connectivity (use either ip or domain)" - }, - "current_server": { - "title": "Current server", - "type": "string", - "default": "192.0.2.0:5000", - "description": "The current command server the monkey is communicating with" - } - } - }, - } - }, - "exploits": { - "title": "Exploits", - "type": "object", - "properties": { - "general": { - "title": "General", - "type": "object", - "properties": { - "exploiter_classes": { - "title": "Exploits", - "type": "array", - "uniqueItems": True, - "items": { - "$ref": "#/definitions/exploiter_classes" - }, - "default": [ - "SmbExploiter", - "WmiExploiter", - "SSHExploiter", - "ShellShockExploiter", - "SambaCryExploiter", - "ElasticGroovyExploiter", - "Struts2Exploiter", - "WebLogicExploiter", - "HadoopExploiter", - "VSFTPDExploiter", - "MSSQLExploiter" - ], - "description": - "Determines which exploits to use. " + WARNING_SIGN - + " Note that using unsafe exploits may cause crashes of the exploited machine/service" - }, - "skip_exploit_if_file_exist": { - "title": "Skip exploit if file exists", - "type": "boolean", - "default": False, - "description": "Determines whether the monkey should skip the exploit if the monkey's file" - " is already on the remote machine" - } - } - }, - "ms08_067": { - "title": "MS08_067", - "type": "object", - "properties": { - "ms08_067_exploit_attempts": { - "title": "MS08_067 exploit attempts", - "type": "integer", - "default": 5, - "description": "Number of attempts to exploit using MS08_067" - }, - "user_to_add": { - "title": "Remote user", - "type": "string", - "default": "Monkey_IUSER_SUPPORT", - "description": "Username to add on successful exploit" - }, - "remote_user_pass": { - "title": "Remote user password", - "type": "string", - "default": "Password1!", - "description": "Password to use for created user" - } - } - }, - "sambacry": { - "title": "SambaCry", - "type": "object", - "properties": { - "sambacry_trigger_timeout": { - "title": "SambaCry trigger timeout", - "type": "integer", - "default": 5, - "description": "Timeout (in seconds) of SambaCry trigger" - }, - "sambacry_folder_paths_to_guess": { - "title": "SambaCry folder paths to guess", - "type": "array", - "uniqueItems": True, - "items": { - "type": "string" - }, - "default": [ - '/', - '/mnt', - '/tmp', - '/storage', - '/export', - '/share', - '/shares', - '/home' - ], - "description": "List of full paths to share folder for SambaCry to guess" - }, - "sambacry_shares_not_to_check": { - "title": "SambaCry shares not to check", - "type": "array", - "uniqueItems": True, - "items": { - "type": "string" - }, - "default": [ - "IPC$", "print$" - ], - "description": "These shares won't be checked when exploiting with SambaCry" - } - } - }, - "smb_service": { - "title": "SMB service", - "type": "object", - "properties": { - "smb_download_timeout": { - "title": "SMB download timeout", - "type": "integer", - "default": 300, - "description": - "Timeout (in seconds) for SMB download operation (used in various exploits using SMB)" - }, - "smb_service_name": { - "title": "SMB service name", - "type": "string", - "default": "InfectionMonkey", - "description": "Name of the SMB service that will be set up to download monkey" - } - } - } - } - }, - "network": { - "title": "Network", - "type": "object", - "properties": { - "tcp_scanner": { - "title": "TCP scanner", - "type": "object", - "properties": { - "HTTP_PORTS": { - "title": "HTTP ports", - "type": "array", - "uniqueItems": True, - "items": { - "type": "integer" - }, - "default": [ - 80, - 8080, - 443, - 8008, - 7001 - ], - "description": "List of ports the monkey will check if are being used for HTTP" - }, - "tcp_target_ports": { - "title": "TCP target ports", - "type": "array", - "uniqueItems": True, - "items": { - "type": "integer" - }, - "default": [ - 22, - 2222, - 445, - 135, - 3389, - 80, - 8080, - 443, - 8008, - 3306, - 9200, - 7001, - 8088 - ], - "description": "List of TCP ports the monkey will check whether they're open" - }, - "tcp_scan_interval": { - "title": "TCP scan interval", - "type": "integer", - "default": 0, - "description": "Time to sleep (in milliseconds) between scans" - }, - "tcp_scan_timeout": { - "title": "TCP scan timeout", - "type": "integer", - "default": 3000, - "description": "Maximum time (in milliseconds) to wait for TCP response" - }, - "tcp_scan_get_banner": { - "title": "TCP scan - get banner", - "type": "boolean", - "default": True, - "description": "Determines whether the TCP scan should try to get the banner" - } - } - }, - "ping_scanner": { - "title": "Ping scanner", - "type": "object", - "properties": { - "ping_scan_timeout": { - "title": "Ping scan timeout", - "type": "integer", - "default": 1000, - "description": "Maximum time (in milliseconds) to wait for ping response" - } - } - } - } - } - }, - "options": { - "collapsed": True - } -} diff --git a/monkey/monkey_island/cc/services/config_schema/__init__.py b/monkey/monkey_island/cc/services/config_schema/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/services/config_schema/basic.py b/monkey/monkey_island/cc/services/config_schema/basic.py new file mode 100644 index 000000000..06d2eda7c --- /dev/null +++ b/monkey/monkey_island/cc/services/config_schema/basic.py @@ -0,0 +1,75 @@ +BASIC = { + "title": "Exploits", + "type": "object", + "primary": True, + "properties": { + "exploiters": { + "title": "Exploiters", + "type": "object", + "description": "Choose which exploiters the Monkey will attempt.", + "properties": { + "exploiter_classes": { + "title": "Exploiters", + "type": "array", + "uniqueItems": True, + "items": { + "$ref": "#/definitions/exploiter_classes" + }, + "default": [ + "SmbExploiter", + "WmiExploiter", + "SSHExploiter", + "ShellShockExploiter", + "SambaCryExploiter", + "ElasticGroovyExploiter", + "Struts2Exploiter", + "WebLogicExploiter", + "HadoopExploiter", + "VSFTPDExploiter", + "MSSQLExploiter" + ] + } + } + }, + "credentials": { + "title": "Credentials", + "type": "object", + "properties": { + "exploit_user_list": { + "title": "Exploit user list", + "type": "array", + "uniqueItems": True, + "items": { + "type": "string" + }, + "default": [ + "Administrator", + "root", + "user" + ], + "description": "List of user names that will be used by exploiters that need credentials, like " + "SSH brute-forcing." + }, + "exploit_password_list": { + "title": "Exploit password list", + "type": "array", + "uniqueItems": True, + "items": { + "type": "string" + }, + "default": [ + "root", + "123456", + "password", + "123456789", + "qwerty", + "111111", + "iloveyou" + ], + "description": "List of passwords that will be used by exploiters that need credentials, like " + "SSH brute-forcing." + } + } + } + } +} diff --git a/monkey/monkey_island/cc/services/config_schema/basic_network.py b/monkey/monkey_island/cc/services/config_schema/basic_network.py new file mode 100644 index 000000000..33467690a --- /dev/null +++ b/monkey/monkey_island/cc/services/config_schema/basic_network.py @@ -0,0 +1,95 @@ +from common.data.validation_formats import IP, IP_RANGE +from monkey_island.cc.services.utils.typographic_symbols import WARNING_SIGN + +BASIC_NETWORK = { + "title": "Network", + "type": "object", + "properties": { + "scope": { + "title": "Scope", + "type": "object", + "properties": { + "blocked_ips": { + "title": "Blocked IPs", + "type": "array", + "uniqueItems": True, + "items": { + "type": "string", + "format": IP, + }, + "default": [ + ], + "description": "List of IPs that the Monkey will not scan.", + "info": "The Monkey scans its subnet if \"Local network scan\" is ticked. " + "Additionally the monkey scans machines according to \"Scan target list\"." + }, + "local_network_scan": { + "title": "Local network scan", + "type": "boolean", + "default": True, + "description": "Determines whether the Monkey will scan the local subnets of machines it runs on, " + "in addition to the IPs that are configured manually in the \"Scan target list\"." + }, + "depth": { + "title": "Scan depth", + "type": "integer", + "minimum": 1, + "default": 2, + "description": + "Amount of hops allowed for the Monkey to spread from the Island server. \n" + + WARNING_SIGN + + " Note that setting this value too high may result in the Monkey propagating too far, " + "if the \"Local network scan\" is enabled." + }, + "subnet_scan_list": { + "title": "Scan target list", + "type": "array", + "uniqueItems": True, + "items": { + "type": "string", + "format": IP_RANGE + }, + "default": [ + ], + "description": + "List of targets the Monkey will try to scan. Targets can be IPs, subnets or hosts." + " Examples:\n" + "\tTarget a specific IP: \"192.168.0.1\"\n" + "\tTarget a subnet using a network range: \"192.168.0.5-192.168.0.20\"\n" + "\tTarget a subnet using an IP mask: \"192.168.0.5/24\"\n" + "\tTarget a specific host: \"printer.example\"" + } + } + }, + "network_analysis": { + "title": "Network Analysis", + "type": "object", + "properties": { + "inaccessible_subnets": { + "title": "Network segmentation testing", + "type": "array", + "uniqueItems": True, + "items": { + "type": "string", + "format": IP_RANGE + }, + "default": [ + ], + "description": + "Test for network segmentation by providing a list of network segments " + "that should NOT be accessible to each other.\n\n" + "For example, if you configured the following three segments: " + "\"10.0.0.0/24\", \"11.0.0.2/32\", and \"12.2.3.0/24\", " + "a Monkey running on 10.0.0.5 will try to access machines in the following subnets: " + "11.0.0.2/32, 12.2.3.0/24. An alert on successful cross-segment connections " + "will be shown in the reports. \n\n" + "Network segments can be IPs, subnets or hosts. Examples:\n" + "\tDefine a single-IP segment: \"192.168.0.1\"\n" + "\tDefine a segment using a network range: \"192.168.0.5-192.168.0.20\"\n" + "\tDefine a segment using an subnet IP mask: \"192.168.0.5/24\"\n" + "\tDefine a single-host segment: \"printer.example\"" + } + } + } + } +} diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema.py b/monkey/monkey_island/cc/services/config_schema/config_schema.py new file mode 100644 index 000000000..04e586e71 --- /dev/null +++ b/monkey/monkey_island/cc/services/config_schema/config_schema.py @@ -0,0 +1,33 @@ +from monkey_island.cc.services.config_schema.basic import BASIC +from monkey_island.cc.services.config_schema.basic_network import BASIC_NETWORK +from monkey_island.cc.services.config_schema.definitions.exploiter_classes import \ + EXPLOITER_CLASSES +from monkey_island.cc.services.config_schema.definitions.finger_classes import \ + FINGER_CLASSES +from monkey_island.cc.services.config_schema.definitions.post_breach_actions import \ + POST_BREACH_ACTIONS +from monkey_island.cc.services.config_schema.definitions.system_info_collector_classes import \ + SYSTEM_INFO_COLLECTOR_CLASSES +from monkey_island.cc.services.config_schema.internal import INTERNAL +from monkey_island.cc.services.config_schema.monkey import MONKEY + +SCHEMA = { + "title": "Monkey", + "type": "object", + "definitions": { + "exploiter_classes": EXPLOITER_CLASSES, + "system_info_collector_classes": SYSTEM_INFO_COLLECTOR_CLASSES, + "post_breach_actions": POST_BREACH_ACTIONS, + "finger_classes": FINGER_CLASSES + + }, + "properties": { + "basic": BASIC, + "basic_network": BASIC_NETWORK, + "monkey": MONKEY, + "internal": INTERNAL, + }, + "options": { + "collapsed": True + } +} diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py new file mode 100644 index 000000000..604ba2073 --- /dev/null +++ b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py @@ -0,0 +1,130 @@ +from monkey_island.cc.services.utils.typographic_symbols import WARNING_SIGN + +EXPLOITER_CLASSES = { + "title": "Exploit class", + "description": "Click on exploiter to get more information about it." + WARNING_SIGN + + " Note that using unsafe exploits may cause crashes of the exploited machine/service.", + "type": "string", + "anyOf": [ + { + "type": "string", + "enum": [ + "SmbExploiter" + ], + "title": "SMB Exploiter", + "attack_techniques": ["T1110", "T1075", "T1035"], + "info": "Brute forces using credentials provided by user and" + " hashes gathered by mimikatz.", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/smbexec/" + }, + { + "type": "string", + "enum": [ + "WmiExploiter" + ], + "title": "WMI Exploiter", + "attack_techniques": ["T1110", "T1106"], + "info": "Brute forces WMI (Windows Management Instrumentation) " + "using credentials provided by user and hashes gathered by mimikatz.", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/wmiexec/" + }, + { + "type": "string", + "enum": [ + "MSSQLExploiter" + ], + "title": "MSSQL Exploiter", + "attack_techniques": ["T1110"], + "info": "Tries to brute force into MsSQL server and uses insecure " + "configuration to execute commands on server.", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/mssql/" + }, + { + "type": "string", + "enum": [ + "Ms08_067_Exploiter" + ], + "title": "MS08-067 Exploiter (UNSAFE)", + "info": "Unsafe exploiter, that might cause system crash due to the use of buffer overflow. " + "Uses MS08-067 vulnerability.", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/ms08-067/" + }, + { + "type": "string", + "enum": [ + "SSHExploiter" + ], + "title": "SSH Exploiter", + "attack_techniques": ["T1110", "T1145", "T1106"], + "info": "Brute forces using credentials provided by user and SSH keys gathered from systems.", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/sshexec/" + }, + { + "type": "string", + "enum": [ + "ShellShockExploiter" + ], + "title": "ShellShock Exploiter", + "info": "CVE-2014-6271, based on logic from " + "https://github.com/nccgroup/shocker/blob/master/shocker.py .", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/shellshock/" + }, + { + "type": "string", + "enum": [ + "SambaCryExploiter" + ], + "title": "SambaCry Exploiter", + "info": "Bruteforces and searches for anonymous shares. Uses Impacket.", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/sambacry/" + }, + { + "type": "string", + "enum": [ + "ElasticGroovyExploiter" + ], + "title": "ElasticGroovy Exploiter", + "info": "CVE-2015-1427. Logic is based on Metasploit module.", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/elasticgroovy/" + }, + { + "type": "string", + "enum": [ + "Struts2Exploiter" + ], + "title": "Struts2 Exploiter", + "info": "Exploits struts2 java web framework. CVE-2017-5638. Logic based on " + "https://www.exploit-db.com/exploits/41570 .", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/struts2/" + }, + { + "type": "string", + "enum": [ + "WebLogicExploiter" + ], + "title": "WebLogic Exploiter", + "info": "Exploits CVE-2017-10271 and CVE-2019-2725 vulnerabilities on WebLogic server.", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/weblogic/" + }, + { + "type": "string", + "enum": [ + "HadoopExploiter" + ], + "title": "Hadoop/Yarn Exploiter", + "info": "Remote code execution on HADOOP server with YARN and default settings. " + "Logic based on https://github.com/vulhub/vulhub/tree/master/hadoop/unauthorized-yarn.", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/hadoop/" + }, + { + "type": "string", + "enum": [ + "VSFTPDExploiter" + ], + "title": "VSFTPD Exploiter", + "info": "Exploits a malicious backdoor that was added to the VSFTPD download archive. " + "Logic based on Metasploit module.", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/vsftpd/" + } + ] +} diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py new file mode 100644 index 000000000..6fe5e8fea --- /dev/null +++ b/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py @@ -0,0 +1,70 @@ +FINGER_CLASSES = { + "title": "Fingerprint class", + "description": "Fingerprint modules collect info about external services " + "Infection Monkey scans.", + "type": "string", + "anyOf": [ + { + "type": "string", + "enum": [ + "SMBFinger" + ], + "title": "SMBFinger", + "info": "Figures out if SMB is running and what's the version of it.", + "attack_techniques": ["T1210"] + }, + { + "type": "string", + "enum": [ + "SSHFinger" + ], + "title": "SSHFinger", + "info": "Figures out if SSH is running.", + "attack_techniques": ["T1210"] + }, + { + "type": "string", + "enum": [ + "PingScanner" + ], + "title": "PingScanner", + "info": "Tries to identify if host is alive and which OS it's running by ping scan." + }, + { + "type": "string", + "enum": [ + "HTTPFinger" + ], + "title": "HTTPFinger", + "info": "Checks if host has HTTP/HTTPS ports open." + }, + { + "type": "string", + "enum": [ + "MySQLFinger" + ], + "title": "MySQLFinger", + "info": "Checks if MySQL server is running and tries to get it's version.", + "attack_techniques": ["T1210"] + }, + { + "type": "string", + "enum": [ + "MSSQLFinger" + ], + "title": "MSSQLFinger", + "info": "Checks if Microsoft SQL service is running and tries to gather information about it.", + "attack_techniques": ["T1210"] + }, + + { + "type": "string", + "enum": [ + "ElasticFinger" + ], + "title": "ElasticFinger", + "info": "Checks if ElasticSearch is running and attempts to find it's version.", + "attack_techniques": ["T1210"] + } + ] +} diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py new file mode 100644 index 000000000..f3e2a9bfa --- /dev/null +++ b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py @@ -0,0 +1,75 @@ +POST_BREACH_ACTIONS = { + "title": "Post breach actions", + "description": "Runs scripts/commands on infected machines. These actions safely simulate what an adversary" + "might do after breaching a new machine. Used in ATT&CK and Zero trust reports.", + "type": "string", + "anyOf": [ + { + "type": "string", + "enum": [ + "BackdoorUser" + ], + "title": "Back door user", + "info": "Attempts to create a new user on the system and delete it afterwards.", + "attack_techniques": ["T1136"] + }, + { + "type": "string", + "enum": [ + "CommunicateAsNewUser" + ], + "title": "Communicate as new user", + "info": "Attempts to create a new user, create HTTPS requests as that user and delete the user " + "afterwards.", + "attack_techniques": ["T1136"] + }, + { + "type": "string", + "enum": [ + "ModifyShellStartupFiles" + ], + "title": "Modify shell startup files", + "info": "Attempts to modify shell startup files, like ~/.profile, ~/.bashrc, ~/.bash_profile " + "in linux, and profile.ps1 in windows. Reverts modifications done afterwards.", + "attack_techniques": ["T1156", "T1504"] + }, + { + "type": "string", + "enum": [ + "HiddenFiles" + ], + "title": "Hidden files and directories", + "info": "Attempts to create a hidden file and remove it afterward.", + "attack_techniques": ["T1158"] + }, + { + "type": "string", + "enum": [ + "TrapCommand" + ], + "title": "Trap", + "info": "On Linux systems, attempts to trap an interrupt signal in order to execute a command " + "upon receiving that signal. Removes the trap afterwards.", + "attack_techniques": ["T1154"] + }, + { + "type": "string", + "enum": [ + "ChangeSetuidSetgid" + ], + "title": "Setuid and Setgid", + "info": "On Linux systems, attempts to set the setuid and setgid bits of a new file. " + "Removes the file afterwards.", + "attack_techniques": ["T1166"] + }, + { + "type": "string", + "enum": [ + "ScheduleJobs" + ], + "title": "Job scheduling", + "info": "Attempts to create a scheduled job on the system and remove it.", + "attack_techniques": ["T1168", "T1053"] + } + ] +} diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py new file mode 100644 index 000000000..5f113f4a7 --- /dev/null +++ b/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py @@ -0,0 +1,68 @@ +from common.data.system_info_collectors_names import (AWS_COLLECTOR, + AZURE_CRED_COLLECTOR, + ENVIRONMENT_COLLECTOR, + HOSTNAME_COLLECTOR, + MIMIKATZ_COLLECTOR, + PROCESS_LIST_COLLECTOR) + +SYSTEM_INFO_COLLECTOR_CLASSES = { + "title": "System Information Collectors", + "description": "Click on a system info collector to find out what it collects.", + "type": "string", + "anyOf": [ + { + "type": "string", + "enum": [ + ENVIRONMENT_COLLECTOR + ], + "title": "Environment collector", + "info": "Collects information about machine's environment (on premise/GCP/AWS).", + "attack_techniques": ["T1082"] + }, + { + "type": "string", + "enum": [ + MIMIKATZ_COLLECTOR + ], + "title": "Mimikatz collector", + "info": "Collects credentials from Windows credential manager.", + "attack_techniques": ["T1003", "T1005"] + }, + { + "type": "string", + "enum": [ + AWS_COLLECTOR + ], + "title": "AWS collector", + "info": "If on AWS, collects more information about the AWS instance currently running on.", + "attack_techniques": ["T1082"] + }, + { + "type": "string", + "enum": [ + HOSTNAME_COLLECTOR + ], + "title": "Hostname collector", + "info": "Collects machine's hostname.", + "attack_techniques": ["T1082", "T1016"] + }, + { + "type": "string", + "enum": [ + PROCESS_LIST_COLLECTOR + ], + "title": "Process list collector", + "info": "Collects a list of running processes on the machine.", + "attack_techniques": ["T1082"] + }, + { + "type": "string", + "enum": [ + AZURE_CRED_COLLECTOR + ], + "title": "Azure credential collector", + "info": "Collects password credentials from Azure VMs", + "attack_techniques": ["T1003", "T1005"] + } + ] +} diff --git a/monkey/monkey_island/cc/services/config_schema/internal.py b/monkey/monkey_island/cc/services/config_schema/internal.py new file mode 100644 index 000000000..bdbae2461 --- /dev/null +++ b/monkey/monkey_island/cc/services/config_schema/internal.py @@ -0,0 +1,490 @@ +from monkey_island.cc.services.utils.typographic_symbols import WARNING_SIGN + +INTERNAL = { + "title": "Internal", + "type": "object", + "properties": { + "general": { + "title": "General", + "type": "object", + "properties": { + "singleton_mutex_name": { + "title": "Singleton mutex name", + "type": "string", + "default": "{2384ec59-0df8-4ab9-918c-843740924a28}", + "description": + "The name of the mutex used to determine whether the monkey is already running" + }, + "keep_tunnel_open_time": { + "title": "Keep tunnel open time", + "type": "integer", + "default": 60, + "description": "Time to keep tunnel open before going down after last exploit (in seconds)" + }, + "monkey_dir_name": { + "title": "Monkey's directory name", + "type": "string", + "default": r"monkey_dir", + "description": "Directory name for the directory which will contain all of the monkey files" + }, + "started_on_island": { + "title": "Started on island", + "type": "boolean", + "default": False, + "description": "Was exploitation started from island" + "(did monkey with max depth ran on island)" + }, + } + }, + "monkey": { + "title": "Monkey", + "type": "object", + "properties": { + "victims_max_find": { + "title": "Max victims to find", + "type": "integer", + "default": 100, + "description": "Determines the maximum number of machines the monkey is allowed to scan" + }, + "victims_max_exploit": { + "title": "Max victims to exploit", + "type": "integer", + "default": 100, + "description": + "Determines the maximum number of machines the monkey" + " is allowed to successfully exploit. " + WARNING_SIGN + + " Note that setting this value too high may result in the monkey propagating to " + "a high number of machines" + }, + "internet_services": { + "title": "Internet services", + "type": "array", + "uniqueItems": True, + "items": { + "type": "string" + }, + "default": [ + "monkey.guardicore.com", + "www.google.com" + ], + "description": + "List of internet services to try and communicate with to determine internet" + " connectivity (use either ip or domain)" + }, + "self_delete_in_cleanup": { + "title": "Self delete on cleanup", + "type": "boolean", + "default": True, + "description": "Should the monkey delete its executable when going down" + }, + "use_file_logging": { + "title": "Use file logging", + "type": "boolean", + "default": True, + "description": "Should the monkey dump to a log file" + }, + "serialize_config": { + "title": "Serialize config", + "type": "boolean", + "default": False, + "description": "Should the monkey dump its config on startup" + }, + "alive": { + "title": "Alive", + "type": "boolean", + "default": True, + "description": "Is the monkey alive" + } + } + }, + "island_server": { + "title": "Island server", + "type": "object", + "properties": { + "command_servers": { + "title": "Island server's IP's", + "type": "array", + "uniqueItems": True, + "items": { + "type": "string" + }, + "default": [ + "192.0.2.0:5000" + ], + "description": "List of command servers/network interfaces to try to communicate with " + "(format is :)" + }, + "current_server": { + "title": "Current server", + "type": "string", + "default": "192.0.2.0:5000", + "description": "The current command server the monkey is communicating with" + } + } + }, + "network": { + "title": "Network", + "type": "object", + "properties": { + "tcp_scanner": { + "title": "TCP scanner", + "type": "object", + "properties": { + "HTTP_PORTS": { + "title": "HTTP ports", + "type": "array", + "uniqueItems": True, + "items": { + "type": "integer" + }, + "default": [ + 80, + 8080, + 443, + 8008, + 7001 + ], + "description": "List of ports the monkey will check if are being used for HTTP" + }, + "tcp_target_ports": { + "title": "TCP target ports", + "type": "array", + "uniqueItems": True, + "items": { + "type": "integer" + }, + "default": [ + 22, + 2222, + 445, + 135, + 3389, + 80, + 8080, + 443, + 8008, + 3306, + 9200, + 7001, + 8088 + ], + "description": "List of TCP ports the monkey will check whether they're open" + }, + "tcp_scan_interval": { + "title": "TCP scan interval", + "type": "integer", + "default": 0, + "description": "Time to sleep (in milliseconds) between scans" + }, + "tcp_scan_timeout": { + "title": "TCP scan timeout", + "type": "integer", + "default": 3000, + "description": "Maximum time (in milliseconds) to wait for TCP response" + }, + "tcp_scan_get_banner": { + "title": "TCP scan - get banner", + "type": "boolean", + "default": True, + "description": "Determines whether the TCP scan should try to get the banner" + } + } + }, + "ping_scanner": { + "title": "Ping scanner", + "type": "object", + "properties": { + "ping_scan_timeout": { + "title": "Ping scan timeout", + "type": "integer", + "default": 1000, + "description": "Maximum time (in milliseconds) to wait for ping response" + } + } + } + } + }, + "classes": { + "title": "Classes", + "type": "object", + "properties": { + "finger_classes": { + "title": "Fingerprint classes", + "type": "array", + "uniqueItems": True, + "items": { + "$ref": "#/definitions/finger_classes" + }, + "default": [ + "SMBFinger", + "SSHFinger", + "PingScanner", + "HTTPFinger", + "MySQLFinger", + "MSSQLFinger", + "ElasticFinger" + ] + } + } + }, + "kill_file": { + "title": "Kill file", + "type": "object", + "properties": { + "kill_file_path_windows": { + "title": "Kill file path on Windows", + "type": "string", + "default": "%windir%\\monkey.not", + "description": "Path of file which kills monkey if it exists (on Windows)" + }, + "kill_file_path_linux": { + "title": "Kill file path on Linux", + "type": "string", + "default": "/var/run/monkey.not", + "description": "Path of file which kills monkey if it exists (on Linux)" + } + } + }, + "dropper": { + "title": "Dropper", + "type": "object", + "properties": { + "dropper_set_date": { + "title": "Dropper sets date", + "type": "boolean", + "default": True, + "description": + "Determines whether the dropper should set the monkey's file date to be the same as" + " another file" + }, + "dropper_date_reference_path_windows": { + "title": "Dropper date reference path (Windows)", + "type": "string", + "default": "%windir%\\system32\\kernel32.dll", + "description": + "Determines which file the dropper should copy the date from if it's configured to do" + " so on Windows (use fullpath)" + }, + "dropper_date_reference_path_linux": { + "title": "Dropper date reference path (Linux)", + "type": "string", + "default": "/bin/sh", + "description": + "Determines which file the dropper should copy the date from if it's configured to do" + " so on Linux (use fullpath)" + }, + "dropper_target_path_linux": { + "title": "Dropper target path on Linux", + "type": "string", + "default": "/tmp/monkey", + "description": "Determines where should the dropper place the monkey on a Linux machine" + }, + "dropper_target_path_win_32": { + "title": "Dropper target path on Windows (32bit)", + "type": "string", + "default": "C:\\Windows\\temp\\monkey32.exe", + "description": "Determines where should the dropper place the monkey on a Windows machine " + "(32bit)" + }, + "dropper_target_path_win_64": { + "title": "Dropper target path on Windows (64bit)", + "type": "string", + "default": "C:\\Windows\\temp\\monkey64.exe", + "description": "Determines where should the dropper place the monkey on a Windows machine " + "(64 bit)" + }, + "dropper_try_move_first": { + "title": "Try to move first", + "type": "boolean", + "default": True, + "description": + "Determines whether the dropper should try to move itself instead of copying itself" + " to target path" + } + } + }, + "logging": { + "title": "Logging", + "type": "object", + "properties": { + "dropper_log_path_linux": { + "title": "Dropper log file path on Linux", + "type": "string", + "default": "/tmp/user-1562", + "description": "The fullpath of the dropper log file on Linux" + }, + "dropper_log_path_windows": { + "title": "Dropper log file path on Windows", + "type": "string", + "default": "%temp%\\~df1562.tmp", + "description": "The fullpath of the dropper log file on Windows" + }, + "monkey_log_path_linux": { + "title": "Monkey log file path on Linux", + "type": "string", + "default": "/tmp/user-1563", + "description": "The fullpath of the monkey log file on Linux" + }, + "monkey_log_path_windows": { + "title": "Monkey log file path on Windows", + "type": "string", + "default": "%temp%\\~df1563.tmp", + "description": "The fullpath of the monkey log file on Windows" + }, + "send_log_to_server": { + "title": "Send log to server", + "type": "boolean", + "default": True, + "description": "Determines whether the monkey sends its log to the Monkey Island server" + } + } + }, + "exploits": { + "title": "Exploits", + "type": "object", + "properties": { + "exploit_lm_hash_list": { + "title": "Exploit LM hash list", + "type": "array", + "uniqueItems": True, + "items": { + "type": "string" + }, + "default": [], + "description": "List of LM hashes to use on exploits using credentials" + }, + "exploit_ntlm_hash_list": { + "title": "Exploit NTLM hash list", + "type": "array", + "uniqueItems": True, + "items": { + "type": "string" + }, + "default": [], + "description": "List of NTLM hashes to use on exploits using credentials" + }, + "exploit_ssh_keys": { + "title": "SSH key pairs list", + "type": "array", + "uniqueItems": True, + "default": [], + "items": { + "type": "string" + }, + "description": "List of SSH key pairs to use, when trying to ssh into servers" + }, "general": { + "title": "General", + "type": "object", + "properties": { + "skip_exploit_if_file_exist": { + "title": "Skip exploit if file exists", + "type": "boolean", + "default": False, + "description": "Determines whether the monkey should skip the exploit if the monkey's file" + " is already on the remote machine" + } + } + }, + "ms08_067": { + "title": "MS08_067", + "type": "object", + "properties": { + "ms08_067_exploit_attempts": { + "title": "MS08_067 exploit attempts", + "type": "integer", + "default": 5, + "description": "Number of attempts to exploit using MS08_067" + }, + "user_to_add": { + "title": "Remote user", + "type": "string", + "default": "Monkey_IUSER_SUPPORT", + "description": "Username to add on successful exploit" + }, + "remote_user_pass": { + "title": "Remote user password", + "type": "string", + "default": "Password1!", + "description": "Password to use for created user" + } + } + }, + "sambacry": { + "title": "SambaCry", + "type": "object", + "properties": { + "sambacry_trigger_timeout": { + "title": "SambaCry trigger timeout", + "type": "integer", + "default": 5, + "description": "Timeout (in seconds) of SambaCry trigger" + }, + "sambacry_folder_paths_to_guess": { + "title": "SambaCry folder paths to guess", + "type": "array", + "uniqueItems": True, + "items": { + "type": "string" + }, + "default": [ + '/', + '/mnt', + '/tmp', + '/storage', + '/export', + '/share', + '/shares', + '/home' + ], + "description": "List of full paths to share folder for SambaCry to guess" + }, + "sambacry_shares_not_to_check": { + "title": "SambaCry shares not to check", + "type": "array", + "uniqueItems": True, + "items": { + "type": "string" + }, + "default": [ + "IPC$", "print$" + ], + "description": "These shares won't be checked when exploiting with SambaCry" + } + } + } + }, + "smb_service": { + "title": "SMB service", + "type": "object", + "properties": { + "smb_download_timeout": { + "title": "SMB download timeout", + "type": "integer", + "default": 300, + "description": + "Timeout (in seconds) for SMB download operation (used in various exploits using SMB)" + }, + "smb_service_name": { + "title": "SMB service name", + "type": "string", + "default": "InfectionMonkey", + "description": "Name of the SMB service that will be set up to download monkey" + } + } + } + }, + "testing": { + "title": "Testing", + "type": "object", + "properties": { + "export_monkey_telems": { + "title": "Export monkey telemetries", + "type": "boolean", + "default": False, + "description": "Exports unencrypted telemetries that can be used for tests in development." + " Do not turn on!" + } + } + } + } +} diff --git a/monkey/monkey_island/cc/services/config_schema/monkey.py b/monkey/monkey_island/cc/services/config_schema/monkey.py new file mode 100644 index 000000000..dd10cb35b --- /dev/null +++ b/monkey/monkey_island/cc/services/config_schema/monkey.py @@ -0,0 +1,128 @@ +from common.data.system_info_collectors_names import (AWS_COLLECTOR, + AZURE_CRED_COLLECTOR, + ENVIRONMENT_COLLECTOR, + HOSTNAME_COLLECTOR, + MIMIKATZ_COLLECTOR, + PROCESS_LIST_COLLECTOR) + +MONKEY = { + "title": "Monkey", + "type": "object", + "properties": { + "post_breach": { + "title": "Post breach", + "type": "object", + "properties": { + "custom_PBA_linux_cmd": { + "title": "Linux post breach command", + "type": "string", + "default": "", + "description": "Linux command to be executed after breaching." + }, + "PBA_linux_file": { + "title": "Linux post breach file", + "type": "string", + "format": "data-url", + "description": "File to be executed after breaching. " + "If you want custom execution behavior, " + "specify it in 'Linux post breach command' field. " + "Reference your file by filename." + }, + "custom_PBA_windows_cmd": { + "title": "Windows post breach command", + "type": "string", + "default": "", + "description": "Windows command to be executed after breaching." + }, + "PBA_windows_file": { + "title": "Windows post breach file", + "type": "string", + "format": "data-url", + "description": "File to be executed after breaching. " + "If you want custom execution behavior, " + "specify it in 'Windows post breach command' field. " + "Reference your file by filename." + }, + "PBA_windows_filename": { + "title": "Windows PBA filename", + "type": "string", + "default": "" + }, + "PBA_linux_filename": { + "title": "Linux PBA filename", + "type": "string", + "default": "" + }, + "post_breach_actions": { + "title": "Post breach actions", + "type": "array", + "uniqueItems": True, + "items": { + "$ref": "#/definitions/post_breach_actions" + }, + "default": [ + "BackdoorUser", + "CommunicateAsNewUser", + "ModifyShellStartupFiles", + "HiddenFiles", + "TrapCommand", + "ChangeSetuidSetgid", + "ScheduleJobs" + ] + }, + } + }, + "system_info": { + "title": "System info", + "type": "object", + "properties": { + "system_info_collector_classes": { + "title": "System info collectors", + "type": "array", + "uniqueItems": True, + "items": { + "$ref": "#/definitions/system_info_collector_classes" + }, + "default": [ + ENVIRONMENT_COLLECTOR, + AWS_COLLECTOR, + HOSTNAME_COLLECTOR, + PROCESS_LIST_COLLECTOR, + MIMIKATZ_COLLECTOR, + AZURE_CRED_COLLECTOR + ] + }, + } + }, + "persistent_scanning": { + "title": "Persistent scanning", + "type": "object", + "properties": { + "max_iterations": { + "title": "Max iterations", + "type": "integer", + "default": 1, + "minimum": 1, + "description": "Determines how many iterations of the monkey's full lifecycle should occur " + "(how many times to do the scan)" + }, + "timeout_between_iterations": { + "title": "Wait time between iterations", + "type": "integer", + "default": 100, + "minimum": 0, + "description": + "Determines for how long (in seconds) should the monkey wait before starting another scan" + }, + "retry_failed_explotation": { + "title": "Retry failed exploitation", + "type": "boolean", + "default": True, + "description": + "Determines whether the monkey should retry exploiting machines" + " it didn't successfully exploit on previous scans" + } + } + } + } +} diff --git a/monkey/monkey_island/cc/services/post_breach_files.py b/monkey/monkey_island/cc/services/post_breach_files.py index fe4f47ddd..e3e6266cb 100644 --- a/monkey/monkey_island/cc/services/post_breach_files.py +++ b/monkey/monkey_island/cc/services/post_breach_files.py @@ -9,8 +9,8 @@ __author__ = "VakarisZ" logger = logging.getLogger(__name__) # Where to find file names in config -PBA_WINDOWS_FILENAME_PATH = ['monkey', 'behaviour', 'PBA_windows_filename'] -PBA_LINUX_FILENAME_PATH = ['monkey', 'behaviour', 'PBA_linux_filename'] +PBA_WINDOWS_FILENAME_PATH = ['monkey', 'post_breach', 'PBA_windows_filename'] +PBA_LINUX_FILENAME_PATH = ['monkey', 'post_breach', 'PBA_linux_filename'] UPLOADS_DIR = Path('monkey_island', 'cc', 'userUploads') @@ -41,5 +41,5 @@ def set_config_PBA_files(config_json): if monkey_island.cc.services.config.ConfigService.get_config(): linux_filename = monkey_island.cc.services.config.ConfigService.get_config_value(PBA_LINUX_FILENAME_PATH) windows_filename = monkey_island.cc.services.config.ConfigService.get_config_value(PBA_WINDOWS_FILENAME_PATH) - config_json['monkey']['behaviour']['PBA_linux_filename'] = linux_filename - config_json['monkey']['behaviour']['PBA_windows_filename'] = windows_filename + config_json['monkey']['post_breach']['PBA_linux_filename'] = linux_filename + config_json['monkey']['post_breach']['PBA_windows_filename'] = windows_filename diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 9880b0ed0..f557c6a01 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -618,7 +618,7 @@ class ReportService: @staticmethod def get_config_exploits(): - exploits_config_value = ['exploits', 'general', 'exploiter_classes'] + exploits_config_value = ['basic', 'exploiters', 'exploiter_classes'] default_exploits = ConfigService.get_default_config(False) for namespace in exploits_config_value: default_exploits = default_exploits[namespace] @@ -632,11 +632,11 @@ class ReportService: @staticmethod def get_config_ips(): - return ConfigService.get_config_value(['basic_network', 'general', 'subnet_scan_list'], True, True) + return ConfigService.get_config_value(['basic_network', 'scope', 'subnet_scan_list'], True, True) @staticmethod def get_config_scan(): - return ConfigService.get_config_value(['basic_network', 'general', 'local_network_scan'], True, True) + return ConfigService.get_config_value(['basic_network', 'scope', 'local_network_scan'], True, True) @staticmethod def get_issues_overview(issues, config_users, config_passwords): diff --git a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py index 0cb8fcb1e..fb54522b6 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py @@ -1,8 +1,6 @@ import copy -from common.data.post_breach_consts import ( - POST_BREACH_COMMUNICATE_AS_NEW_USER, - POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION) +from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER from monkey_island.cc.database import mongo from monkey_island.cc.models import Monkey from monkey_island.cc.services.telemetry.zero_trust_tests.communicate_as_new_user import \ diff --git a/monkey/monkey_island/cc/services/utils/__init__.py b/monkey/monkey_island/cc/services/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/services/utils/typographic_symbols.py b/monkey/monkey_island/cc/services/utils/typographic_symbols.py new file mode 100644 index 000000000..ab87f169e --- /dev/null +++ b/monkey/monkey_island/cc/services/utils/typographic_symbols.py @@ -0,0 +1 @@ +WARNING_SIGN = " \u26A0" diff --git a/monkey/monkey_island/cc/ui/.eslintrc b/monkey/monkey_island/cc/ui/.eslintrc index 3cdf79db7..2cd52bb98 100644 --- a/monkey/monkey_island/cc/ui/.eslintrc +++ b/monkey/monkey_island/cc/ui/.eslintrc @@ -41,7 +41,7 @@ "global-strict": 0, "no-underscore-dangle": 0, "no-console": 0, - "no-unused-vars": 1, + "no-unused-vars": [1, {"vars": "all", "args": "all", "argsIgnorePattern": "^_", "varsIgnorePattern": "^React$" }], "no-trailing-spaces": [ 1, { diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index c93487ca0..ab78673b9 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -8220,9 +8220,9 @@ } }, "marked": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz", - "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-1.1.1.tgz", + "integrity": "sha512-mJzT8D2yPxoPh7h0UXkB+dBj4FykPJ2OIfxAWeIHrvoHDkFxukV/29QxoFQoPM6RLEwhIFdJpmKBlqVM3s2ZIw==" }, "mathml-tag-names": { "version": "2.1.3", @@ -13649,28 +13649,6 @@ "react-base16-styling": "^0.5.1" } }, - "react-jsonschema-form": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.8.1.tgz", - "integrity": "sha512-aaDloxNAcGXOOOcdKOxxqEEn5oDlPUZgWcs8unXXB9vjBRgCF8rCm/wVSv1u2G5ih0j/BX6Ewd/WjI2g00lPdg==", - "requires": { - "@babel/runtime-corejs2": "^7.4.5", - "ajv": "^6.7.0", - "core-js": "^2.5.7", - "lodash": "^4.17.15", - "prop-types": "^15.5.8", - "react-is": "^16.8.4", - "react-lifecycles-compat": "^3.0.4", - "shortid": "^2.2.14" - }, - "dependencies": { - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" - } - } - }, "react-jsonschema-form-bs4": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/react-jsonschema-form-bs4/-/react-jsonschema-form-bs4-1.7.1.tgz", diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 77c91ec5c..cdab06af6 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -74,7 +74,8 @@ "file-saver": "^2.0.2", "filepond": "^4.18.0", "jwt-decode": "^2.2.0", - "marked": "^0.8.2", + "lodash": "^4.17.15", + "marked": "^1.1.1", "normalize.css": "^8.0.0", "npm": "^6.14.6", "pluralize": "^7.0.0", @@ -94,7 +95,6 @@ "react-graph-vis": "^1.0.5", "react-hot-loader": "^4.12.20", "react-json-tree": "^0.11.2", - "react-jsonschema-form": "^1.8.0", "react-jsonschema-form-bs4": "^1.7.1", "react-particles-js": "^3.2.1", "react-redux": "^5.1.2", diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index 6a3f18a5a..7ef373f05 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -97,7 +97,7 @@ class AppComponent extends AuthComponent { }; redirectTo = (userPath, targetPath) => { - let pathQuery = new RegExp(userPath + '[\/]?$', 'g'); + let pathQuery = new RegExp(userPath + '[/]?$', 'g'); if (window.location.pathname.match(pathQuery)) { return } diff --git a/monkey/monkey_island/cc/ui/src/components/SideNavComponent.js b/monkey/monkey_island/cc/ui/src/components/SideNavComponent.js index 373d07fd3..52fb3fb4e 100644 --- a/monkey/monkey_island/cc/ui/src/components/SideNavComponent.js +++ b/monkey/monkey_island/cc/ui/src/components/SideNavComponent.js @@ -49,7 +49,7 @@ class SideNavComponent extends React.Component {
  • { + isActive={(_match, location) => { return (location.pathname === '/report/attack' || location.pathname === '/report/zeroTrust' || location.pathname === '/report/security') diff --git a/monkey/monkey_island/cc/ui/src/components/attack/ConfigMatrixComponent.js b/monkey/monkey_island/cc/ui/src/components/attack/ConfigMatrixComponent.js index 75c2a2106..ff9a11766 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/ConfigMatrixComponent.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/ConfigMatrixComponent.js @@ -4,7 +4,7 @@ import Tooltip from 'react-tooltip-lite' import AuthComponent from '../AuthComponent'; import ReactTable from 'react-table'; import 'filepond/dist/filepond.min.css'; -import '../../styles/Tooltip.scss'; +import '../../styles/components/Tooltip.scss'; import {Col} from 'react-bootstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 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 ebe12f25b..95820b82f 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 @@ -65,5 +65,6 @@ export function renderUsageFields(usages) { export const ScanStatus = { UNSCANNED: 0, SCANNED: 1, - USED: 2 + USED: 2, + DISABLED: 3 }; diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1136.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1136.js index 55cd9966c..2df34c454 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1136.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1136.js @@ -1,7 +1,7 @@ import React from 'react'; import ReactTable from 'react-table'; import {renderMachineFromSystemData, ScanStatus} from './Helpers'; -import MitigationsComponent from "./MitigationsComponent"; +import MitigationsComponent from './MitigationsComponent'; class T1136 extends React.Component { diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/FieldWithInfo.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/FieldWithInfo.js new file mode 100644 index 000000000..8a0bc0c04 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/FieldWithInfo.js @@ -0,0 +1,20 @@ +import ObjectField from 'react-jsonschema-form-bs4/lib/components/fields/ArrayField'; +import * as React from 'react'; +import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; +import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle'; + +class FieldWithInfo extends React.Component { + + render() { + return ( + <> +
    + + {this.props.schema.info} +
    + + ); + } +} + +export default FieldWithInfo; diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/InternalConfig.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/InternalConfig.js new file mode 100644 index 000000000..784931cbb --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/InternalConfig.js @@ -0,0 +1,79 @@ +import Form from 'react-jsonschema-form-bs4'; +import React, {useState} from 'react'; +import {Nav} from 'react-bootstrap'; + +const sectionOrder = [ + 'network', + 'monkey', + 'island_server', + 'logging', + 'exploits', + 'dropper', + 'classes', + 'general', + 'kill_file', + 'testing' +]; +const initialSection = sectionOrder[0]; + +export default function InternalConfig(props) { + const { + schema, + uiSchema, + onChange, + customFormats, + className, + formData + } = props; + const [selectedSection, setSelectedSection] = useState(initialSection); + const [displayedSchema, setDisplayedSchema] = useState(getSchemaByKey(schema, initialSection)); + const [displayedSchemaUi, setDisplayedSchemaUi] = useState(uiSchema[initialSection]); + + const onInnerDataChange = (innerData) => { + formData[selectedSection] = innerData.formData; + onChange({formData: formData}); + } + + const setSection = (sectionKey) => { + setSelectedSection(sectionKey); + setDisplayedSchema(getSchemaByKey(schema, sectionKey)); + setDisplayedSchemaUi(uiSchema[sectionKey]); + } + + const renderNav = () => { + return () + } + + return (
    + {renderNav()} +
    + +
    +
    ) +} + +function getSchemaByKey(schema, key) { + let definitions = schema['definitions']; + return {definitions: definitions, properties: schema['properties'][key]['properties']}; +} + +function getNavTitle(schema, key) { + return schema.properties[key].title; +} diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/PbaInput.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/PbaInput.js new file mode 100644 index 000000000..507173b6f --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/PbaInput.js @@ -0,0 +1,79 @@ +import React from 'react'; +import AuthComponent from '../AuthComponent'; + +import {FilePond} from 'react-filepond'; +import 'filepond/dist/filepond.min.css'; + + +class PbaInput extends AuthComponent { + + constructor(props) { + super(props); + // set schema from server + this.state = this.getStateFromProps(this.props); + } + + getStateFromProps(props){ + let options = props.options + // set schema from server + return { + filename: options.filename, + apiEndpoint: options.apiEndpoint, + setPbaFilename: options.setPbaFilename + }; + } + + componentDidUpdate(prevProps, _prevState, _snapshot) { + if(prevProps.options.filename !== this.props.options.filename && this.props.options.filename === ''){ + this.setState({filename: this.props.options.filename}) + } + } + + getPBAfile() { + if (this.state.filename) { + return PbaInput.getFullPBAfile(this.state.filename) + } + } + + static getFullPBAfile(filename) { + return [{ + source: filename, + options: { + type: 'limbo' + } + }]; + } + + getServerParams(path) { + return { + url: path, + process: this.getRequestParams(), + revert: this.getRequestParams(), + restore: this.getRequestParams(), + load: this.getRequestParams(), + fetch: this.getRequestParams() + } + } + + getRequestParams() { + return {headers: {'Authorization': this.jwtHeader}} + } + + render() { + return ( { + if (fileItems.length > 0) { + this.state.setPbaFilename(fileItems[0].file.name) + } else { + this.state.setPbaFilename('') + } + }} + />) + } +} + + +export default PbaInput; diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js new file mode 100644 index 000000000..aab3f1899 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js @@ -0,0 +1,92 @@ +import AdvancedMultiSelect from '../ui-components/AdvancedMultiSelect'; +import PbaInput from './PbaInput'; +import {API_PBA_LINUX, API_PBA_WINDOWS} from '../pages/ConfigurePage'; +import FieldWithInfo from './FieldWithInfo'; + +export default function UiSchema(props) { + const UiSchema = { + basic: { + 'ui:order': ['exploiters', 'credentials'], + exploiters: { + exploiter_classes: { + classNames: 'config-template-no-header', + 'ui:widget': AdvancedMultiSelect + } + } + }, + basic_network: { + 'ui:order': ['scope', 'network_analysis'], + scope: { + blocked_ips: { + 'ui:field': FieldWithInfo + }, + subnet_scan_list: { + format: 'ip-list' + } + } + }, + monkey: { + post_breach: { + post_breach_actions: { + classNames: 'config-template-no-header', + 'ui:widget': AdvancedMultiSelect + }, + custom_PBA_linux_cmd: { + 'ui:widget': 'textarea', + 'ui:emptyValue': '' + }, + PBA_linux_file: { + 'ui:widget': PbaInput, + 'ui:options': { + filename: props.PBA_linux_filename, + apiEndpoint: API_PBA_LINUX, + setPbaFilename: props.setPbaFilenameLinux + } + }, + custom_PBA_windows_cmd: { + 'ui:widget': 'textarea', + 'ui:emptyValue': '' + }, + PBA_windows_file: { + 'ui:widget': PbaInput, + 'ui:options': { + filename: props.PBA_windows_filename, + apiEndpoint: API_PBA_WINDOWS, + setPbaFilename: props.setPbaFilenameWindows + } + }, + PBA_linux_filename: { + classNames: 'linux-pba-file-info', + 'ui:emptyValue': '' + }, + PBA_windows_filename: { + classNames: 'windows-pba-file-info', + 'ui:emptyValue': '' + } + }, + system_info: { + system_info_collector_classes: { + classNames: 'config-template-no-header', + 'ui:widget': AdvancedMultiSelect + } + } + }, + internal: { + general: { + started_on_island: {'ui:widget': 'hidden'} + }, + classes: { + finger_classes: { + classNames: 'config-template-no-header', + 'ui:widget': AdvancedMultiSelect + } + }, + monkey: { + alive: { + classNames: 'config-field-hidden' + } + } + } + }; + return UiSchema[props.selectedSection] +} diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/ValidationErrorMessages.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/ValidationErrorMessages.js new file mode 100644 index 000000000..a5782948a --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/ValidationErrorMessages.js @@ -0,0 +1,14 @@ +import {IP, IP_RANGE} from './ValidationFormats'; + +export default function transformErrors(errors) { + return errors.map(error => { + if (error.name === 'type') { + error.message = 'Field can\'t be empty.' + } else if (error.name === 'format' && error.params.format === IP_RANGE) { + error.message = 'Invalid IP range, refer to description for valid examples.' + } else if (error.name === 'format' && error.params.format === IP) { + error.message = 'Invalid IP.' + } + return error; + }); +} diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/ValidationFormats.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/ValidationFormats.js new file mode 100644 index 000000000..ff0b4706b --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/ValidationFormats.js @@ -0,0 +1,24 @@ +const ipRegex = '((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' +const cidrNotationRegex = '([0-9]|1[0-9]|2[0-9]|3[0-2])' +const hostnameRegex = '^([A-Za-z0-9]*[A-Za-z]+[A-Za-z0-9]*.?)*([A-Za-z0-9]*[A-Za-z]+[A-Za-z0-9]*)$' + +export const IP_RANGE = 'ip-range'; +export const IP = 'ip'; + +export const formValidationFormats = { + [IP_RANGE]: buildIpRangeRegex(), + [IP]: buildIpRegex() +}; + +function buildIpRangeRegex(){ + return new RegExp([ + '^'+ipRegex+'$|', // Single: IP + '^'+ipRegex+'-'+ipRegex+'$|', // IP range: IP-IP + '^'+ipRegex+'/'+cidrNotationRegex+'$|', // IP range with cidr notation: IP/cidr + hostnameRegex // Hostname: target.tg + ].join('')) +} + +function buildIpRegex(){ + return new RegExp('^'+ipRegex+'$') +} diff --git a/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js b/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js index 84443ec75..27800cb97 100644 --- a/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js +++ b/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js @@ -101,9 +101,9 @@ class PreviewPaneComponent extends AuthComponent { .replace(/\\t/g, '\t') .replace(/\\b/g, '\b') .replace(/\\f/g, '\f') - .replace(/\\"/g, '\"') + .replace(/\\"/g, '"') .replace(/\\'/g, '\'') - .replace(/\\&/g, '\&'); + .replace(/\\&/g, '&'); } downloadLog(asset) { diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js b/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js index 2472f5adc..f3b3e190c 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js @@ -3,30 +3,30 @@ import Form from 'react-jsonschema-form-bs4'; import {Col, Modal, Nav, Button} from 'react-bootstrap'; import FileSaver from 'file-saver'; import AuthComponent from '../AuthComponent'; -import {FilePond} from 'react-filepond'; -import 'filepond/dist/filepond.min.css'; import ConfigMatrixComponent from '../attack/ConfigMatrixComponent'; +import UiSchema from '../configuration-components/UiSchema'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; -import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle'; import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck'; import {faExclamationCircle} from '@fortawesome/free-solid-svg-icons/faExclamationCircle'; +import {formValidationFormats} from '../configuration-components/ValidationFormats'; +import transformErrors from '../configuration-components/ValidationErrorMessages'; +import InternalConfig from '../configuration-components/InternalConfig'; const ATTACK_URL = '/api/attack'; const CONFIG_URL = '/api/configuration/island'; +export const API_PBA_LINUX = '/api/fileUpload/PBAlinux'; +export const API_PBA_WINDOWS = '/api/fileUpload/PBAwindows'; class ConfigurePageComponent extends AuthComponent { constructor(props) { super(props); - this.PBAwindowsPond = null; - this.PBAlinuxPond = null; this.currentSection = 'attack'; this.currentFormData = {}; this.initialConfig = {}; this.initialAttackConfig = {}; - this.sectionsOrder = ['attack', 'basic', 'basic_network', 'monkey', 'cnc', 'network', 'exploits', 'internal']; - this.uiSchemas = this.getUiSchemas(); - // set schema from server + this.sectionsOrder = ['attack', 'basic', 'basic_network', 'monkey', 'internal']; + this.state = { schema: {}, configuration: {}, @@ -34,53 +34,10 @@ class ConfigurePageComponent extends AuthComponent { lastAction: 'none', sections: [], selectedSection: 'attack', - PBAwinFile: [], - PBAlinuxFile: [], showAttackAlert: false }; } - getUiSchemas() { - return ({ - basic: {'ui:order': ['general', 'credentials']}, - basic_network: {}, - monkey: { - behaviour: { - custom_PBA_linux_cmd: { - 'ui:widget': 'textarea', - 'ui:emptyValue': '' - }, - PBA_linux_file: { - 'ui:widget': this.PBAlinux - }, - custom_PBA_windows_cmd: { - 'ui:widget': 'textarea', - 'ui:emptyValue': '' - }, - PBA_windows_file: { - 'ui:widget': this.PBAwindows - }, - PBA_linux_filename: { - classNames: 'linux-pba-file-info', - 'ui:emptyValue': '' - }, - PBA_windows_filename: { - classNames: 'windows-pba-file-info', - 'ui:emptyValue': '' - } - } - }, - cnc: {}, - network: {}, - exploits: {}, - internal: { - general: { - started_on_island: {'ui:widget': 'hidden'} - } - } - }) - } - setInitialConfig(config) { // Sets a reference to know if config was changed this.initialConfig = JSON.parse(JSON.stringify(config)); @@ -122,7 +79,7 @@ class ConfigurePageComponent extends AuthComponent { .then(res => res.json()) .then(data => { this.setInitialConfig(data.configuration); - this.setState({configuration: data.configuration}) + this.setState({configuration: data.configuration}); }) }; @@ -271,7 +228,6 @@ class ConfigurePageComponent extends AuthComponent { }; resetConfig = () => { - this.removePBAfiles(); this.authFetch(CONFIG_URL, { method: 'POST', @@ -280,14 +236,18 @@ class ConfigurePageComponent extends AuthComponent { }) .then(res => res.json()) .then(res => { - this.setState({ - lastAction: 'reset', - schema: res.schema, - configuration: res.configuration - }); - this.setInitialConfig(res.configuration); - this.props.onStatusChange(); - }); + this.setState({ + lastAction: 'reset', + schema: res.schema, + configuration: res.configuration + }); + this.setInitialConfig(res.configuration); + this.props.onStatusChange(); + } + ).then(() => { + this.removePBAfile(API_PBA_WINDOWS, this.setPbaFilenameWindows) + this.removePBAfile(API_PBA_LINUX, this.setPbaFilenameLinux) + }); this.authFetch(ATTACK_URL, { method: 'POST', headers: {'Content-Type': 'application/json'}, @@ -300,21 +260,17 @@ class ConfigurePageComponent extends AuthComponent { }) }; - removePBAfiles() { - // We need to clean files from widget, local state and configuration (to sync with bac end) - if (this.PBAwindowsPond !== null) { - this.PBAwindowsPond.removeFile(); - } - if (this.PBAlinuxPond !== null) { - this.PBAlinuxPond.removeFile(); - } + removePBAfile(apiEndpoint, setFilenameFnc) { + this.sendPbaRemoveRequest(apiEndpoint) + setFilenameFnc('') + } + + sendPbaRemoveRequest(apiEndpoint) { let request_options = { method: 'DELETE', headers: {'Content-Type': 'text/plain'} }; - this.authFetch('/api/fileUpload/PBAlinux', request_options); - this.authFetch('/api/fileUpload/PBAwindows', request_options); - this.setState({PBAlinuxFile: [], PBAwinFile: []}); + this.authFetch(apiEndpoint, request_options); } setConfigOnImport = (event) => { @@ -366,82 +322,6 @@ class ConfigurePageComponent extends AuthComponent { event.target.value = null; }; - PBAwindows = () => { - return ( { - this.setState({ - PBAwinFile: fileItems.map(fileItem => fileItem.file) - }) - }} - ref={ref => this.PBAwindowsPond = ref} - />) - }; - - PBAlinux = () => { - return ( { - this.setState({ - PBAlinuxFile: fileItems.map(fileItem => fileItem.file) - }) - }} - ref={ref => this.PBAlinuxPond = ref} - />) - }; - - getWinPBAfile() { - if (this.state.PBAwinFile.length !== 0) { - return ConfigurePageComponent.getMockPBAfile(this.state.PBAwinFile[0]) - } else if (this.state.configuration.monkey.behaviour.PBA_windows_filename) { - return ConfigurePageComponent.getFullPBAfile(this.state.configuration.monkey.behaviour.PBA_windows_filename) - } - } - - getLinuxPBAfile() { - if (this.state.PBAlinuxFile.length !== 0) { - return ConfigurePageComponent.getMockPBAfile(this.state.PBAlinuxFile[0]) - } else if (this.state.configuration.monkey.behaviour.PBA_linux_filename) { - return ConfigurePageComponent.getFullPBAfile(this.state.configuration.monkey.behaviour.PBA_linux_filename) - } - } - - static getFullPBAfile(filename) { - return [{ - source: filename, - options: { - type: 'limbo' - } - }]; - } - - static getMockPBAfile(mockFile) { - let pbaFile = [{ - source: mockFile.name, - options: { - type: 'limbo' - } - }]; - pbaFile[0].options.file = mockFile; - return pbaFile - } - renderMatrix = () => { return () }; - renderConfigContent = (displayedSchema) => { - return (
    - {this.renderBasicNetworkWarning()} -
    - -
    -
    ) - }; + let formProperties = {}; + formProperties['schema'] = displayedSchema + formProperties['uiSchema'] = UiSchema({ + PBA_linux_filename: this.state.configuration.monkey.post_breach.PBA_linux_filename, + PBA_windows_filename: this.state.configuration.monkey.post_breach.PBA_windows_filename, + setPbaFilenameWindows: this.setPbaFilenameWindows, + setPbaFilenameLinux: this.setPbaFilenameLinux, + selectedSection: this.state.selectedSection + }) + formProperties['formData'] = this.state.configuration[this.state.selectedSection]; + formProperties['onChange'] = this.onChange; + formProperties['customFormats'] = formValidationFormats; + formProperties['transformErrors'] = transformErrors; + formProperties['className'] = 'config-form'; + formProperties['liveValidate'] = true; - renderBasicNetworkWarning = () => { - if (this.state.selectedSection === 'basic_network') { - return (
    - - The Monkey scans its subnet if 'Local network scan' is ticked. Additionally the monkey scans machines - according to its range class. -
    ) + if (this.state.selectedSection === 'internal') { + return () } else { - return (
    ) + return ( +
    +
    + +
    +
    + ) } }; + setPbaFilenameWindows = (filename) => { + let config = this.state.configuration + config.monkey.post_breach.PBA_windows_filename = filename + this.setState({ + configuration: config + }) + } + + setPbaFilenameLinux = (filename) => { + let config = this.state.configuration + config.monkey.post_breach.PBA_linux_filename = filename + this.setState({ + configuration: config + }) + } + renderNav = () => { return () }; @@ -495,6 +397,7 @@ class ConfigurePageComponent extends AuthComponent { displayedSchema = this.state.schema['properties'][this.state.selectedSection]; displayedSchema['definitions'] = this.state.schema['definitions']; } + let content = ''; if (this.state.selectedSection === 'attack' && Object.entries(this.state.attackConfig).length !== 0) { content = this.renderMatrix() diff --git a/monkey/monkey_island/cc/ui/src/components/pages/LicensePage.js b/monkey/monkey_island/cc/ui/src/components/pages/LicensePage.js index 3fc29649b..bbc05a12f 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/LicensePage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/LicensePage.js @@ -1,8 +1,8 @@ import React from 'react'; import {Col} from 'react-bootstrap'; import rainge from 'rainge'; -import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; -import {faCopyright} from "@fortawesome/free-regular-svg-icons"; +import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; +import {faCopyright} from '@fortawesome/free-regular-svg-icons'; class LicensePageComponent extends React.Component { constructor(props) { diff --git a/monkey/monkey_island/cc/ui/src/components/pages/LoginPage.js b/monkey/monkey_island/cc/ui/src/components/pages/LoginPage.js index 04e1ab506..df8aa08d0 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/LoginPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/LoginPage.js @@ -2,9 +2,9 @@ import React from 'react'; import {Button, Col, Container, Form, Row} from 'react-bootstrap'; import AuthService from '../../services/AuthService'; -import Particles from "react-particles-js"; -import {particleParams} from "../../styles/particle-component/AuthPageParams"; -import monkeyGeneral from "../../images/militant-monkey.svg"; +import Particles from 'react-particles-js'; +import {particleParams} from '../../styles/components/particle-component/AuthPageParams'; +import monkeyGeneral from '../../images/militant-monkey.svg'; class LoginPageComponent extends React.Component { login = (event) => { diff --git a/monkey/monkey_island/cc/ui/src/components/pages/MapPage.js b/monkey/monkey_island/cc/ui/src/components/pages/MapPage.js index 11c9174e9..ae14b6a47 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/MapPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/MapPage.js @@ -8,8 +8,8 @@ import PreviewPaneComponent from 'components/map/preview-pane/PreviewPane'; import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; import {getOptions, edgeGroupToColor} from 'components/map/MapOptions'; import AuthComponent from '../AuthComponent'; -import '../../styles/Map.scss'; -import {faInfoCircle} from "@fortawesome/free-solid-svg-icons/faInfoCircle"; +import '../../styles/components/Map.scss'; +import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle'; class MapPageComponent extends AuthComponent { constructor(props) { diff --git a/monkey/monkey_island/cc/ui/src/components/pages/NotFoundPage.js b/monkey/monkey_island/cc/ui/src/components/pages/NotFoundPage.js index 9696fa521..5cd341574 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/NotFoundPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/NotFoundPage.js @@ -1,6 +1,6 @@ import React from 'react'; -import '../../styles/NotFoundPage.scss'; +import '../../styles/pages/NotFoundPage.scss'; import monkeyDetective from '../../images/detective-monkey.svg'; diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js index 88905d805..e051010c9 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js @@ -3,7 +3,7 @@ import {Row, Col, Container, Form, Button} from 'react-bootstrap'; import Particles from 'react-particles-js'; import AuthService from '../../services/AuthService'; -import {particleParams} from '../../styles/particle-component/AuthPageParams'; +import {particleParams} from '../../styles/components/particle-component/AuthPageParams'; import monkeyDetective from '../../images/detective-monkey.svg'; class RegisterPageComponent extends React.Component { diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js index 155dca40b..a75a65f24 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js @@ -8,8 +8,8 @@ import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import {faClipboard} from '@fortawesome/free-solid-svg-icons/faClipboard'; import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck'; import {faSync} from '@fortawesome/free-solid-svg-icons/faSync'; -import {faInfoCircle} from "@fortawesome/free-solid-svg-icons/faInfoCircle"; -import {faExclamationTriangle} from "@fortawesome/free-solid-svg-icons/faExclamationTriangle"; +import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle'; +import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons/faExclamationTriangle'; import {Link} from 'react-router-dom'; import AuthComponent from '../AuthComponent'; diff --git a/monkey/monkey_island/cc/ui/src/components/pages/StartOverPage.js b/monkey/monkey_island/cc/ui/src/components/pages/StartOverPage.js index cf38b56cb..c536146bf 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/StartOverPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/StartOverPage.js @@ -3,10 +3,10 @@ import {Col, Button} from 'react-bootstrap'; import {Link} from 'react-router-dom'; import AuthComponent from '../AuthComponent'; import StartOverModal from '../ui-components/StartOverModal'; -import '../../styles/StartOverPage.scss'; +import '../../styles/pages/StartOverPage.scss'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; -import {faInfoCircle} from "@fortawesome/free-solid-svg-icons/faInfoCircle"; -import {faCheck} from "@fortawesome/free-solid-svg-icons/faCheck"; +import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle'; +import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck'; class StartOverPageComponent extends AuthComponent { constructor(props) { diff --git a/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js b/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js index f5f8b10d4..9c0355aa2 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js @@ -6,8 +6,8 @@ import AuthComponent from '../AuthComponent'; import download from 'downloadjs'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; -import '../../styles/TelemetryPage.scss'; -import {faDownload} from "@fortawesome/free-solid-svg-icons/faDownload"; +import '../../styles/pages/TelemetryPage.scss'; +import {faDownload} from '@fortawesome/free-solid-svg-icons/faDownload'; const renderJson = (val) => ; const renderTime = (val) => val.split('.')[0]; 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 5c066034c..97f3c1a18 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 @@ -1,11 +1,13 @@ import React from 'react'; import {Col, Button} from 'react-bootstrap'; -import '../../styles/Collapse.scss'; +import '../../styles/components/Collapse.scss'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import {faCircle} from '@fortawesome/free-solid-svg-icons/faCircle'; import {faRadiation} from '@fortawesome/free-solid-svg-icons/faRadiation'; import {faEye} from '@fortawesome/free-solid-svg-icons/faEye'; import {faEyeSlash} from '@fortawesome/free-solid-svg-icons/faEyeSlash'; +import {faToggleOff} from '@fortawesome/free-solid-svg-icons/faToggleOff'; +import marked from 'marked'; import ReportHeader, {ReportTypes} from './common/ReportHeader'; import {ScanStatus} from '../attack/techniques/Helpers'; @@ -37,18 +39,18 @@ class AttackReport extends React.Component { }; if (typeof this.props.report.schema !== 'undefined' && typeof this.props.report.techniques !== 'undefined'){ this.state['schema'] = this.props.report['schema']; - this.state['techniques'] = AttackReport.addLinksToTechniques(this.props.report['schema'], this.props.report['techniques']); + this.state['techniques'] = AttackReport.modifyTechniqueData(this.props.report['schema'], this.props.report['techniques']); } } componentDidUpdate(prevProps) { if (this.props.report !== prevProps.report) { this.setState({schema: this.props.report['schema'], - techniques: AttackReport.addLinksToTechniques(this.props.report['schema'], this.props.report['techniques'])}) + techniques: AttackReport.modifyTechniqueData(this.props.report['schema'], this.props.report['techniques'])}) } } - onTechniqueSelect = (technique, value) => { + onTechniqueSelect = (technique, _) => { let selectedTechnique = this.getTechniqueByTitle(technique); if (selectedTechnique === false){ return; @@ -62,6 +64,8 @@ class AttackReport extends React.Component { return 'collapse-warning'; case ScanStatus.USED: return 'collapse-danger'; + case ScanStatus.DISABLED: + return 'collapse-disabled'; default: return 'collapse-default'; } @@ -73,22 +77,28 @@ class AttackReport extends React.Component { return ; case ScanStatus.USED: return ; + case ScanStatus.DISABLED: + return ; default: return ; - } + } } renderLegend() { return (