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 index b2d59310d..8bea7165a 100644 --- a/monkey/monkey_island/cc/services/attack/attack_config.py +++ b/monkey/monkey_island/cc/services/attack/attack_config.py @@ -1,10 +1,7 @@ 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__) @@ -32,10 +29,6 @@ class AttackConfig(object): return technique return None - @staticmethod - def get_config_schema(): - return SCHEMA - @staticmethod def reset_config(): AttackConfig.update_config(SCHEMA) @@ -45,137 +38,6 @@ class AttackConfig(object): 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(): """