diff --git a/monkey/monkey_island/cc/main.py b/monkey/monkey_island/cc/main.py index 2018da89c..4bb704aee 100644 --- a/monkey/monkey_island/cc/main.py +++ b/monkey/monkey_island/cc/main.py @@ -23,6 +23,7 @@ from monkey_island.cc.app import init_app # noqa: E402 from monkey_island.cc.server_utils.bootloader_server import BootloaderHttpServer # noqa: E402 from monkey_island.cc.database import get_db_version # noqa: E402 from monkey_island.cc.database import is_db_server_up # noqa: E402 +from monkey_island.cc.encryptor import initialize_encryptor # noqa: E402 from monkey_island.cc.services.utils.network_utils import local_ip_addresses # noqa: E402 from monkey_island.cc.resources.monkey_download import MonkeyDownload # noqa: E402 from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list # noqa: E402 @@ -34,6 +35,7 @@ MINIMUM_MONGO_DB_VERSION_REQUIRED = "4.2.0" def main(should_setup_only=False, server_config_filename=DEFAULT_SERVER_CONFIG_PATH): logger.info("Starting bootloader server") env_singleton.initialize_from_file(server_config_filename) + initialize_encryptor(env_singleton.config.data_dir) mongo_url = os.environ.get('MONGO_URL', env_singleton.env.get_mongo_url()) bootloader_server_thread = Thread(target=BootloaderHttpServer(mongo_url).serve_forever, daemon=True) diff --git a/monkey/monkey_island/cc/server_utils/encryptor.py b/monkey/monkey_island/cc/server_utils/encryptor.py index cce7d464a..2a9ce8fea 100644 --- a/monkey/monkey_island/cc/server_utils/encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryptor.py @@ -6,33 +6,32 @@ import os from Crypto import Random # noqa: DUO133 # nosec: B413 from Crypto.Cipher import AES # noqa: DUO133 # nosec: B413 -from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH - __author__ = "itay.mizeretz" +_encryptor = None + class Encryptor: _BLOCK_SIZE = 32 - _DB_PASSWORD_FILENAME = os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc/mongo_key.bin') + _PASSWORD_FILENAME = "mongo_key.bin" - def __init__(self): - self._load_key() + def __init__(self, data_dir): + password_file = os.path.join(data_dir, self._PASSWORD_FILENAME) - def _init_key(self): + if os.path.exists(password_file): + self._load_existing_key(password_file) + else: + self._init_key(password_file) + + def _init_key(self, password_file): self._cipher_key = Random.new().read(self._BLOCK_SIZE) - with open(self._DB_PASSWORD_FILENAME, 'wb') as f: + with open(password_file, 'wb') as f: f.write(self._cipher_key) - def _load_existing_key(self): - with open(self._DB_PASSWORD_FILENAME, 'rb') as f: + def _load_existing_key(self, password_file): + with open(password_file, 'rb') as f: self._cipher_key = f.read() - def _load_key(self): - if os.path.exists(self._DB_PASSWORD_FILENAME): - self._load_existing_key() - else: - self._init_key() - def _pad(self, message): return message + (self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE)) * chr( self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE)) @@ -52,4 +51,11 @@ class Encryptor: return self._unpad(cipher.decrypt(enc_message[AES.block_size:]).decode()) -encryptor = Encryptor() +def initialize_encryptor(data_dir): + global _encryptor + + _encryptor = Encryptor(data_dir) + + +def encryptor(): + return _encryptor diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/technique_report_tools.py b/monkey/monkey_island/cc/services/attack/technique_reports/technique_report_tools.py index 34be687a4..35356cad4 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/technique_report_tools.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/technique_report_tools.py @@ -29,7 +29,7 @@ def censor_password(password, plain_chars=3, secret_chars=5): """ if not password: return "" - password = encryptor.dec(password) + password = encryptor().dec(password) return password[0:plain_chars] + '*' * secret_chars @@ -42,5 +42,5 @@ def censor_hash(hash_, plain_chars=5): """ if not hash_: return "" - hash_ = encryptor.dec(hash_) + hash_ = encryptor().dec(hash_) return hash_[0: plain_chars] + ' ...' diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py index 390380131..edf44b967 100644 --- a/monkey/monkey_island/cc/services/config.py +++ b/monkey/monkey_island/cc/services/config.py @@ -75,9 +75,9 @@ class ConfigService: if should_decrypt: if config_key_as_arr in ENCRYPTED_CONFIG_VALUES: if isinstance(config, str): - config = encryptor.dec(config) + config = encryptor().dec(config) elif isinstance(config, list): - config = [encryptor.dec(x) for x in config] + config = [encryptor().dec(x) for x in config] return config @staticmethod @@ -112,7 +112,7 @@ class ConfigService: if item_value in items_from_config: return if should_encrypt: - item_value = encryptor.enc(item_value) + item_value = encryptor().enc(item_value) mongo.db.config.update( {'name': 'newconfig'}, {'$addToSet': {item_key: item_value}}, @@ -297,9 +297,9 @@ class ConfigService: if flat_config[key] and isinstance(flat_config[key][0], dict) and 'public_key' in flat_config[key][0]: flat_config[key] = [ConfigService.decrypt_ssh_key_pair(item) for item in flat_config[key]] else: - flat_config[key] = [encryptor.dec(item) for item in flat_config[key]] + flat_config[key] = [encryptor().dec(item) for item in flat_config[key]] else: - flat_config[key] = encryptor.dec(flat_config[key]) + flat_config[key] = encryptor().dec(flat_config[key]) return flat_config @staticmethod @@ -320,19 +320,19 @@ class ConfigService: config_arr[i] = ConfigService.decrypt_ssh_key_pair(config_arr[i]) if is_decrypt else \ ConfigService.decrypt_ssh_key_pair(config_arr[i], True) else: - config_arr[i] = encryptor.dec(config_arr[i]) if is_decrypt else encryptor.enc(config_arr[i]) + config_arr[i] = encryptor().dec(config_arr[i]) if is_decrypt else encryptor().enc(config_arr[i]) else: parent_config_arr[config_arr_as_array[-1]] = \ - encryptor.dec(config_arr) if is_decrypt else encryptor.enc(config_arr) + encryptor().dec(config_arr) if is_decrypt else encryptor().enc(config_arr) @staticmethod def decrypt_ssh_key_pair(pair, encrypt=False): if encrypt: - pair['public_key'] = encryptor.enc(pair['public_key']) - pair['private_key'] = encryptor.enc(pair['private_key']) + pair['public_key'] = encryptor().enc(pair['public_key']) + pair['private_key'] = encryptor().enc(pair['private_key']) else: - pair['public_key'] = encryptor.dec(pair['public_key']) - pair['private_key'] = encryptor.dec(pair['private_key']) + pair['public_key'] = encryptor().dec(pair['public_key']) + pair['private_key'] = encryptor().dec(pair['private_key']) return pair @staticmethod diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py index 3d8588663..b2baff562 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py @@ -66,4 +66,4 @@ def encrypt_exploit_creds(telemetry_json): for field in ['password', 'lm_hash', 'ntlm_hash']: credential = attempts[i][field] if len(credential) > 0: - attempts[i][field] = encryptor.enc(credential) + attempts[i][field] = encryptor().enc(credential) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py index d3e7cfb54..5af4d1dde 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py @@ -63,7 +63,7 @@ def encrypt_system_info_ssh_keys(ssh_info): for idx, user in enumerate(ssh_info): for field in ['public_key', 'private_key', 'known_hosts']: if ssh_info[idx][field]: - ssh_info[idx][field] = encryptor.enc(ssh_info[idx][field]) + ssh_info[idx][field] = encryptor().enc(ssh_info[idx][field]) def process_credential_info(telemetry_json): 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 index 701598168..2bc7b2323 100644 --- 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 @@ -37,7 +37,7 @@ def set_aws_keys(access_key_id: str, secret_access_key: str, session_token: str) def _set_aws_key(key_type: str, key_value: str): path_to_keys = AWS_KEYS_PATH - encrypted_key = encryptor.enc(key_value) + encrypted_key = encryptor().enc(key_value) ConfigService.set_config_value(path_to_keys + [key_type], encrypted_key) diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py index c35e55a8f..72a1c711a 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_auth_service.py @@ -4,7 +4,7 @@ import pytest import dpath.util from monkey_island.cc.database import mongo -from monkey_island.cc.server_utils import encryptor +from monkey_island.cc.server_utils.encryptor import initialize_encryptor, encryptor from monkey_island.cc.services.config import ConfigService from common.config_value_paths import AWS_KEYS_PATH from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_auth_service import is_aws_keys_setup @@ -16,7 +16,7 @@ class MockObject: @pytest.mark.usefixtures(FixtureEnum.USES_DATABASE) -def test_is_aws_keys_setup(): +def test_is_aws_keys_setup(tmp_path): # Mock default configuration ConfigService.init_default_config() mongo.db = MockObject() @@ -26,7 +26,8 @@ def test_is_aws_keys_setup(): assert not is_aws_keys_setup() # Make sure noone changed config path and broke this function - bogus_key_value = encryptor.encryptor.enc('bogus_aws_key') + initialize_encryptor(tmp_path) + bogus_key_value = encryptor().enc('bogus_aws_key') dpath.util.set(ConfigService.default_config, AWS_KEYS_PATH+['aws_secret_access_key'], bogus_key_value) dpath.util.set(ConfigService.default_config, AWS_KEYS_PATH+['aws_access_key_id'], bogus_key_value) diff --git a/monkey/monkey_island/cc/test_encryptor.py b/monkey/monkey_island/cc/test_encryptor.py new file mode 100644 index 000000000..9a424cc01 --- /dev/null +++ b/monkey/monkey_island/cc/test_encryptor.py @@ -0,0 +1,29 @@ +import os + +from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH +from monkey_island.cc.server_utils.encryptor import initialize_encryptor, encryptor + + +TEST_DATA_DIR = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", "testing") +PASSWORD_FILENAME = "mongo_key.bin" + +PLAINTEXT = "Hello, Monkey!" +CYPHERTEXT = "vKgvD6SjRyIh1dh2AM/rnTa0NI/vjfwnbZLbMocWtE4e42WJmSUz2ordtbQrH1Fq" + + +def test_aes_cbc_encryption(): + initialize_encryptor(TEST_DATA_DIR) + + assert encryptor().enc(PLAINTEXT) != PLAINTEXT + + +def test_aes_cbc_decryption(): + initialize_encryptor(TEST_DATA_DIR) + + assert encryptor().dec(CYPHERTEXT) == PLAINTEXT + + +def test_create_new_password_file(tmpdir): + initialize_encryptor(tmpdir) + + assert os.path.isfile(os.path.join(tmpdir, PASSWORD_FILENAME)) diff --git a/monkey/monkey_island/cc/testing/mongo_key.bin b/monkey/monkey_island/cc/testing/mongo_key.bin new file mode 100644 index 000000000..6b8091efb --- /dev/null +++ b/monkey/monkey_island/cc/testing/mongo_key.bin @@ -0,0 +1,2 @@ ++ RO +)ꝞT|RS&C \ No newline at end of file