forked from p15670423/monkey
Merge pull request #86 from guardicore/feature/secure-island-db
Feature/secure island db
This commit is contained in:
commit
1a3ca06ce8
|
@ -0,0 +1,51 @@
|
|||
import base64
|
||||
import os
|
||||
|
||||
from Crypto import Random
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
__author__ = "itay.mizeretz"
|
||||
|
||||
|
||||
class Encryptor:
|
||||
_BLOCK_SIZE = 32
|
||||
_DB_PASSWORD_FILENAME = "mongo_key.bin"
|
||||
|
||||
def __init__(self):
|
||||
self._load_key()
|
||||
|
||||
def _init_key(self):
|
||||
self._cipher_key = Random.new().read(self._BLOCK_SIZE)
|
||||
with open(self._DB_PASSWORD_FILENAME, 'wb') as f:
|
||||
f.write(self._cipher_key)
|
||||
|
||||
def _load_existing_key(self):
|
||||
with open(self._DB_PASSWORD_FILENAME, '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))
|
||||
|
||||
def _unpad(self, message):
|
||||
return message[0:-ord(message[len(message) - 1])]
|
||||
|
||||
def enc(self, message):
|
||||
cipher_iv = Random.new().read(AES.block_size)
|
||||
cipher = AES.new(self._cipher_key, AES.MODE_CBC, cipher_iv)
|
||||
return base64.b64encode(cipher_iv + cipher.encrypt(self._pad(message)))
|
||||
|
||||
def dec(self, enc_message):
|
||||
enc_message = base64.b64decode(enc_message)
|
||||
cipher_iv = enc_message[0:AES.block_size]
|
||||
cipher = AES.new(self._cipher_key, AES.MODE_CBC, cipher_iv)
|
||||
return self._unpad(cipher.decrypt(enc_message[AES.block_size:]))
|
||||
|
||||
|
||||
encryptor = Encryptor()
|
|
@ -65,7 +65,7 @@ class Monkey(flask_restful.Resource):
|
|||
# if new monkey telem, change config according to "new monkeys" config.
|
||||
db_monkey = mongo.db.monkey.find_one({"guid": monkey_json["guid"]})
|
||||
if not db_monkey:
|
||||
new_config = ConfigService.get_flat_config()
|
||||
new_config = ConfigService.get_flat_config(False, True)
|
||||
monkey_json['config'] = monkey_json.get('config', {})
|
||||
monkey_json['config'].update(new_config)
|
||||
else:
|
||||
|
|
|
@ -12,7 +12,7 @@ __author__ = 'Barak'
|
|||
class MonkeyConfiguration(flask_restful.Resource):
|
||||
@jwt_required()
|
||||
def get(self):
|
||||
return jsonify(schema=ConfigService.get_config_schema(), configuration=ConfigService.get_config())
|
||||
return jsonify(schema=ConfigService.get_config_schema(), configuration=ConfigService.get_config(False, True))
|
||||
|
||||
@jwt_required()
|
||||
def post(self):
|
||||
|
@ -20,5 +20,5 @@ class MonkeyConfiguration(flask_restful.Resource):
|
|||
if config_json.has_key('reset'):
|
||||
ConfigService.reset_config()
|
||||
else:
|
||||
ConfigService.update_config(config_json)
|
||||
ConfigService.update_config(config_json, should_encrypt=True)
|
||||
return self.get()
|
||||
|
|
|
@ -12,6 +12,7 @@ from cc.database import mongo
|
|||
from cc.services.config import ConfigService
|
||||
from cc.services.edge import EdgeService
|
||||
from cc.services.node import NodeService
|
||||
from cc.encryptor import encryptor
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
||||
|
@ -112,6 +113,8 @@ class Telemetry(flask_restful.Resource):
|
|||
@staticmethod
|
||||
def process_exploit_telemetry(telemetry_json):
|
||||
edge = Telemetry.get_edge_by_scan_or_exploit_telemetry(telemetry_json)
|
||||
Telemetry.encrypt_exploit_creds(telemetry_json)
|
||||
|
||||
new_exploit = copy.deepcopy(telemetry_json['data'])
|
||||
|
||||
new_exploit.pop('machine')
|
||||
|
@ -166,25 +169,49 @@ class Telemetry(flask_restful.Resource):
|
|||
def process_system_info_telemetry(telemetry_json):
|
||||
if 'credentials' in telemetry_json['data']:
|
||||
creds = telemetry_json['data']['credentials']
|
||||
for user in creds:
|
||||
ConfigService.creds_add_username(user)
|
||||
if 'password' in creds[user]:
|
||||
ConfigService.creds_add_password(creds[user]['password'])
|
||||
if 'lm_hash' in creds[user]:
|
||||
ConfigService.creds_add_lm_hash(creds[user]['lm_hash'])
|
||||
if 'ntlm_hash' in creds[user]:
|
||||
ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash'])
|
||||
|
||||
for user in creds:
|
||||
if -1 != user.find('.'):
|
||||
new_user = user.replace('.', ',')
|
||||
creds[new_user] = creds.pop(user)
|
||||
Telemetry.encrypt_system_info_creds(creds)
|
||||
Telemetry.add_system_info_creds_to_config(creds)
|
||||
Telemetry.replace_user_dot_with_comma(creds)
|
||||
|
||||
@staticmethod
|
||||
def process_trace_telemetry(telemetry_json):
|
||||
# Nothing to do
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def replace_user_dot_with_comma(creds):
|
||||
for user in creds:
|
||||
if -1 != user.find('.'):
|
||||
new_user = user.replace('.', ',')
|
||||
creds[new_user] = creds.pop(user)
|
||||
|
||||
@staticmethod
|
||||
def encrypt_system_info_creds(creds):
|
||||
for user in creds:
|
||||
for field in ['password', 'lm_hash', 'ntlm_hash']:
|
||||
if field in creds[user]:
|
||||
creds[user][field] = encryptor.enc(creds[user][field])
|
||||
|
||||
@staticmethod
|
||||
def add_system_info_creds_to_config(creds):
|
||||
for user in creds:
|
||||
ConfigService.creds_add_username(user)
|
||||
if 'password' in creds[user]:
|
||||
ConfigService.creds_add_password(creds[user]['password'])
|
||||
if 'lm_hash' in creds[user]:
|
||||
ConfigService.creds_add_lm_hash(creds[user]['lm_hash'])
|
||||
if 'ntlm_hash' in creds[user]:
|
||||
ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash'])
|
||||
|
||||
@staticmethod
|
||||
def encrypt_exploit_creds(telemetry_json):
|
||||
attempts = telemetry_json['data']['attempts']
|
||||
for i in range(len(attempts)):
|
||||
for field in ['password', 'lm_hash', 'ntlm_hash']:
|
||||
credential = attempts[i][field]
|
||||
if len(credential) > 0:
|
||||
attempts[i][field] = encryptor.enc(credential)
|
||||
|
||||
|
||||
TELEM_PROCESS_DICT = \
|
||||
{
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
from cc.database import mongo
|
||||
import copy
|
||||
import functools
|
||||
from jsonschema import Draft4Validator, validators
|
||||
|
||||
from cc.database import mongo
|
||||
from cc.encryptor import encryptor
|
||||
from cc.environment.environment import env
|
||||
from cc.utils import local_ip_addresses
|
||||
|
||||
|
@ -17,60 +20,60 @@ SCHEMA = {
|
|||
"type": "string",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"SmbExploiter"
|
||||
],
|
||||
"title": "SMB Exploiter"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"SmbExploiter"
|
||||
],
|
||||
"title": "SMB Exploiter"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"WmiExploiter"
|
||||
],
|
||||
"title": "WMI Exploiter"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"WmiExploiter"
|
||||
],
|
||||
"title": "WMI Exploiter"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"RdpExploiter"
|
||||
],
|
||||
"title": "RDP Exploiter (UNSAFE)"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"RdpExploiter"
|
||||
],
|
||||
"title": "RDP Exploiter (UNSAFE)"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Ms08_067_Exploiter"
|
||||
],
|
||||
"title": "MS08-067 Exploiter (UNSAFE)"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Ms08_067_Exploiter"
|
||||
],
|
||||
"title": "MS08-067 Exploiter (UNSAFE)"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"SSHExploiter"
|
||||
],
|
||||
"title": "SSH Exploiter"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"SSHExploiter"
|
||||
],
|
||||
"title": "SSH Exploiter"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ShellShockExploiter"
|
||||
],
|
||||
"title": "ShellShock Exploiter"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ShellShockExploiter"
|
||||
],
|
||||
"title": "ShellShock Exploiter"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"SambaCryExploiter"
|
||||
],
|
||||
"title": "SambaCry Exploiter"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"SambaCryExploiter"
|
||||
],
|
||||
"title": "SambaCry Exploiter"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ElasticGroovyExploiter"
|
||||
],
|
||||
"title": "ElasticGroovy Exploiter"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ElasticGroovyExploiter"
|
||||
],
|
||||
"title": "ElasticGroovy Exploiter"
|
||||
},
|
||||
]
|
||||
},
|
||||
|
@ -79,46 +82,46 @@ SCHEMA = {
|
|||
"type": "string",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"SMBFinger"
|
||||
],
|
||||
"title": "SMBFinger"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"SMBFinger"
|
||||
],
|
||||
"title": "SMBFinger"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"SSHFinger"
|
||||
],
|
||||
"title": "SSHFinger"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"SSHFinger"
|
||||
],
|
||||
"title": "SSHFinger"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"PingScanner"
|
||||
],
|
||||
"title": "PingScanner"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"PingScanner"
|
||||
],
|
||||
"title": "PingScanner"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"HTTPFinger"
|
||||
],
|
||||
"title": "HTTPFinger"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"HTTPFinger"
|
||||
],
|
||||
"title": "HTTPFinger"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"MySQLFinger"
|
||||
],
|
||||
"title": "MySQLFinger"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"MySQLFinger"
|
||||
],
|
||||
"title": "MySQLFinger"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ElasticFinger"
|
||||
],
|
||||
"title": "ElasticFinger"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ElasticFinger"
|
||||
],
|
||||
"title": "ElasticFinger"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -800,29 +803,56 @@ SCHEMA = {
|
|||
}
|
||||
}
|
||||
|
||||
ENCRYPTED_CONFIG_ARRAYS = \
|
||||
[
|
||||
['basic', 'credentials', 'exploit_password_list'],
|
||||
['internal', 'exploits', 'exploit_lm_hash_list'],
|
||||
['internal', 'exploits', 'exploit_ntlm_hash_list']
|
||||
]
|
||||
|
||||
|
||||
class ConfigService:
|
||||
default_config = None
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_config(is_initial_config=False):
|
||||
def get_config(is_initial_config=False, should_decrypt=True):
|
||||
"""
|
||||
Gets the entire global config.
|
||||
:param is_initial_config: If True, the initial config will be returned instead of the current config.
|
||||
:param should_decrypt: If True, all config values which are set as encrypted will be decrypted.
|
||||
:return: The entire global config.
|
||||
"""
|
||||
config = mongo.db.config.find_one({'name': 'initial' if is_initial_config else 'newconfig'}) or {}
|
||||
for field in ('name', '_id'):
|
||||
config.pop(field, None)
|
||||
if should_decrypt and len(config) > 0:
|
||||
ConfigService.decrypt_config(config)
|
||||
return config
|
||||
|
||||
@staticmethod
|
||||
def get_config_value(config_key_as_arr, is_initial_config=False):
|
||||
config_key = reduce(lambda x, y: x+'.'+y, config_key_as_arr)
|
||||
def get_config_value(config_key_as_arr, is_initial_config=False, 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 is_initial_config: If True, returns the value of the initial config instead of the current config.
|
||||
: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)
|
||||
config = mongo.db.config.find_one({'name': 'initial' if is_initial_config else 'newconfig'}, {config_key: 1})
|
||||
for config_key_part in config_key_as_arr:
|
||||
config = config[config_key_part]
|
||||
if should_decrypt and (config_key_as_arr in ENCRYPTED_CONFIG_ARRAYS):
|
||||
config = [encryptor.dec(x) for x in config]
|
||||
return config
|
||||
|
||||
@staticmethod
|
||||
def get_flat_config(is_initial_config=False):
|
||||
config_json = ConfigService.get_config(is_initial_config)
|
||||
def get_flat_config(is_initial_config=False, should_decrypt=True):
|
||||
config_json = ConfigService.get_config(is_initial_config, should_decrypt)
|
||||
flat_config_json = {}
|
||||
for i in config_json:
|
||||
for j in config_json[i]:
|
||||
|
@ -866,27 +896,38 @@ class ConfigService:
|
|||
ConfigService.add_item_to_config_set('internal.exploits.exploit_ntlm_hash_list', ntlm_hash)
|
||||
|
||||
@staticmethod
|
||||
def update_config(config_json):
|
||||
def update_config(config_json, should_encrypt):
|
||||
if should_encrypt:
|
||||
ConfigService.encrypt_config(config_json)
|
||||
mongo.db.config.update({'name': 'newconfig'}, {"$set": config_json}, upsert=True)
|
||||
|
||||
@staticmethod
|
||||
def get_default_config():
|
||||
defaultValidatingDraft4Validator = ConfigService._extend_config_with_default(Draft4Validator)
|
||||
config = {}
|
||||
defaultValidatingDraft4Validator(SCHEMA).validate(config)
|
||||
def init_default_config():
|
||||
if ConfigService.default_config is None:
|
||||
defaultValidatingDraft4Validator = ConfigService._extend_config_with_default(Draft4Validator)
|
||||
config = {}
|
||||
defaultValidatingDraft4Validator(SCHEMA).validate(config)
|
||||
ConfigService.default_config = config
|
||||
|
||||
@staticmethod
|
||||
def get_default_config(should_encrypt=False):
|
||||
ConfigService.init_default_config()
|
||||
config = copy.deepcopy(ConfigService.default_config)
|
||||
if should_encrypt:
|
||||
ConfigService.encrypt_config(config)
|
||||
return config
|
||||
|
||||
@staticmethod
|
||||
def init_config():
|
||||
if ConfigService.get_config() != {}:
|
||||
if ConfigService.get_config(should_decrypt=False) != {}:
|
||||
return
|
||||
ConfigService.reset_config()
|
||||
|
||||
@staticmethod
|
||||
def reset_config():
|
||||
config = ConfigService.get_default_config()
|
||||
config = ConfigService.get_default_config(True)
|
||||
ConfigService.set_server_ips_in_config(config)
|
||||
ConfigService.update_config(config)
|
||||
ConfigService.update_config(config, should_encrypt=False)
|
||||
|
||||
@staticmethod
|
||||
def set_server_ips_in_config(config):
|
||||
|
@ -928,3 +969,21 @@ class ConfigService:
|
|||
return validators.extend(
|
||||
validator_class, {"properties": set_defaults},
|
||||
)
|
||||
|
||||
@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_ARRAYS:
|
||||
config_arr = config
|
||||
for config_key_part in config_arr_as_array:
|
||||
config_arr = config_arr[config_key_part]
|
||||
|
||||
for i in range(len(config_arr)):
|
||||
config_arr[i] = encryptor.dec(config_arr[i]) if is_decrypt else encryptor.enc(config_arr[i])
|
||||
|
|
|
@ -293,19 +293,19 @@ class ReportService:
|
|||
|
||||
@staticmethod
|
||||
def get_config_users():
|
||||
return ConfigService.get_config_value(['basic', 'credentials', 'exploit_user_list'], True)
|
||||
return ConfigService.get_config_value(['basic', 'credentials', 'exploit_user_list'], True, True)
|
||||
|
||||
@staticmethod
|
||||
def get_config_passwords():
|
||||
return ConfigService.get_config_value(['basic', 'credentials', 'exploit_password_list'], True)
|
||||
return ConfigService.get_config_value(['basic', 'credentials', 'exploit_password_list'], True, True)
|
||||
|
||||
@staticmethod
|
||||
def get_config_exploits():
|
||||
exploits_config_value = ['exploits', 'general', 'exploiter_classes']
|
||||
default_exploits = ConfigService.get_default_config()
|
||||
default_exploits = ConfigService.get_default_config(False)
|
||||
for namespace in exploits_config_value:
|
||||
default_exploits = default_exploits[namespace]
|
||||
exploits = ConfigService.get_config_value(exploits_config_value, True)
|
||||
exploits = ConfigService.get_config_value(exploits_config_value, True, True)
|
||||
|
||||
if exploits == default_exploits:
|
||||
return ['default']
|
||||
|
@ -315,13 +315,13 @@ class ReportService:
|
|||
|
||||
@staticmethod
|
||||
def get_config_ips():
|
||||
if ConfigService.get_config_value(['basic_network', 'network_range', 'range_class'], True) != 'FixedRange':
|
||||
if ConfigService.get_config_value(['basic_network', 'network_range', 'range_class'], True, True) != 'FixedRange':
|
||||
return []
|
||||
return ConfigService.get_config_value(['basic_network', 'network_range', 'range_fixed'], True)
|
||||
return ConfigService.get_config_value(['basic_network', 'network_range', 'range_fixed'], True, True)
|
||||
|
||||
@staticmethod
|
||||
def get_config_scan():
|
||||
return ConfigService.get_config_value(['basic_network', 'general', 'local_network_scan'], True)
|
||||
return ConfigService.get_config_value(['basic_network', 'general', 'local_network_scan'], True, True)
|
||||
|
||||
@staticmethod
|
||||
def get_issues_overview(issues, config_users, config_passwords):
|
||||
|
|
|
@ -13,4 +13,5 @@ jsonschema
|
|||
netifaces
|
||||
ipaddress
|
||||
enum34
|
||||
PyCrypto
|
||||
virtualenv
|
|
@ -12,4 +12,5 @@ Flask-JWT
|
|||
jsonschema
|
||||
netifaces
|
||||
ipaddress
|
||||
enum34
|
||||
enum34
|
||||
PyCrypto
|
||||
|
|
Loading…
Reference in New Issue