Island: Remove ConfigService (cc/services/config.py)
This commit is contained in:
parent
c5dfd9c3e6
commit
360b438f62
|
@ -1,181 +0,0 @@
|
||||||
import collections
|
|
||||||
import functools
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from common.config_value_paths import (
|
|
||||||
LM_HASH_LIST_PATH,
|
|
||||||
NTLM_HASH_LIST_PATH,
|
|
||||||
PASSWORD_LIST_PATH,
|
|
||||||
PBA_LINUX_FILENAME_PATH,
|
|
||||||
PBA_WINDOWS_FILENAME_PATH,
|
|
||||||
SSH_KEYS_PATH,
|
|
||||||
)
|
|
||||||
from monkey_island.cc.database import mongo
|
|
||||||
from monkey_island.cc.server_utils.encryption import (
|
|
||||||
SensitiveField,
|
|
||||||
StringEncryptor,
|
|
||||||
decrypt_dict,
|
|
||||||
encrypt_dict,
|
|
||||||
get_datastore_encryptor,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
# This should be used for config values of array type (array of strings only)
|
|
||||||
ENCRYPTED_CONFIG_VALUES = [
|
|
||||||
PASSWORD_LIST_PATH,
|
|
||||||
LM_HASH_LIST_PATH,
|
|
||||||
NTLM_HASH_LIST_PATH,
|
|
||||||
SSH_KEYS_PATH,
|
|
||||||
]
|
|
||||||
|
|
||||||
SENSITIVE_SSH_KEY_FIELDS = [
|
|
||||||
SensitiveField(path="private_key", field_encryptor=StringEncryptor),
|
|
||||||
SensitiveField(path="public_key", field_encryptor=StringEncryptor),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigService:
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_config(should_decrypt=True, is_island=False):
|
|
||||||
"""
|
|
||||||
Gets the entire global config.
|
|
||||||
|
|
||||||
:param should_decrypt: If True, all config values which are set as encrypted will be \
|
|
||||||
decrypted. \
|
|
||||||
:param is_island: If True, will include island specific configuration parameters. \
|
|
||||||
:return: The entire global config.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# is_initial_config and should_decrypt are only there to compare if we are on the
|
|
||||||
# default configuration or did user modified it already
|
|
||||||
config = mongo.db.config.find_one() or {}
|
|
||||||
config.pop("_id", None)
|
|
||||||
if should_decrypt and len(config) > 0:
|
|
||||||
ConfigService.decrypt_config(config)
|
|
||||||
if not is_island:
|
|
||||||
config.get("cnc", {}).pop("aws_config", None)
|
|
||||||
return config
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_config_value(config_key_as_arr, should_decrypt=True):
|
|
||||||
"""
|
|
||||||
Get a specific config value.
|
|
||||||
|
|
||||||
:param config_key_as_arr: The config key as an array.
|
|
||||||
e.g. ['basic', 'credentials','exploit_password_list'].
|
|
||||||
:param should_decrypt: If True, the value of the config key will be decrypted
|
|
||||||
(if it's in the list of encrypted config values).
|
|
||||||
:return: The value of the requested config key.
|
|
||||||
"""
|
|
||||||
config_key = functools.reduce(lambda x, y: x + "." + y, config_key_as_arr)
|
|
||||||
|
|
||||||
# This should just call get_config from repository. If None, then call get_default prob
|
|
||||||
config = mongo.db.config.find_one({}, {config_key: 1})
|
|
||||||
|
|
||||||
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_VALUES:
|
|
||||||
if isinstance(config, str):
|
|
||||||
config = get_datastore_encryptor().decrypt(config)
|
|
||||||
elif isinstance(config, list):
|
|
||||||
if config:
|
|
||||||
if isinstance(config[0], str):
|
|
||||||
config = [get_datastore_encryptor().decrypt(x) for x in config]
|
|
||||||
elif isinstance(config[0], dict) and "public_key" in config[0]:
|
|
||||||
config = [decrypt_dict(SENSITIVE_SSH_KEY_FIELDS, x) for x in config]
|
|
||||||
|
|
||||||
return config
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def set_config_value(config_key_as_arr, value):
|
|
||||||
mongo_key = ".".join(config_key_as_arr)
|
|
||||||
mongo.db.config.update({}, {"$set": {mongo_key: value}})
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _filter_none_values(data):
|
|
||||||
if isinstance(data, dict):
|
|
||||||
return {
|
|
||||||
k: ConfigService._filter_none_values(v)
|
|
||||||
for k, v in data.items()
|
|
||||||
if k is not None and v is not None
|
|
||||||
}
|
|
||||||
elif isinstance(data, list):
|
|
||||||
return [ConfigService._filter_none_values(item) for item in data if item is not None]
|
|
||||||
else:
|
|
||||||
return data
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def update_config(config_json, should_encrypt):
|
|
||||||
# PBA file upload happens on pba_file_upload endpoint and corresponding config options
|
|
||||||
# are set there
|
|
||||||
config_json = ConfigService._filter_none_values(config_json)
|
|
||||||
ConfigService.set_config_PBA_files(config_json)
|
|
||||||
if should_encrypt:
|
|
||||||
try:
|
|
||||||
ConfigService.encrypt_config(config_json)
|
|
||||||
except KeyError:
|
|
||||||
logger.error("Bad configuration file was submitted.")
|
|
||||||
return False
|
|
||||||
mongo.db.config.update({}, {"$set": config_json}, upsert=True)
|
|
||||||
logger.info("monkey config was updated")
|
|
||||||
return True
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def set_config_PBA_files(config_json):
|
|
||||||
"""
|
|
||||||
Sets PBA file info in config_json to current config's PBA file info values.
|
|
||||||
:param config_json: config_json that will be modified
|
|
||||||
"""
|
|
||||||
if ConfigService.get_config():
|
|
||||||
linux_filename = ConfigService.get_config_value(PBA_LINUX_FILENAME_PATH)
|
|
||||||
windows_filename = ConfigService.get_config_value(PBA_WINDOWS_FILENAME_PATH)
|
|
||||||
|
|
||||||
ConfigService.set_config_value(PBA_LINUX_FILENAME_PATH, linux_filename)
|
|
||||||
ConfigService.set_config_value(PBA_WINDOWS_FILENAME_PATH, windows_filename)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def decrypt_config(config):
|
|
||||||
ConfigService._encrypt_or_decrypt_config(config, True)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def encrypt_config(config):
|
|
||||||
ConfigService._encrypt_or_decrypt_config(config, False)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _encrypt_or_decrypt_config(config, is_decrypt=False):
|
|
||||||
for config_arr_as_array in ENCRYPTED_CONFIG_VALUES:
|
|
||||||
config_arr = config
|
|
||||||
parent_config_arr = None
|
|
||||||
|
|
||||||
# Because the config isn't flat, this for-loop gets the actual config value out of
|
|
||||||
# the config
|
|
||||||
for config_key_part in config_arr_as_array:
|
|
||||||
parent_config_arr = config_arr
|
|
||||||
config_arr = config_arr[config_key_part]
|
|
||||||
|
|
||||||
if isinstance(config_arr, collections.abc.Sequence) and not isinstance(config_arr, str):
|
|
||||||
for i in range(len(config_arr)):
|
|
||||||
# Check if array of shh key pairs and then decrypt
|
|
||||||
if isinstance(config_arr[i], dict) and "public_key" in config_arr[i]:
|
|
||||||
config_arr[i] = (
|
|
||||||
decrypt_dict(SENSITIVE_SSH_KEY_FIELDS, config_arr[i])
|
|
||||||
if is_decrypt
|
|
||||||
else encrypt_dict(SENSITIVE_SSH_KEY_FIELDS, config_arr[i])
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
config_arr[i] = (
|
|
||||||
get_datastore_encryptor().decrypt(config_arr[i])
|
|
||||||
if is_decrypt
|
|
||||||
else get_datastore_encryptor().encrypt(config_arr[i])
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
parent_config_arr[config_arr_as_array[-1]] = (
|
|
||||||
get_datastore_encryptor().decrypt(config_arr)
|
|
||||||
if is_decrypt
|
|
||||||
else get_datastore_encryptor().encrypt(config_arr)
|
|
||||||
)
|
|
Loading…
Reference in New Issue