From 4197ab12a39e184a3d68cac7de99c54cb1ed3d22 Mon Sep 17 00:00:00 2001 From: Vakaris Date: Thu, 24 May 2018 16:59:22 +0300 Subject: [PATCH] SSH keys are now encrypted and added to database --- infection_monkey/config.py | 1 + .../system_info/SSH_info_collector.py | 27 +++++++------ monkey_island/cc/resources/telemetry.py | 21 +++++++++- monkey_island/cc/services/config.py | 39 +++++++++++++++++-- 4 files changed, 71 insertions(+), 17 deletions(-) diff --git a/infection_monkey/config.py b/infection_monkey/config.py index 41ecd1d91..1f5fce4b1 100644 --- a/infection_monkey/config.py +++ b/infection_monkey/config.py @@ -251,6 +251,7 @@ class Configuration(object): exploit_password_list = ["Password1!", "1234", "password", "12345678"] exploit_lm_hash_list = [] exploit_ntlm_hash_list = [] + exploit_ssh_keys = [] # smb/wmi exploiter smb_download_timeout = 300 # timeout in seconds diff --git a/infection_monkey/system_info/SSH_info_collector.py b/infection_monkey/system_info/SSH_info_collector.py index 12ff7df02..12244abac 100644 --- a/infection_monkey/system_info/SSH_info_collector.py +++ b/infection_monkey/system_info/SSH_info_collector.py @@ -1,6 +1,5 @@ import logging import pwd -import sys import os import glob @@ -36,13 +35,13 @@ class SSHCollector(object): possibly hashed) """ return {'name': name, 'home_dir': home_dir, 'public_key': None, - 'private_key': None, 'known_hosts': None} + 'private_key': None, 'known_hosts': None} @staticmethod def get_home_dirs(): - root_dir = SSHCollector.get_ssh_struct('root', '/') - home_dirs = [SSHCollector.get_ssh_struct(x.pw_name,x.pw_dir) for x in pwd.getpwall() - if x.pw_dir.startswith('/home')] + root_dir = SSHCollector.get_ssh_struct('root', '') + home_dirs = [SSHCollector.get_ssh_struct(x.pw_name, x.pw_dir) for x in pwd.getpwall() + if x.pw_dir.startswith('/home')] home_dirs.append(root_dir) return home_dirs @@ -54,14 +53,16 @@ class SSHCollector(object): if os.path.isdir(path + directory): try: current_path = path + directory - # searching for public key - if glob.glob(current_path+'*.pub'): - public = (glob.glob(current_path+'*.pub')[0]) + # Searching for public key + if glob.glob(os.path.join(current_path, '*.pub')): + # Getting first file in current path with .pub extension(public key) + public = (glob.glob(os.path.join(current_path, '*.pub'))[0]) LOG.info("Found public key in %s" % public) try: with open(public) as f: info['public_key'] = f.read() - private = public.rsplit('.', 1)[0] + # By default private key has the same name as public, only without .pub + private = os.path.splitext(public)[0] if os.path.exists(private): try: with open(private) as f: @@ -69,11 +70,13 @@ class SSHCollector(object): LOG.info("Found private key in %s" % private) except (IOError, OSError): pass - if os.path.exists(current_path + '/known_hosts'): + # By default known hosts file is called 'known_hosts' + known_hosts = os.path.join(current_path, 'known_hosts') + if os.path.exists(known_hosts): try: - with open(current_path + '/known_hosts') as f: + with open(known_hosts) as f: info['known_hosts'] = f.read() - LOG.info("Found known_hosts in %s" % current_path+'/known_hosts') + LOG.info("Found known_hosts in %s" % known_hosts) except (IOError, OSError): pass except (IOError, OSError): diff --git a/monkey_island/cc/resources/telemetry.py b/monkey_island/cc/resources/telemetry.py index 6095b0946..452a33818 100644 --- a/monkey_island/cc/resources/telemetry.py +++ b/monkey_island/cc/resources/telemetry.py @@ -167,6 +167,10 @@ class Telemetry(flask_restful.Resource): @staticmethod def process_system_info_telemetry(telemetry_json): + if 'ssh_info' in telemetry_json['data']: + ssh_info = telemetry_json['data']['ssh_info'] + Telemetry.encrypt_system_info_creds({}, ssh_info) + Telemetry.add_system_info_creds_to_config({}, ssh_info) if 'credentials' in telemetry_json['data']: creds = telemetry_json['data']['credentials'] Telemetry.encrypt_system_info_creds(creds) @@ -186,15 +190,20 @@ class Telemetry(flask_restful.Resource): creds[new_user] = creds.pop(user) @staticmethod - def encrypt_system_info_creds(creds): + def encrypt_system_info_creds(creds, ssh_info=None): for user in creds: for field in ['password', 'lm_hash', 'ntlm_hash']: if field in creds[user]: # this encoding is because we might run into passwords which are not pure ASCII creds[user][field] = encryptor.enc(creds[user][field].encode('utf-8')) + if 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].encode('utf-8')) @staticmethod - def add_system_info_creds_to_config(creds): + def add_system_info_creds_to_config(creds, ssh_info=None): for user in creds: ConfigService.creds_add_username(user) if 'password' in creds[user]: @@ -203,6 +212,14 @@ class Telemetry(flask_restful.Resource): ConfigService.creds_add_lm_hash(creds[user]['lm_hash']) if 'ntlm_hash' in creds[user]: ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash']) + if ssh_info: + for user in ssh_info: + ConfigService.creds_add_username(user['name']) + # Public key is useless without private key + if user['public_key'] and user['private_key']: + ConfigService.ssh_add_keys(user['public_key'], user['private_key']) + + @staticmethod def encrypt_exploit_creds(telemetry_json): diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py index ebcf2a3ea..2ffc990a9 100644 --- a/monkey_island/cc/services/config.py +++ b/monkey_island/cc/services/config.py @@ -1,6 +1,7 @@ import copy import collections import functools +import json from jsonschema import Draft4Validator, validators from cc.database import mongo @@ -504,6 +505,13 @@ SCHEMA = { }, "default": [], "description": "List of NTLM hashes to use on exploits using credentials" + }, + "exploit_ssh_keys": { + "title": "SSH key pairs list", + "type": "array", + "uniqueItems": True, + "default": [], + "description": "List of SSH key pairs to use, when trying to ssh into servers" } } }, @@ -800,7 +808,8 @@ ENCRYPTED_CONFIG_ARRAYS = \ [ ['basic', 'credentials', 'exploit_password_list'], ['internal', 'exploits', 'exploit_lm_hash_list'], - ['internal', 'exploits', 'exploit_ntlm_hash_list'] + ['internal', 'exploits', 'exploit_ntlm_hash_list'], + ['internal', 'exploits', 'exploit_ssh_keys'] ] @@ -888,6 +897,11 @@ class ConfigService: def creds_add_ntlm_hash(ntlm_hash): ConfigService.add_item_to_config_set('internal.exploits.exploit_ntlm_hash_list', ntlm_hash) + @staticmethod + def ssh_add_keys(public_key, private_key): + ConfigService.add_item_to_config_set('internal.exploits.exploit_ssh_keys', + {"public_key": public_key, "private_key": private_key}) + @staticmethod def update_config(config_json, should_encrypt): if should_encrypt: @@ -979,7 +993,11 @@ class ConfigService: keys = [config_arr_as_array[2] for config_arr_as_array in ENCRYPTED_CONFIG_ARRAYS] for key in keys: if isinstance(flat_config[key], collections.Sequence) and not isinstance(flat_config[key], basestring): - flat_config[key] = [encryptor.dec(item) for item in flat_config[key]] + # Check if we are decrypting ssh key pair + 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]] else: flat_config[key] = encryptor.dec(flat_config[key]) return flat_config @@ -992,4 +1010,19 @@ class ConfigService: 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]) + # 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] = 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]) + + @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']) + else: + pair['public_key'] = encryptor.dec(pair['public_key']) + pair['private_key'] = encryptor.dec(pair['private_key']) + return pair