forked from p15670423/monkey
cc: allow encryptor to store key file in variable locations
This commit is contained in:
parent
438a2701d4
commit
3f6c268f40
|
@ -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.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 get_db_version # noqa: E402
|
||||||
from monkey_island.cc.database import is_db_server_up # 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.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.resources.monkey_download import MonkeyDownload # noqa: E402
|
||||||
from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list # 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):
|
def main(should_setup_only=False, server_config_filename=DEFAULT_SERVER_CONFIG_PATH):
|
||||||
logger.info("Starting bootloader server")
|
logger.info("Starting bootloader server")
|
||||||
env_singleton.initialize_from_file(server_config_filename)
|
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())
|
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)
|
bootloader_server_thread = Thread(target=BootloaderHttpServer(mongo_url).serve_forever, daemon=True)
|
||||||
|
|
|
@ -6,33 +6,32 @@ import os
|
||||||
from Crypto import Random # noqa: DUO133 # nosec: B413
|
from Crypto import Random # noqa: DUO133 # nosec: B413
|
||||||
from Crypto.Cipher import AES # 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"
|
__author__ = "itay.mizeretz"
|
||||||
|
|
||||||
|
_encryptor = None
|
||||||
|
|
||||||
|
|
||||||
class Encryptor:
|
class Encryptor:
|
||||||
_BLOCK_SIZE = 32
|
_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):
|
def __init__(self, data_dir):
|
||||||
self._load_key()
|
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)
|
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)
|
f.write(self._cipher_key)
|
||||||
|
|
||||||
def _load_existing_key(self):
|
def _load_existing_key(self, password_file):
|
||||||
with open(self._DB_PASSWORD_FILENAME, 'rb') as f:
|
with open(password_file, 'rb') as f:
|
||||||
self._cipher_key = f.read()
|
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):
|
def _pad(self, message):
|
||||||
return message + (self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE)) * chr(
|
return message + (self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE)) * chr(
|
||||||
self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE))
|
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())
|
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
|
||||||
|
|
|
@ -29,7 +29,7 @@ def censor_password(password, plain_chars=3, secret_chars=5):
|
||||||
"""
|
"""
|
||||||
if not password:
|
if not password:
|
||||||
return ""
|
return ""
|
||||||
password = encryptor.dec(password)
|
password = encryptor().dec(password)
|
||||||
return password[0:plain_chars] + '*' * secret_chars
|
return password[0:plain_chars] + '*' * secret_chars
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,5 +42,5 @@ def censor_hash(hash_, plain_chars=5):
|
||||||
"""
|
"""
|
||||||
if not hash_:
|
if not hash_:
|
||||||
return ""
|
return ""
|
||||||
hash_ = encryptor.dec(hash_)
|
hash_ = encryptor().dec(hash_)
|
||||||
return hash_[0: plain_chars] + ' ...'
|
return hash_[0: plain_chars] + ' ...'
|
||||||
|
|
|
@ -75,9 +75,9 @@ class ConfigService:
|
||||||
if should_decrypt:
|
if should_decrypt:
|
||||||
if config_key_as_arr in ENCRYPTED_CONFIG_VALUES:
|
if config_key_as_arr in ENCRYPTED_CONFIG_VALUES:
|
||||||
if isinstance(config, str):
|
if isinstance(config, str):
|
||||||
config = encryptor.dec(config)
|
config = encryptor().dec(config)
|
||||||
elif isinstance(config, list):
|
elif isinstance(config, list):
|
||||||
config = [encryptor.dec(x) for x in config]
|
config = [encryptor().dec(x) for x in config]
|
||||||
return config
|
return config
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -112,7 +112,7 @@ class ConfigService:
|
||||||
if item_value in items_from_config:
|
if item_value in items_from_config:
|
||||||
return
|
return
|
||||||
if should_encrypt:
|
if should_encrypt:
|
||||||
item_value = encryptor.enc(item_value)
|
item_value = encryptor().enc(item_value)
|
||||||
mongo.db.config.update(
|
mongo.db.config.update(
|
||||||
{'name': 'newconfig'},
|
{'name': 'newconfig'},
|
||||||
{'$addToSet': {item_key: item_value}},
|
{'$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]:
|
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]]
|
flat_config[key] = [ConfigService.decrypt_ssh_key_pair(item) for item in flat_config[key]]
|
||||||
else:
|
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:
|
else:
|
||||||
flat_config[key] = encryptor.dec(flat_config[key])
|
flat_config[key] = encryptor().dec(flat_config[key])
|
||||||
return flat_config
|
return flat_config
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -320,19 +320,19 @@ class ConfigService:
|
||||||
config_arr[i] = ConfigService.decrypt_ssh_key_pair(config_arr[i]) if is_decrypt else \
|
config_arr[i] = ConfigService.decrypt_ssh_key_pair(config_arr[i]) if is_decrypt else \
|
||||||
ConfigService.decrypt_ssh_key_pair(config_arr[i], True)
|
ConfigService.decrypt_ssh_key_pair(config_arr[i], True)
|
||||||
else:
|
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:
|
else:
|
||||||
parent_config_arr[config_arr_as_array[-1]] = \
|
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
|
@staticmethod
|
||||||
def decrypt_ssh_key_pair(pair, encrypt=False):
|
def decrypt_ssh_key_pair(pair, encrypt=False):
|
||||||
if encrypt:
|
if encrypt:
|
||||||
pair['public_key'] = encryptor.enc(pair['public_key'])
|
pair['public_key'] = encryptor().enc(pair['public_key'])
|
||||||
pair['private_key'] = encryptor.enc(pair['private_key'])
|
pair['private_key'] = encryptor().enc(pair['private_key'])
|
||||||
else:
|
else:
|
||||||
pair['public_key'] = encryptor.dec(pair['public_key'])
|
pair['public_key'] = encryptor().dec(pair['public_key'])
|
||||||
pair['private_key'] = encryptor.dec(pair['private_key'])
|
pair['private_key'] = encryptor().dec(pair['private_key'])
|
||||||
return pair
|
return pair
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -66,4 +66,4 @@ def encrypt_exploit_creds(telemetry_json):
|
||||||
for field in ['password', 'lm_hash', 'ntlm_hash']:
|
for field in ['password', 'lm_hash', 'ntlm_hash']:
|
||||||
credential = attempts[i][field]
|
credential = attempts[i][field]
|
||||||
if len(credential) > 0:
|
if len(credential) > 0:
|
||||||
attempts[i][field] = encryptor.enc(credential)
|
attempts[i][field] = encryptor().enc(credential)
|
||||||
|
|
|
@ -63,7 +63,7 @@ def encrypt_system_info_ssh_keys(ssh_info):
|
||||||
for idx, user in enumerate(ssh_info):
|
for idx, user in enumerate(ssh_info):
|
||||||
for field in ['public_key', 'private_key', 'known_hosts']:
|
for field in ['public_key', 'private_key', 'known_hosts']:
|
||||||
if ssh_info[idx][field]:
|
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):
|
def process_credential_info(telemetry_json):
|
||||||
|
|
|
@ -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):
|
def _set_aws_key(key_type: str, key_value: str):
|
||||||
path_to_keys = AWS_KEYS_PATH
|
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)
|
ConfigService.set_config_value(path_to_keys + [key_type], encrypted_key)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import pytest
|
||||||
import dpath.util
|
import dpath.util
|
||||||
|
|
||||||
from monkey_island.cc.database import mongo
|
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 monkey_island.cc.services.config import ConfigService
|
||||||
from common.config_value_paths import AWS_KEYS_PATH
|
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
|
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)
|
@pytest.mark.usefixtures(FixtureEnum.USES_DATABASE)
|
||||||
def test_is_aws_keys_setup():
|
def test_is_aws_keys_setup(tmp_path):
|
||||||
# Mock default configuration
|
# Mock default configuration
|
||||||
ConfigService.init_default_config()
|
ConfigService.init_default_config()
|
||||||
mongo.db = MockObject()
|
mongo.db = MockObject()
|
||||||
|
@ -26,7 +26,8 @@ def test_is_aws_keys_setup():
|
||||||
assert not is_aws_keys_setup()
|
assert not is_aws_keys_setup()
|
||||||
|
|
||||||
# Make sure noone changed config path and broke this function
|
# 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_secret_access_key'], bogus_key_value)
|
||||||
dpath.util.set(ConfigService.default_config, AWS_KEYS_PATH+['aws_access_key_id'], bogus_key_value)
|
dpath.util.set(ConfigService.default_config, AWS_KEYS_PATH+['aws_access_key_id'], bogus_key_value)
|
||||||
|
|
||||||
|
|
|
@ -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))
|
|
@ -0,0 +1,2 @@
|
||||||
|
+ùÆõ
RO
|
||||||
|
ý)ê<>ž¾T“|ÄRSíÞ&Cá™â
|
Loading…
Reference in New Issue