diff --git a/.swm/JFXftJml8DpmuCPBA9rL.swm b/.swm/JFXftJml8DpmuCPBA9rL.swm index 510b9a2b0..b3f3bd080 100644 --- a/.swm/JFXftJml8DpmuCPBA9rL.swm +++ b/.swm/JFXftJml8DpmuCPBA9rL.swm @@ -11,7 +11,7 @@ "content": [ { "type": "text", - "text": "In order to make sure that the new `ScheduleJobs` PBA is shown in the configuration on the Monkey Island, you need to add its details to the configuration file(s).

\n\nSince this particular PBA is related to the MITRE techniques [T1168](https://attack.mitre.org/techniques/T1168) and [T1053](https://attack.mitre.org/techniques/T1053), make sure to link the PBA with these techniques in the configuration as well.

\n\nEach part of the configuration has an important role \n- *enum* — contains the relevant PBA's class name(s)\n- *title* — holds the name of the PBA which is displayed in the configuration on the Monkey Island\n- *info* — consists of an elaboration on the PBA's working which is displayed in the configuration on the Monkey Island\n- *attack_techniques* — has the IDs of the MITRE techniques associated with the PBA\n\n## Manual test \nOnce you think you're done...\n- Run the Monkey Island\n- You should be able to see your new PBA under the \"Monkey\" tab in the configuration, along with its information when you click on it\n- Further, when you enable/disable the associated MITRE techniques under the ATT&CK tab in the configuration, the PBA should also be enabled/disabled\n\n" + "text": "In order to make sure that the new `ScheduleJobs` PBA is shown in the configuration on the Monkey Island, you need to add its details to the configuration file(s).

\n\nSince this particular PBA is related to the MITRE techniques [T1168](https://attack.mitre.org/techniques/T1168) and [T1053](https://attack.mitre.org/techniques/T1053), make sure to link the PBA with these techniques in the configuration as well.

\n\nEach part of the configuration has an important role \n- *enum* — contains the relevant PBA's class name(s)\n- *title* — holds the name of the PBA which is displayed in the configuration on the Monkey Island\n- *info* — consists of an elaboration on the PBA's working which is displayed in the configuration on the Monkey Island\n- *attack_techniques* — has the IDs of the MITRE techniques associated with the PBA\n\n## Manual test \nOnce you think you're done...\n- Run the Monkey Island\n- You should be able to see your new PBA under the \"Monkey\" tab in the configuration, along with its information when you click on it\n\n" }, { "type": "snippet", diff --git a/.swm/afMu3y3ny5lnrYFWl3EI.swm b/.swm/afMu3y3ny5lnrYFWl3EI.swm index c922932da..89e6a8be9 100644 --- a/.swm/afMu3y3ny5lnrYFWl3EI.swm +++ b/.swm/afMu3y3ny5lnrYFWl3EI.swm @@ -7,7 +7,7 @@ "hints": [ "See `ScheduleJobs` PBA for an example of a PBA which only uses shell commands.", "Make sure to add the PBA to the configuration as well.", - "MITRE ATT&CK technique T1087 articulates that adversaries may attempt to get a listing of accounts on a system or within an environment which can help them determine which accounts can aid in follow-on behavior. Therefore, the AccountDiscovery PBA is relevant to it. Make sure to map this PBA to the MITRE ATT&CK configuration and report." + "MITRE ATT&CK technique T1087 articulates that adversaries may attempt to get a listing of accounts on a system or within an environment which can help them determine which accounts can aid in follow-on behavior. Therefore, the AccountDiscovery PBA is relevant to it which will enable the ATT&CK technique and show it in ATT&CK report." ] }, "content": [ diff --git a/CHANGELOG.md b/CHANGELOG.md index 22cc8d781..3cfb18eae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - Custom monkey directory name config option. #1537 - Hostname system info collector. #1535 - Max iterations and timeout between iterations config options. #1600 +- MITRE ATT&CK configuration screen. #1532 ### Fixed - A bug in network map page that caused delay of telemetry log loading. #1545 diff --git a/docs/content/usage/scenarios/custom-scenario/attack.md b/docs/content/usage/scenarios/custom-scenario/attack.md deleted file mode 100644 index 476a8183e..000000000 --- a/docs/content/usage/scenarios/custom-scenario/attack.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: "MITRE ATT&CK assessment" -date: 2020-10-22T16:58:22+03:00 -draft: false -description: "Assess your network security detection and prevention capabilities." -weight: 2 ---- - -## Overview - -The Infection Monkey can simulate various [ATT&CK](https://attack.mitre.org/matrices/enterprise/) techniques on the network. Use it to assess your security solutions' detection and prevention capabilities. The Infection Monkey will help you find which ATT&CK techniques go unnoticed and provide specific details along with suggested mitigations. - - -## Configuration - -- **ATT&CK matrix** You can use the ATT&CK configuration section to select which techniques you want the Infection Monkey to simulate. -For the full simulation, use the default settings. -- **Exploits -> Credentials** This configuration value will be used for brute-forcing. The Infection Monkey uses the most popular default passwords and usernames, but feel free to adjust it according to the default passwords common in your network. Keep in mind a longer list means longer scanning times. -- **Network -> Scope** Disable “Local network scan” and instead provide specific network ranges in the “Scan target list”. - -![ATT&CK matrix](/images/usage/scenarios/attack-matrix.png "ATT&CK matrix") - -## Suggested run mode - -Run the Infection Monkey on as many machines as you can. You can easily achieve this by selecting the “Manual” run option and executing the command shown on different machines in your environment manually or with your deployment tool. Additionally, you can use any other run options you see fit. - -## Assessing results - -The **ATT&CK Report** shows the status of simulations using ATT&CK techniques. Click on a technique to see more details about it and potential mitigations. Keep in mind that each technique display contains a question mark symbol that will take you to the official documentation of the specific ATT&CK technique used, where you can learn more about it. diff --git a/docs/static/images/usage/scenarios/attack-matrix.png b/docs/static/images/usage/scenarios/attack-matrix.png deleted file mode 100644 index f6a040c1b..000000000 Binary files a/docs/static/images/usage/scenarios/attack-matrix.png and /dev/null differ diff --git a/monkey/common/utils/attack_utils.py b/monkey/common/utils/attack_utils.py index ef1cba65f..d86b0ba55 100644 --- a/monkey/common/utils/attack_utils.py +++ b/monkey/common/utils/attack_utils.py @@ -8,8 +8,6 @@ class ScanStatus(Enum): SCANNED = 1 # Technique was attempted and succeeded USED = 2 - # Techique was disabled - DISABLED = 3 class UsageEnum(Enum): diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index 5f877d318..0e08eb4b9 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -8,7 +8,6 @@ from werkzeug.exceptions import NotFound import monkey_island.cc.environment.environment_singleton as env_singleton from common.common_consts.api_url_consts import T1216_PBA_FILE_DOWNLOAD_PATH from monkey_island.cc.database import database, mongo -from monkey_island.cc.resources.attack.attack_config import AttackConfiguration from monkey_island.cc.resources.attack.attack_report import AttackReport from monkey_island.cc.resources.auth.auth import Authenticate, init_jwt from monkey_island.cc.resources.auth.registration import Registration @@ -93,9 +92,8 @@ def init_app_config(app, mongo_url): # deciding to reset credentials and then still logging in with the old JWT. app.config["JWT_SECRET_KEY"] = str(uuid.uuid4()) - # By default, Flask sorts keys of JSON objects alphabetically, which messes with the ATT&CK - # matrix in the - # configuration. See https://flask.palletsprojects.com/en/1.1.x/config/#JSON_SORT_KEYS. + # By default, Flask sorts keys of JSON objects alphabetically. + # See https://flask.palletsprojects.com/en/1.1.x/config/#JSON_SORT_KEYS. app.config["JSON_SORT_KEYS"] = False app.json_encoder = CustomJSONEncoder @@ -166,7 +164,6 @@ def init_api_resources(api): "/api/fileUpload/?restore=", ) api.add_resource(RemoteRun, "/api/remote-monkey", "/api/remote-monkey/") - api.add_resource(AttackConfiguration, "/api/attack") api.add_resource(VersionUpdate, "/api/version-update", "/api/version-update/") api.add_resource(RemotePortCheck, "/api/monkey_control/check_remote_port/") api.add_resource(StartedOnIsland, "/api/monkey_control/started_on_island") diff --git a/monkey/monkey_island/cc/resources/attack/attack_config.py b/monkey/monkey_island/cc/resources/attack/attack_config.py deleted file mode 100644 index f9cd4c557..000000000 --- a/monkey/monkey_island/cc/resources/attack/attack_config.py +++ /dev/null @@ -1,35 +0,0 @@ -import flask_restful -from flask import current_app, json, jsonify, request - -from monkey_island.cc.resources.auth.auth import jwt_required -from monkey_island.cc.services.attack.attack_config import AttackConfig - - -class AttackConfiguration(flask_restful.Resource): - @jwt_required - def get(self): - return current_app.response_class( - json.dumps( - {"configuration": AttackConfig.get_config()}, - indent=None, - separators=(",", ":"), - sort_keys=False, - ) - + "\n", - mimetype=current_app.config["JSONIFY_MIMETYPE"], - ) - - @jwt_required - def post(self): - """ - Based on request content this endpoint either resets ATT&CK configuration or updates it. - :return: Technique types dict with techniques on reset and nothing on update - """ - config_json = json.loads(request.data) - if "reset_attack_matrix" in config_json: - AttackConfig.reset_config() - return jsonify(configuration=AttackConfig.get_config()) - else: - AttackConfig.update_config({"properties": json.loads(request.data)}) - AttackConfig.apply_to_monkey_config() - return {} diff --git a/monkey/monkey_island/cc/services/attack/attack_config.py b/monkey/monkey_island/cc/services/attack/attack_config.py deleted file mode 100644 index b2d59310d..000000000 --- a/monkey/monkey_island/cc/services/attack/attack_config.py +++ /dev/null @@ -1,205 +0,0 @@ -import logging - -from dpath import util - -from monkey_island.cc.database import mongo -from monkey_island.cc.services.attack.attack_schema import SCHEMA -from monkey_island.cc.services.config import ConfigService - -logger = logging.getLogger(__name__) - - -class AttackConfig(object): - def __init__(self): - pass - - @staticmethod - def get_config(): - config = mongo.db.attack.find_one({"name": "newconfig"})["properties"] - return config - - @staticmethod - def get_technique(technique_id): - """ - Gets technique by id - :param technique_id: E.g. T1210 - :return: Technique object or None if technique is not found - """ - attack_config = AttackConfig.get_config() - for config_key, attack_type in list(attack_config.items()): - for type_key, technique in list(attack_type["properties"].items()): - if type_key == technique_id: - return technique - return None - - @staticmethod - def get_config_schema(): - return SCHEMA - - @staticmethod - def reset_config(): - AttackConfig.update_config(SCHEMA) - - @staticmethod - def update_config(config_json): - mongo.db.attack.update({"name": "newconfig"}, {"$set": config_json}, upsert=True) - return True - - @staticmethod - def apply_to_monkey_config(): - """ - Applies ATT&CK matrix to the monkey configuration - :return: - """ - attack_techniques = AttackConfig.get_technique_values() - monkey_config = ConfigService.get_config(False, True, True) - monkey_schema = ConfigService.get_config_schema() - AttackConfig.set_arrays(attack_techniques, monkey_config, monkey_schema) - AttackConfig.set_booleans(attack_techniques, monkey_config, monkey_schema) - ConfigService.update_config(monkey_config, True) - - @staticmethod - def set_arrays(attack_techniques, monkey_config, monkey_schema): - """ - Sets exploiters/scanners/PBAs and other array type fields in monkey's config according to - ATT&CK matrix - :param attack_techniques: ATT&CK techniques dict. Format: {'T1110': True, ...} - :param monkey_config: Monkey island's configuration - :param monkey_schema: Monkey configuration schema - """ - for key, definition in list(monkey_schema["definitions"].items()): - for array_field in definition["anyOf"]: - # Check if current array field has attack_techniques assigned to it - if "attack_techniques" in array_field and array_field["attack_techniques"]: - should_remove = not AttackConfig.should_enable_field( - array_field["attack_techniques"], attack_techniques - ) - # If exploiter's attack technique is disabled, disable the exploiter/scanner/PBA - AttackConfig.r_alter_array( - monkey_config, key, array_field["enum"][0], remove=should_remove - ) - - @staticmethod - def set_booleans(attack_techniques, monkey_config, monkey_schema): - """ - Sets boolean type fields, like "should use mimikatz?" in monkey's config according to - ATT&CK matrix - :param attack_techniques: ATT&CK techniques dict. Format: {'T1110': True, ...} - :param monkey_config: Monkey island's configuration - :param monkey_schema: Monkey configuration schema - """ - for key, value in list(monkey_schema["properties"].items()): - AttackConfig.r_set_booleans([key], value, attack_techniques, monkey_config) - - @staticmethod - def r_set_booleans(path, value, attack_techniques, monkey_config): - """ - Recursively walks through monkey configuration (DFS) to find which boolean fields needs to - be set and sets them - according to ATT&CK matrix. - :param path: Property names that leads to current value. E.g. ['monkey', 'system_info', - 'should_use_mimikatz'] - :param value: Value of config property - :param attack_techniques: ATT&CK techniques dict. Format: {'T1110': True, ...} - :param monkey_config: Monkey island's configuration - """ - if isinstance(value, dict): - dictionary = {} - # If 'value' is a boolean value that should be set: - if ( - "type" in value - and value["type"] == "boolean" - and "attack_techniques" in value - and value["attack_techniques"] - ): - AttackConfig.set_bool_conf_val( - path, - AttackConfig.should_enable_field(value["attack_techniques"], attack_techniques), - monkey_config, - ) - # If 'value' is dict, we go over each of it's fields to search for booleans - elif "properties" in value: - dictionary = value["properties"] - else: - dictionary = value - for key, item in list(dictionary.items()): - path.append(key) - AttackConfig.r_set_booleans(path, item, attack_techniques, monkey_config) - # Method enumerated everything in current path, goes back a level. - del path[-1] - - @staticmethod - def set_bool_conf_val(path, val, monkey_config): - """ - Changes monkey's configuration by setting one of its boolean fields value - :param path: Path to boolean value in monkey's configuration. ['monkey', 'system_info', - 'should_use_mimikatz'] - :param val: Boolean - :param monkey_config: Monkey's configuration - """ - util.set(monkey_config, "/".join(path), val) - - @staticmethod - def should_enable_field(field_techniques, users_techniques): - """ - Determines whether a single config field should be enabled or not. - :param field_techniques: ATT&CK techniques that field uses - :param users_techniques: ATT&CK techniques that user chose - :return: True, if user enabled all techniques used by the field, false otherwise - """ - for technique in field_techniques: - try: - if not users_techniques[technique]: - return False - except KeyError: - logger.error( - "Attack technique %s is defined in schema, but not implemented." % technique - ) - return True - - @staticmethod - def r_alter_array(config_value, array_name, field, remove=True): - """ - Recursively searches config (DFS) for array and removes/adds a field. - :param config_value: Some object/value from config - :param array_name: Name of array this method should search - :param field: Field in array that this method should add/remove - :param remove: Removes field from array if true, adds it if false - """ - if isinstance(config_value, dict): - if array_name in config_value and isinstance(config_value[array_name], list): - if remove and field in config_value[array_name]: - config_value[array_name].remove(field) - elif not remove and field not in config_value[array_name]: - config_value[array_name].append(field) - else: - for prop in list(config_value.items()): - AttackConfig.r_alter_array(prop[1], array_name, field, remove) - - @staticmethod - def get_technique_values(): - """ - Parses ATT&CK config into a dict of techniques and corresponding values. - :return: Dictionary of techniques. Format: {"T1110": True, "T1075": False, ...} - """ - attack_config = AttackConfig.get_config() - techniques = {} - for type_name, attack_type in list(attack_config.items()): - for key, technique in list(attack_type["properties"].items()): - techniques[key] = technique["value"] - return techniques - - @staticmethod - def get_techniques_for_report(): - """ - :return: Format: {"T1110": {"selected": True, "type": "Credential Access", "T1075": ...} - """ - attack_config = AttackConfig.get_config() - techniques = {} - for type_name, attack_type in list(attack_config.items()): - for key, technique in list(attack_type["properties"].items()): - techniques[key] = { - "selected": technique["value"], - "type": SCHEMA["properties"][type_name]["title"], - } - return techniques diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 3872bf770..3fb3f4c32 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -2,7 +2,7 @@ import logging from monkey_island.cc.database import mongo from monkey_island.cc.models import Monkey -from monkey_island.cc.services.attack.attack_config import AttackConfig +from monkey_island.cc.services.attack.attack_schema import SCHEMA from monkey_island.cc.services.attack.technique_reports import ( T1003, T1005, @@ -102,7 +102,7 @@ class AttackReportService: "meta": {"latest_monkey_modifytime": Monkey.get_latest_modifytime()}, "name": REPORT_NAME, } - for tech_id, tech_info in list(AttackConfig.get_techniques_for_report().items()): + for tech_id, tech_info in list(AttackReportService.get_techniques_for_report().items()): try: technique_report_data = TECHNIQUES[tech_id].get_report_data() technique_report_data.update(tech_info) @@ -146,3 +146,17 @@ class AttackReportService: raise RuntimeError( "Attack Report cache not cleared. DeleteResult: " + delete_result.raw_result ) + + @staticmethod + def get_techniques_for_report(): + """ + :return: Format: {"T1110": {"type": "Credential Access", "T1075": ...} + """ + attack_config = SCHEMA["properties"] + techniques = {} + for type_name, attack_type in list(attack_config.items()): + for key, technique in list(attack_type["properties"].items()): + techniques[key] = { + "type": SCHEMA["properties"][type_name]["title"], + } + return techniques diff --git a/monkey/monkey_island/cc/services/attack/attack_schema.py b/monkey/monkey_island/cc/services/attack/attack_schema.py index 6db87d4fb..dca2a1513 100644 --- a/monkey/monkey_island/cc/services/attack/attack_schema.py +++ b/monkey/monkey_island/cc/services/attack/attack_schema.py @@ -10,8 +10,6 @@ SCHEMA = { "T1059": { "title": "Command line interface", "type": "bool", - "value": True, - "necessary": True, "link": "https://attack.mitre.org/techniques/T1059", "description": "Adversaries may use command-line interfaces to interact with " "systems " @@ -20,8 +18,6 @@ SCHEMA = { "T1106": { "title": "Execution through API", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1106", "description": "Adversary tools may directly use the Windows application " "programming interface (API) to execute binaries.", @@ -30,8 +26,6 @@ SCHEMA = { "T1086": { "title": "PowerShell", "type": "bool", - "value": True, - "necessary": True, "link": "https://attack.mitre.org/techniques/T1086", "description": "Adversaries can use PowerShell to perform a number of actions," " including discovery of information and execution of code.", @@ -39,8 +33,6 @@ SCHEMA = { "T1064": { "title": "Scripting", "type": "bool", - "value": True, - "necessary": True, "link": "https://attack.mitre.org/techniques/T1064", "description": "Adversaries may use scripts to aid in operations and " "perform multiple actions that would otherwise be manual.", @@ -48,8 +40,6 @@ SCHEMA = { "T1035": { "title": "Service execution", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1035", "description": "Adversaries may execute a binary, command, or script via a " "method " @@ -60,8 +50,6 @@ SCHEMA = { "T1154": { "title": "Trap", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1154", "description": "Adversaries can use the trap command to register code to be " "executed " @@ -77,8 +65,6 @@ SCHEMA = { "T1156": { "title": ".bash_profile and .bashrc", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1156", "description": "Adversaries may abuse shell scripts by " "inserting arbitrary shell commands to gain persistence, which " @@ -89,8 +75,6 @@ SCHEMA = { "T1136": { "title": "Create account", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1136", "description": "Adversaries with a sufficient level of access " "may create a local system, domain, or cloud tenant account.", @@ -98,8 +82,6 @@ SCHEMA = { "T1158": { "title": "Hidden files and directories", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1158", "description": "Adversaries can hide files and folders on the system " "and evade a typical user or system analysis that does not " @@ -108,8 +90,6 @@ SCHEMA = { "T1168": { "title": "Local job scheduling", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1168/", "description": "Linux supports multiple methods for creating pre-scheduled and " "periodic background jobs. Job scheduling can be used by " @@ -121,8 +101,6 @@ SCHEMA = { "T1504": { "title": "PowerShell profile", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1504", "description": "Adversaries may gain persistence and elevate privileges " "in certain situations by abusing PowerShell profiles which " @@ -132,8 +110,6 @@ SCHEMA = { "T1053": { "title": "Scheduled task", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1053", "description": "Windows utilities can be used to schedule programs or scripts " "to " @@ -146,8 +122,6 @@ SCHEMA = { "T1166": { "title": "Setuid and Setgid", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1166", "description": "Adversaries can set the setuid or setgid bits to get code " "running in " @@ -163,8 +137,6 @@ SCHEMA = { "T1197": { "title": "BITS jobs", "type": "bool", - "value": True, - "necessary": True, "link": "https://attack.mitre.org/techniques/T1197", "description": "Adversaries may abuse BITS to download, execute, " "and even clean up after running malicious code.", @@ -172,8 +144,6 @@ SCHEMA = { "T1146": { "title": "Clear command history", "type": "bool", - "value": False, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1146", "description": "Adversaries may clear/disable command history of a compromised " "account to conceal the actions undertaken during an intrusion.", @@ -181,8 +151,6 @@ SCHEMA = { "T1107": { "title": "File Deletion", "type": "bool", - "value": True, - "necessary": True, "link": "https://attack.mitre.org/techniques/T1107", "description": "Adversaries may remove files over the course of an intrusion " "to keep their footprint low or remove them at the end as part " @@ -191,8 +159,6 @@ SCHEMA = { "T1222": { "title": "File permissions modification", "type": "bool", - "value": True, - "necessary": True, "link": "https://attack.mitre.org/techniques/T1222", "description": "Adversaries may modify file permissions/attributes to evade " "intended DACLs.", @@ -200,8 +166,6 @@ SCHEMA = { "T1099": { "title": "Timestomping", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1099", "description": "Adversaries may modify file time attributes to hide " "new/changes to existing " @@ -211,8 +175,6 @@ SCHEMA = { "T1216": { "title": "Signed script proxy execution", "type": "bool", - "value": False, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1216", "description": "Adversaries may use scripts signed with trusted certificates " "to proxy execution of malicious files on Windows systems. This behavior could " @@ -229,8 +191,6 @@ SCHEMA = { "T1110": { "title": "Brute force", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1110", "description": "Adversaries may use brute force techniques to attempt access " "to accounts " @@ -241,8 +201,6 @@ SCHEMA = { "T1003": { "title": "Credential dumping", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1003", "description": "Mapped with T1078 Valid Accounts because " "both techniques require" @@ -257,8 +215,6 @@ SCHEMA = { "T1145": { "title": "Private keys", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1145", "description": "Adversaries may gather private keys from compromised systems " "for use in " @@ -277,8 +233,6 @@ SCHEMA = { "T1087": { "title": "Account Discovery", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1087", "description": "Adversaries may attempt to get a listing of accounts on a " "system or " @@ -289,8 +243,6 @@ SCHEMA = { "T1018": { "title": "Remote System Discovery", "type": "bool", - "value": True, - "necessary": True, "link": "https://attack.mitre.org/techniques/T1018", "description": "Adversaries will likely attempt to get a listing of other " "systems by IP address, " @@ -300,8 +252,6 @@ SCHEMA = { "T1082": { "title": "System information discovery", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1082", "depends_on": ["T1016", "T1005"], "description": "An adversary may attempt to get detailed information about the " @@ -312,8 +262,6 @@ SCHEMA = { "T1016": { "title": "System network configuration discovery", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1016", "depends_on": ["T1005", "T1082"], "description": "Adversaries will likely look for details about the network " @@ -332,8 +280,6 @@ SCHEMA = { "T1210": { "title": "Exploitation of Remote services", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1210", "description": "Exploitation of a software vulnerability occurs when an " "adversary " @@ -345,8 +291,6 @@ SCHEMA = { "T1075": { "title": "Pass the hash", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1075", "description": "Pass the hash (PtH) is a method of authenticating as a user " "without " @@ -355,8 +299,6 @@ SCHEMA = { "T1105": { "title": "Remote file copy", "type": "bool", - "value": True, - "necessary": True, "link": "https://attack.mitre.org/techniques/T1105", "description": "Files may be copied from one system to another to stage " "adversary tools or other files over the course of an operation.", @@ -364,8 +306,6 @@ SCHEMA = { "T1021": { "title": "Remote services", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1021", "depends_on": ["T1110"], "description": "An adversary may use Valid Accounts to log into a service" @@ -381,8 +321,6 @@ SCHEMA = { "T1005": { "title": "Data from local system", "type": "bool", - "value": True, - "necessary": False, "link": "https://attack.mitre.org/techniques/T1005", "depends_on": ["T1016", "T1082"], "description": "Sensitive data can be collected from local system sources, " @@ -400,8 +338,6 @@ SCHEMA = { "T1090": { "title": "Connection proxy", "type": "bool", - "value": True, - "necessary": True, "link": "https://attack.mitre.org/techniques/T1090", "description": "A connection proxy is used to direct network traffic between " "systems " @@ -410,8 +346,6 @@ SCHEMA = { "T1065": { "title": "Uncommonly used port", "type": "bool", - "value": True, - "necessary": True, "link": "https://attack.mitre.org/techniques/T1065", "description": "Adversaries may conduct C2 communications over a non-standard " "port to bypass proxies and firewalls that have been improperly " @@ -420,8 +354,6 @@ SCHEMA = { "T1188": { "title": "Multi-hop proxy", "type": "bool", - "value": True, - "necessary": True, "link": "https://attack.mitre.org/techniques/T1188", "description": "To disguise the source of malicious traffic, " "adversaries may chain together multiple proxies.", @@ -436,8 +368,6 @@ SCHEMA = { "T1041": { "title": "Exfiltration Over Command and Control Channel", "type": "bool", - "value": True, - "necessary": True, "link": "https://attack.mitre.org/techniques/T1041", "description": "Data exfiltration is performed over the Command and Control " "channel.", 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 79edfc8fd..c842436dd 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -35,7 +35,6 @@ 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 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 13f5385f3..988515026 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py @@ -36,7 +36,6 @@ class T1016(AttackTechnique): @staticmethod def get_report_data(): - @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 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 f78718de7..ac96cf742 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py @@ -40,7 +40,6 @@ class T1018(AttackTechnique): @staticmethod def get_report_data(): - @T1018.is_status_disabled def get_technique_status_and_data(): scan_info = list(mongo.db.telemetry.aggregate(T1018.query)) if scan_info: 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 e829098fd..b387af8ca 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py @@ -35,7 +35,6 @@ class T1021(AttackTechnique): @staticmethod def get_report_data(): - @T1021.is_status_disabled def get_technique_status_and_data(): attempts = [] if mongo.db.telemetry.count_documents(T1021.scanned_query): 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 d13f557fb..d4fe15b67 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py @@ -12,7 +12,6 @@ class T1041(AttackTechnique): @staticmethod def get_report_data(): - @T1041.is_status_disabled def get_technique_status_and_data(): monkeys = list(Monkey.objects()) info = [ 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 e4301d61b..a90ca5baa 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -26,7 +26,6 @@ class T1059(AttackTechnique): @staticmethod def get_report_data(): - @T1059.is_status_disabled def get_technique_status_and_data(): cmd_data = list(mongo.db.telemetry.aggregate(T1059.query)) if cmd_data: 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 4dddb9e9c..194789454 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py @@ -51,7 +51,6 @@ 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: 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 3aaa92c96..ad1bc6281 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py @@ -64,7 +64,6 @@ 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: 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 1fd99500e..6541a2b43 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py @@ -60,7 +60,6 @@ class T1086(AttackTechnique): @staticmethod def get_report_data(): - @T1086.is_status_disabled def get_technique_status_and_data(): exploit_cmd_data = list(mongo.db.telemetry.aggregate(T1086.query_for_exploits)) pba_cmd_data = list(mongo.db.telemetry.aggregate(T1086.query_for_pbas)) 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 aa172b87d..874524782 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py @@ -12,7 +12,6 @@ class T1090(AttackTechnique): @staticmethod def get_report_data(): - @T1090.is_status_disabled def get_technique_status_and_data(): monkeys = Monkey.get_tunneled_monkeys() monkeys = [monkey.get_network_info() for monkey in monkeys] 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 30f4d8508..e2af01d12 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py @@ -33,7 +33,6 @@ class T1110(AttackTechnique): @staticmethod def get_report_data(): - @T1110.is_status_disabled def get_technique_status_and_data(): attempts = list(mongo.db.telemetry.aggregate(T1110.query)) succeeded = False 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 5fea316aa..ec22a19ef 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1145.py @@ -29,7 +29,6 @@ class T1145(AttackTechnique): @staticmethod def get_report_data(): - @T1145.is_status_disabled def get_technique_status_and_data(): ssh_info = list(mongo.db.telemetry.aggregate(T1145.query)) if ssh_info: 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 47aabf7aa..abb22d08f 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py @@ -12,7 +12,6 @@ class T1188(AttackTechnique): @staticmethod def get_report_data(): - @T1188.is_status_disabled def get_technique_status_and_data(): monkeys = Monkey.get_tunneled_monkeys() hops = [] 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 a00462d85..91eb42d8b 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py @@ -16,7 +16,6 @@ class T1210(AttackTechnique): @staticmethod def get_report_data(): - @T1210.is_status_disabled def get_technique_status_and_data(): scanned_services = T1210.get_scanned_services() exploited_services = T1210.get_exploited_services() @@ -30,10 +29,8 @@ class T1210(AttackTechnique): status_and_data = get_technique_status_and_data() status = status_and_data[0] - if status == ScanStatus.DISABLED.value: - scanned_services, exploited_services = [], [] - else: - scanned_services, exploited_services = status_and_data[1], status_and_data[2] + + 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)) 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 529cf7b95..e11d6297b 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -6,19 +6,14 @@ from common.utils.attack_utils import ScanStatus from common.utils.code_utils import abstractstatic from monkey_island.cc.database import mongo from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations -from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.services.config_schema.config_schema import SCHEMA +from monkey_island.cc.services.attack.attack_schema import SCHEMA as ATTACK_SCHEMA from monkey_island.cc.services.config_schema.config_schema_per_attack_technique import ( ConfigSchemaPerAttackTechnique, ) 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 """ @@ -81,9 +76,7 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): Gets the status of a certain attack technique. :return: ScanStatus numeric value """ - if not cls._is_enabled_in_config(): - return ScanStatus.DISABLED.value - elif mongo.db.telemetry.find_one( + if mongo.db.telemetry.find_one( { "telem_category": "attack", "data.status": ScanStatus.USED.value, @@ -118,8 +111,6 @@ 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: if not cls.config_schema_per_attack_technique: cls.config_schema_per_attack_technique = ( @@ -172,7 +163,7 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): """ :return: techniques title. E.g. "T1110 Brute force" """ - return AttackConfig.get_technique(cls.tech_id)["title"] + return get_technique(cls.tech_id)["title"] @classmethod def get_tech_base_data(cls): @@ -205,17 +196,16 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): 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] +def get_technique(technique_id): + """ + Gets technique by id + :param technique_id: E.g. T1210 + :return: Technique object or None if technique is not found + """ + attack_config = ATTACK_SCHEMA["properties"] + for config_key, attack_type in list(attack_config.items()): + for type_key, technique in list(attack_type["properties"].items()): + if type_key == technique_id: + return technique + return None 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 index 3cc3d4085..c370590d9 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py @@ -54,7 +54,6 @@ class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta): :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, cls.relevant_systems)) diff --git a/monkey/monkey_island/cc/services/database.py b/monkey/monkey_island/cc/services/database.py index fb46cd726..027bd49e2 100644 --- a/monkey/monkey_island/cc/services/database.py +++ b/monkey/monkey_island/cc/services/database.py @@ -4,7 +4,6 @@ from flask import jsonify from monkey_island.cc.database import mongo from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations -from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.services.config import ConfigService logger = logging.getLogger(__name__) @@ -24,7 +23,6 @@ class Database(object): if not x.startswith("system.") and not x == AttackMitigations.COLLECTION_NAME ] ConfigService.init_config() - AttackConfig.reset_config() logger.info("DB was reset") return jsonify(status="OK") diff --git a/monkey/monkey_island/cc/ui/src/components/attack/ConfigMatrixComponent.js b/monkey/monkey_island/cc/ui/src/components/attack/ConfigMatrixComponent.js deleted file mode 100644 index ff9a11766..000000000 --- a/monkey/monkey_island/cc/ui/src/components/attack/ConfigMatrixComponent.js +++ /dev/null @@ -1,123 +0,0 @@ -import React from 'react'; -import Checkbox from '../ui-components/Checkbox' -import Tooltip from 'react-tooltip-lite' -import AuthComponent from '../AuthComponent'; -import ReactTable from 'react-table'; -import 'filepond/dist/filepond.min.css'; -import '../../styles/components/Tooltip.scss'; -import {Col} from 'react-bootstrap'; - -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faCircle as faCircle } from '@fortawesome/free-solid-svg-icons/faCircle'; -import { faCircle as faCircleThin } from '@fortawesome/free-regular-svg-icons/faCircle'; - -class ConfigMatrixComponent extends AuthComponent { - constructor(props) { - super(props); - this.state = {lastAction: 'none'} - } - - // Finds which attack type has most techniques and returns that number - static findMaxTechniques(data) { - let maxLen = 0; - data.forEach(function (techType) { - if (Object.keys(techType.properties).length > maxLen) { - maxLen = Object.keys(techType.properties).length - } - }); - return maxLen - } - - // Parses ATT&CK config schema into data suitable for react-table (ATT&CK matrix) - static parseTechniques(data, maxLen) { - let techniques = []; - // Create rows with attack techniques - for (let i = 0; i < maxLen; i++) { - let row = {}; - data.forEach(function (techType) { - let rowColumn = {}; - rowColumn.techName = techType.title; - - if (i <= Object.keys(techType.properties).length) { - rowColumn.technique = Object.values(techType.properties)[i]; - if (rowColumn.technique) { - rowColumn.technique.name = Object.keys(techType.properties)[i]; - } - } else { - rowColumn.technique = null; - } - row[rowColumn.techName] = rowColumn; - }); - techniques.push(row) - } - return techniques; - } - - getColumns(matrixData) { - return Object.keys(matrixData[0]).map((key) => { - return { - Header: key, - id: key, - accessor: x => this.renderTechnique(x[key].technique), - style: {'whiteSpace': 'unset'} - }; - }); - } - - renderTechnique(technique) { - if (technique == null) { - return (
) - } else { - return ( - - {technique.title} - - ) - } - } - - getTableData = (config) => { - let configCopy = JSON.parse(JSON.stringify(config)); - let maxTechniques = ConfigMatrixComponent.findMaxTechniques(Object.values(configCopy)); - let matrixTableData = ConfigMatrixComponent.parseTechniques(Object.values(configCopy), maxTechniques); - let columns = this.getColumns(matrixTableData); - return {'columns': columns, 'matrixTableData': matrixTableData, 'maxTechniques': maxTechniques} - }; - - renderLegend = () => { - return ( - ) - }; - - render() { - let tableData = this.getTableData(this.props.configuration); - return ( -
- {this.renderLegend()} -
- -
-
); - } -} - -export default ConfigMatrixComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/ConfigurationTabs.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/ConfigurationTabs.js index 7701959cf..f6057dd6e 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/ConfigurationTabs.js +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/ConfigurationTabs.js @@ -1,5 +1,4 @@ const CONFIGURATION_TABS = { - ATTACK: 'attack', BASIC: 'basic', BASIC_NETWORK: 'basic_network', RANSOMWARE: 'ransomware', @@ -8,7 +7,6 @@ const CONFIGURATION_TABS = { }; const advancedModeConfigTabs = [ - CONFIGURATION_TABS.ATTACK, CONFIGURATION_TABS.BASIC, CONFIGURATION_TABS.BASIC_NETWORK, CONFIGURATION_TABS.RANSOMWARE, 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 f23df62e6..4650aec07 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js @@ -2,7 +2,6 @@ import React from 'react'; import Form from 'react-jsonschema-form-bs4'; import {Button, Col, Modal, Nav} from 'react-bootstrap'; import AuthComponent from '../AuthComponent'; -import ConfigMatrixComponent from '../attack/ConfigMatrixComponent'; import UiSchema from '../configuration-components/UiSchema'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck'; @@ -20,7 +19,6 @@ import applyUiSchemaManipulators from '../configuration-components/UISchemaManip import HtmlFieldDescription from '../configuration-components/HtmlFieldDescription.js'; import CONFIGURATION_TABS_PER_MODE from '../configuration-components/ConfigurationTabs.js'; -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'; @@ -30,11 +28,9 @@ class ConfigurePageComponent extends AuthComponent { constructor(props) { super(props); this.initialConfig = {}; - this.initialAttackConfig = {}; this.currentSection = this.getSectionsOrder()[0]; this.state = { - attackConfig: {}, configuration: {}, currentFormData: {}, importCandidateConfig: null, @@ -42,7 +38,7 @@ class ConfigurePageComponent extends AuthComponent { schema: {}, sections: [], selectedSection: this.currentSection, - showAttackAlert: false, + showUnsubmittedConfigWarning: false, showUnsafeOptionsConfirmation: false, showUnsafeAttackOptionsWarning: false, showConfigExportModal: false, @@ -64,39 +60,26 @@ class ConfigurePageComponent extends AuthComponent { setInitialConfig(config) { // Sets a reference to know if config was changed - config['attack'] = {} this.initialConfig = JSON.parse(JSON.stringify(config)); } - setInitialAttackConfig(attackConfig) { - // Sets a reference to know if attack config was changed - this.initialAttackConfig = JSON.parse(JSON.stringify(attackConfig)); - } - componentDidMount = () => { - let urls = [CONFIG_URL, ATTACK_URL]; + let urls = [CONFIG_URL]; // ??? Why fetch config here and not in `render()`? Promise.all(urls.map(url => this.authFetch(url).then(res => res.json()))) .then(data => { let sections = []; - let attackConfig = data[1]; let monkeyConfig = data[0]; this.setInitialConfig(monkeyConfig.configuration); - this.setInitialAttackConfig(attackConfig.configuration); for (let sectionKey of this.getSectionsOrder()) { - if (sectionKey === 'attack') { - sections.push({key: sectionKey, title: 'ATT&CK'}) - } else { - sections.push({ - key: sectionKey, - title: monkeyConfig.schema.properties[sectionKey].title - }); - } + sections.push({ + key: sectionKey, + title: monkeyConfig.schema.properties[sectionKey].title + }); } this.setState({ schema: monkeyConfig.schema, configuration: monkeyConfig.configuration, - attackConfig: attackConfig.configuration, sections: sections, currentFormData: monkeyConfig.configuration[this.state.selectedSection] }) @@ -130,42 +113,13 @@ class ConfigurePageComponent extends AuthComponent { }; onSubmit = () => { - if (this.state.selectedSection === 'attack') { - this.matrixSubmit(); - } else { - this.attemptConfigSubmit(); - } + this.attemptConfigSubmit(); }; canSafelySubmitConfig(config) { return !isUnsafeOptionSelected(this.state.schema, config); } - matrixSubmit = () => { - // Submit attack matrix - this.authFetch(ATTACK_URL, - { - method: 'POST', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify(this.state.attackConfig) - }) - .then(res => { - if (!res.ok) { - throw Error() - } - return res; - }) - .then(() => { - this.setInitialAttackConfig(this.state.attackConfig); - }) - .then(() => this.updateConfig(this.checkAndShowUnsafeAttackWarning)) - .then(() => this.setState({lastAction: 'saved'})) - .catch(error => { - console.log('Bad configuration: ' + error.toString()); - this.setState({lastAction: 'invalid_configuration'}); - }); - }; - checkAndShowUnsafeAttackWarning = () => { if (isUnsafeOptionSelected(this.state.schema, this.state.configuration)) { this.setState({showUnsafeAttackOptionsWarning: true}); @@ -201,38 +155,8 @@ class ConfigurePageComponent extends AuthComponent { }); } - // Alters attack configuration when user toggles technique - attackTechniqueChange = (technique, value, mapped = false) => { - // Change value in attack configuration - // Go trough each column in matrix, searching for technique - Object.entries(this.state.attackConfig).forEach(techType => { - if (Object.prototype.hasOwnProperty.call(techType[1].properties, technique)) { - let tempMatrix = this.state.attackConfig; - tempMatrix[techType[0]].properties[technique].value = value; - this.setState({attackConfig: tempMatrix}); - - // Toggle all mapped techniques - if (!mapped) { - // Loop trough each column and each row - Object.entries(this.state.attackConfig).forEach(otherType => { - Object.entries(otherType[1].properties).forEach(otherTech => { - // If this technique depends on a technique that was changed - if (Object.prototype.hasOwnProperty.call(otherTech[1], 'depends_on') && - otherTech[1]['depends_on'].includes(technique)) { - this.attackTechniqueChange(otherTech[0], value, true) - } - }) - }); - } - } - }); - }; - onChange = ({formData}) => { let configuration = this.state.configuration; - if (this.state.selectedSection === 'attack'){ - formData = {}; - } configuration[this.state.selectedSection] = formData; this.setState({currentFormData: formData, configuration: configuration}); }; @@ -270,8 +194,8 @@ class ConfigurePageComponent extends AuthComponent { } renderAttackAlertModal = () => { - return ( { - this.setState({showAttackAlert: false}) + return ( { + this.setState({showUnsubmittedConfigWarning: false}) }}>

@@ -286,7 +210,7 @@ class ConfigurePageComponent extends AuthComponent { size='lg' style={{margin: '5px'}} onClick={() => { - this.setState({showAttackAlert: false}) + this.setState({showUnsubmittedConfigWarning: false}) }}> Cancel @@ -330,16 +254,13 @@ class ConfigurePageComponent extends AuthComponent { return true; } - userChangedMatrix() { - return (JSON.stringify(this.state.attackConfig) !== JSON.stringify(this.initialAttackConfig)) - } - setSelectedSection = (key) => { - if ((key === 'attack' && this.userChangedConfig()) || - (this.currentSection === 'attack' && this.userChangedMatrix())) { - this.setState({showAttackAlert: true}); - return; - } + + // TODO: Fix https://github.com/guardicore/monkey/issues/1621 + //if ( key === 'basic' & this.userChangedConfig()) { + // this.setState({showUnsubmittedConfigWarning: true}); + // return; + //} this.updateConfigSection(); this.currentSection = key; @@ -358,7 +279,6 @@ class ConfigurePageComponent extends AuthComponent { }) .then(res => res.json()) .then(res => { - res.configuration['attack'] = {} this.setState({ lastAction: 'reset', schema: res.schema, @@ -372,16 +292,6 @@ class ConfigurePageComponent extends AuthComponent { 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'}, - body: JSON.stringify('reset_attack_matrix') - }) - .then(res => res.json()) - .then(res => { - this.setState({attackConfig: res.configuration}); - this.setInitialAttackConfig(res.configuration); - }) }; removePBAfile(apiEndpoint, setFilenameFnc) { @@ -421,13 +331,6 @@ class ConfigurePageComponent extends AuthComponent { })); } - renderMatrix = () => { - return () - }; - renderConfigContent = (displayedSchema) => { let formProperties = {}; formProperties['schema'] = displayedSchema @@ -497,15 +400,13 @@ class ConfigurePageComponent extends AuthComponent { render() { let displayedSchema = {}; - if (Object.prototype.hasOwnProperty.call(this.state.schema, 'properties') && this.state.selectedSection !== 'attack') { + if (Object.prototype.hasOwnProperty.call(this.state.schema, 'properties')) { 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() - } else if (this.state.selectedSection !== 'attack' && Object.entries(this.state.configuration).length !== 0) { + if (Object.entries(this.state.configuration).length !== 0) { content = this.renderConfigContent(displayedSchema) } return ( diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py index 1da9860b9..e408a3aff 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/attack/technique_reports/test_technique_reports.py @@ -3,10 +3,7 @@ from enum import Enum import pytest from common.utils.attack_utils import ScanStatus -from monkey_island.cc.services.attack.technique_reports.__init__ import ( - AttackTechnique, - disabled_msg, -) +from monkey_island.cc.services.attack.technique_reports.__init__ import AttackTechnique @pytest.fixture(scope="function", autouse=True) @@ -68,13 +65,6 @@ class ExpectedMsgs_OneRelevantSystem(Enum): USED: str = "USED" -def test_get_message_by_status_disabled_two_relevant_systems(): - technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status( - ScanStatus.DISABLED.value - ) - assert technique_msg == disabled_msg - - def test_get_message_by_status_unscanned_two_relevant_systems(): technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status( ScanStatus.UNSCANNED.value @@ -96,13 +86,6 @@ def test_get_message_by_status_used_two_relevant_systems(): assert technique_msg == ExpectedMsgs_TwoRelevantSystems.USED.value -def test_get_message_by_status_disabled_one_relevant_system(): - technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status( - ScanStatus.DISABLED.value - ) - assert technique_msg == disabled_msg - - def test_get_message_by_status_unscanned_one_relevant_system(): technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status( ScanStatus.UNSCANNED.value diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py b/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py index d3ca3fbcc..4afdbc969 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/mongo/test_database_initializer.py @@ -22,9 +22,6 @@ def patch_dependencies(monkeypatch_session): monkeypatch_session.setattr( "monkey_island.cc.services.config.ConfigService.init_config", lambda: None ) - monkeypatch_session.setattr( - "monkey_island.cc.services.attack.attack_config.AttackConfig.reset_config", lambda: None - ) monkeypatch_session.setattr( "monkey_island.cc.services.database.jsonify", MagicMock(return_value=True) )