diff --git a/monkey/common/cloud/scoutsuite_consts.py b/monkey/common/cloud/scoutsuite_consts.py new file mode 100644 index 000000000..86411e179 --- /dev/null +++ b/monkey/common/cloud/scoutsuite_consts.py @@ -0,0 +1,9 @@ +from enum import Enum + + +class PROVIDERS(Enum): + AWS = 'aws' + AZURE = 'azure' + GCP = 'gcp' + ALIBABA = 'aliyun' + ORACLE = 'oci' diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py index 2917524c5..7597e3de3 100644 --- a/monkey/infection_monkey/config.py +++ b/monkey/infection_monkey/config.py @@ -246,6 +246,10 @@ class Configuration(object): exploit_ntlm_hash_list = [] exploit_ssh_keys = [] + access_key_id = '' + secret_access_key = '' + session_token = '' + # smb/wmi exploiter smb_download_timeout = 300 # timeout in seconds smb_service_name = "InfectionMonkey" diff --git a/monkey/infection_monkey/system_info/collectors/scoutsuite_collector/scoutsuite_collector.py b/monkey/infection_monkey/system_info/collectors/scoutsuite_collector/scoutsuite_collector.py index f7d6b7ec5..6965e53c7 100644 --- a/monkey/infection_monkey/system_info/collectors/scoutsuite_collector/scoutsuite_collector.py +++ b/monkey/infection_monkey/system_info/collectors/scoutsuite_collector/scoutsuite_collector.py @@ -1,22 +1,28 @@ +import logging + import infection_monkey.system_info.collectors.scoutsuite_collector.scoutsuite_api as scoutsuite_api +from common.cloud.scoutsuite_consts import PROVIDERS from infection_monkey.telemetry.scoutsuite_telem import ScoutSuiteTelem +from infection_monkey.config import WormConfiguration + +logger = logging.getLogger(__name__) -class CLOUD_TYPES: - AWS = 'aws' - AZURE = 'azure' - GCP = 'gcp' - ALIBABA = 'aliyun' - ORACLE = 'oci' +def scan_cloud_security(cloud_type: PROVIDERS): + try: + results = run_scoutsuite(cloud_type.value) + if 'error' in results and results['error']: + raise Exception(results['error']) + send_results(results) + except Exception as e: + logger.error(f"ScoutSuite didn't scan {cloud_type.value} security because: {e}") -def scan_cloud_security(cloud_type: CLOUD_TYPES): - results = run_scoutsuite(cloud_type) - send_results(results) - - -def run_scoutsuite(cloud_type): - return scoutsuite_api.run(provider=cloud_type) +def run_scoutsuite(cloud_type: str): + return scoutsuite_api.run(provider=cloud_type, + aws_access_key_id=WormConfiguration.access_key_id, + aws_secret_access_key=WormConfiguration.secret_access_key, + aws_session_token=WormConfiguration.session_token) def send_results(results): diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py index e67abc1f3..e071366fb 100644 --- a/monkey/monkey_island/cc/services/config.py +++ b/monkey/monkey_island/cc/services/config.py @@ -21,12 +21,15 @@ from monkey_island.cc.services.config_schema.config_value_paths import STARTED_O logger = logging.getLogger(__name__) # This should be used for config values of array type (array of strings only) -ENCRYPTED_CONFIG_ARRAYS = \ +ENCRYPTED_CONFIG_VALUES = \ [ - ['basic', 'credentials', 'exploit_password_list'], - ['internal', 'exploits', 'exploit_lm_hash_list'], - ['internal', 'exploits', 'exploit_ntlm_hash_list'], - ['internal', 'exploits', 'exploit_ssh_keys'] + PASSWORD_LIST_PATH, + LM_HASH_LIST_PATH, + NTLM_HASH_LIST_PATH, + SSH_KEYS_PATH, + AWS_KEYS_PATH + ['access_key_id'], + AWS_KEYS_PATH + ['secret_access_key'], + AWS_KEYS_PATH + ['session_token'] ] @@ -69,8 +72,11 @@ class ConfigService: for config_key_part in config_key_as_arr: config = config[config_key_part] if should_decrypt: - if config_key_as_arr in ENCRYPTED_CONFIG_ARRAYS: - config = [encryptor.dec(x) for x in config] + if config_key_as_arr in ENCRYPTED_CONFIG_VALUES: + if isinstance(config, str): + config = encryptor.dec(config) + elif isinstance(config, list): + config = [encryptor.dec(x) for x in config] return config @staticmethod @@ -79,12 +85,6 @@ class ConfigService: mongo.db.config.update({'name': 'newconfig'}, {"$set": {mongo_key: value}}) - @staticmethod - def append_to_config_array(config_key_as_arr, value): - mongo_key = ".".join(config_key_as_arr) - mongo.db.config.update({'name': 'newconfig'}, - {"$push": {mongo_key: value}}) - @staticmethod def get_flat_config(is_initial_config=False, should_decrypt=True): config_json = ConfigService.get_config(is_initial_config, should_decrypt) @@ -92,7 +92,11 @@ class ConfigService: for i in config_json: for j in config_json[i]: for k in config_json[i][j]: - flat_config_json[k] = config_json[i][j][k] + if isinstance(config_json[i][j][k], dict): + for key, value in config_json[i][j][k].items(): + flat_config_json[key] = value + else: + flat_config_json[k] = config_json[i][j][k] return flat_config_json @@ -101,8 +105,8 @@ class ConfigService: return SCHEMA @staticmethod - def add_item_to_config_set_if_dont_exist(item_key, item_value, should_encrypt): - item_path_array = item_key.split('.') + def add_item_to_config_set_if_dont_exist(item_path_array, item_value, should_encrypt): + item_key = '.'.join(item_path_array) items_from_config = ConfigService.get_config_value(item_path_array, False, should_encrypt) if item_value in items_from_config: return diff --git a/monkey/monkey_island/cc/services/config_schema/internal.py b/monkey/monkey_island/cc/services/config_schema/internal.py index bdbae2461..b571d31ab 100644 --- a/monkey/monkey_island/cc/services/config_schema/internal.py +++ b/monkey/monkey_island/cc/services/config_schema/internal.py @@ -94,6 +94,23 @@ INTERNAL = { "type": "boolean", "default": True, "description": "Is the monkey alive" + }, + "aws_keys": { + "type": "object", + "properties": { + "access_key_id": { + "type": "string", + "default": "" + }, + "secret_access_key": { + "type": "string", + "default": "" + }, + "session_token": { + "type": "string", + "default": "" + } + } } } }, diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py new file mode 100644 index 000000000..5cd29d423 --- /dev/null +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_auth_service.py @@ -0,0 +1,63 @@ +import pkgutil +import sys +from pathlib import PurePath +from typing import Tuple + +from common.cloud.scoutsuite_consts import PROVIDERS +from common.utils.exceptions import InvalidAWSKeys +from monkey_island.cc.encryptor import encryptor +from monkey_island.cc.services.config import ConfigService +from monkey_island.cc.services.config_schema.config_value_paths import AWS_KEYS_PATH + +_scoutsuite_api_package = pkgutil.get_loader('common.cloud.scoutsuite.ScoutSuite.__main__') + + +def _add_scoutsuite_to_python_path(): + scoutsuite_path = PurePath(_scoutsuite_api_package.path).parent.parent.__str__() + sys.path.append(scoutsuite_path) + + +_add_scoutsuite_to_python_path() + + +def is_cloud_authentication_setup(provider: PROVIDERS) -> Tuple[bool, str]: + if provider == PROVIDERS.AWS.value: + if is_aws_keys_setup(): + return True, "AWS keys already setup. Run monkey on Island to scan." + + import common.cloud.scoutsuite.ScoutSuite.providers.aws.authentication_strategy as auth_strategy + try: + profile = auth_strategy.AWSAuthenticationStrategy().authenticate() + return True, f" Profile \"{profile.session.profile_name}\" is already setup. Run monkey on Island to scan." + except Exception: + return False, "" + + +def is_aws_keys_setup(): + return (ConfigService.get_config_value(AWS_KEYS_PATH + ['access_key_id']) and + ConfigService.get_config_value(AWS_KEYS_PATH + ['secret_access_key'])) + + +def set_aws_keys(access_key_id: str, secret_access_key: str, session_token: str): + if not access_key_id or not secret_access_key: + raise InvalidAWSKeys("Missing some of the following fields: access key ID, secret access key.") + _set_aws_key('access_key_id', access_key_id) + _set_aws_key('secret_access_key', secret_access_key) + _set_aws_key('session_token', session_token) + + +def _set_aws_key(key_type: str, key_value: str): + path_to_keys = AWS_KEYS_PATH + encrypted_key = encryptor.enc(key_value) + ConfigService.set_config_value(path_to_keys + [key_type], encrypted_key) + + +def get_aws_keys(): + return {'access_key_id': _get_aws_key('access_key_id'), + 'secret_access_key': _get_aws_key('secret_access_key'), + 'session_token': _get_aws_key('session_token')} + + +def _get_aws_key(key_type: str): + path_to_keys = AWS_KEYS_PATH + return ConfigService.get_config_value(config_key_as_arr=path_to_keys + [key_type]) 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 index aab3f1899..ac9104817 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js @@ -84,6 +84,9 @@ export default function UiSchema(props) { monkey: { alive: { classNames: 'config-field-hidden' + }, + aws_keys: { + classNames: 'config-field-hidden' } } }