forked from p15670423/monkey
SSH keys are now encrypted and added to database
This commit is contained in:
parent
e8b388482b
commit
4197ab12a3
|
@ -251,6 +251,7 @@ class Configuration(object):
|
||||||
exploit_password_list = ["Password1!", "1234", "password", "12345678"]
|
exploit_password_list = ["Password1!", "1234", "password", "12345678"]
|
||||||
exploit_lm_hash_list = []
|
exploit_lm_hash_list = []
|
||||||
exploit_ntlm_hash_list = []
|
exploit_ntlm_hash_list = []
|
||||||
|
exploit_ssh_keys = []
|
||||||
|
|
||||||
# smb/wmi exploiter
|
# smb/wmi exploiter
|
||||||
smb_download_timeout = 300 # timeout in seconds
|
smb_download_timeout = 300 # timeout in seconds
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
import pwd
|
import pwd
|
||||||
import sys
|
|
||||||
import os
|
import os
|
||||||
import glob
|
import glob
|
||||||
|
|
||||||
|
@ -36,13 +35,13 @@ class SSHCollector(object):
|
||||||
possibly hashed)
|
possibly hashed)
|
||||||
"""
|
"""
|
||||||
return {'name': name, 'home_dir': home_dir, 'public_key': None,
|
return {'name': name, 'home_dir': home_dir, 'public_key': None,
|
||||||
'private_key': None, 'known_hosts': None}
|
'private_key': None, 'known_hosts': None}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_home_dirs():
|
def get_home_dirs():
|
||||||
root_dir = SSHCollector.get_ssh_struct('root', '/')
|
root_dir = SSHCollector.get_ssh_struct('root', '')
|
||||||
home_dirs = [SSHCollector.get_ssh_struct(x.pw_name,x.pw_dir) for x in pwd.getpwall()
|
home_dirs = [SSHCollector.get_ssh_struct(x.pw_name, x.pw_dir) for x in pwd.getpwall()
|
||||||
if x.pw_dir.startswith('/home')]
|
if x.pw_dir.startswith('/home')]
|
||||||
home_dirs.append(root_dir)
|
home_dirs.append(root_dir)
|
||||||
return home_dirs
|
return home_dirs
|
||||||
|
|
||||||
|
@ -54,14 +53,16 @@ class SSHCollector(object):
|
||||||
if os.path.isdir(path + directory):
|
if os.path.isdir(path + directory):
|
||||||
try:
|
try:
|
||||||
current_path = path + directory
|
current_path = path + directory
|
||||||
# searching for public key
|
# Searching for public key
|
||||||
if glob.glob(current_path+'*.pub'):
|
if glob.glob(os.path.join(current_path, '*.pub')):
|
||||||
public = (glob.glob(current_path+'*.pub')[0])
|
# 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)
|
LOG.info("Found public key in %s" % public)
|
||||||
try:
|
try:
|
||||||
with open(public) as f:
|
with open(public) as f:
|
||||||
info['public_key'] = f.read()
|
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):
|
if os.path.exists(private):
|
||||||
try:
|
try:
|
||||||
with open(private) as f:
|
with open(private) as f:
|
||||||
|
@ -69,11 +70,13 @@ class SSHCollector(object):
|
||||||
LOG.info("Found private key in %s" % private)
|
LOG.info("Found private key in %s" % private)
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
pass
|
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:
|
try:
|
||||||
with open(current_path + '/known_hosts') as f:
|
with open(known_hosts) as f:
|
||||||
info['known_hosts'] = f.read()
|
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):
|
except (IOError, OSError):
|
||||||
pass
|
pass
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
|
|
|
@ -167,6 +167,10 @@ class Telemetry(flask_restful.Resource):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def process_system_info_telemetry(telemetry_json):
|
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']:
|
if 'credentials' in telemetry_json['data']:
|
||||||
creds = telemetry_json['data']['credentials']
|
creds = telemetry_json['data']['credentials']
|
||||||
Telemetry.encrypt_system_info_creds(creds)
|
Telemetry.encrypt_system_info_creds(creds)
|
||||||
|
@ -186,15 +190,20 @@ class Telemetry(flask_restful.Resource):
|
||||||
creds[new_user] = creds.pop(user)
|
creds[new_user] = creds.pop(user)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def encrypt_system_info_creds(creds):
|
def encrypt_system_info_creds(creds, ssh_info=None):
|
||||||
for user in creds:
|
for user in creds:
|
||||||
for field in ['password', 'lm_hash', 'ntlm_hash']:
|
for field in ['password', 'lm_hash', 'ntlm_hash']:
|
||||||
if field in creds[user]:
|
if field in creds[user]:
|
||||||
# this encoding is because we might run into passwords which are not pure ASCII
|
# 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'))
|
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
|
@staticmethod
|
||||||
def add_system_info_creds_to_config(creds):
|
def add_system_info_creds_to_config(creds, ssh_info=None):
|
||||||
for user in creds:
|
for user in creds:
|
||||||
ConfigService.creds_add_username(user)
|
ConfigService.creds_add_username(user)
|
||||||
if 'password' in creds[user]:
|
if 'password' in creds[user]:
|
||||||
|
@ -203,6 +212,14 @@ class Telemetry(flask_restful.Resource):
|
||||||
ConfigService.creds_add_lm_hash(creds[user]['lm_hash'])
|
ConfigService.creds_add_lm_hash(creds[user]['lm_hash'])
|
||||||
if 'ntlm_hash' in creds[user]:
|
if 'ntlm_hash' in creds[user]:
|
||||||
ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash'])
|
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
|
@staticmethod
|
||||||
def encrypt_exploit_creds(telemetry_json):
|
def encrypt_exploit_creds(telemetry_json):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import copy
|
import copy
|
||||||
import collections
|
import collections
|
||||||
import functools
|
import functools
|
||||||
|
import json
|
||||||
from jsonschema import Draft4Validator, validators
|
from jsonschema import Draft4Validator, validators
|
||||||
|
|
||||||
from cc.database import mongo
|
from cc.database import mongo
|
||||||
|
@ -504,6 +505,13 @@ SCHEMA = {
|
||||||
},
|
},
|
||||||
"default": [],
|
"default": [],
|
||||||
"description": "List of NTLM hashes to use on exploits using credentials"
|
"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'],
|
['basic', 'credentials', 'exploit_password_list'],
|
||||||
['internal', 'exploits', 'exploit_lm_hash_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):
|
def creds_add_ntlm_hash(ntlm_hash):
|
||||||
ConfigService.add_item_to_config_set('internal.exploits.exploit_ntlm_hash_list', 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
|
@staticmethod
|
||||||
def update_config(config_json, should_encrypt):
|
def update_config(config_json, should_encrypt):
|
||||||
if 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]
|
keys = [config_arr_as_array[2] for config_arr_as_array in ENCRYPTED_CONFIG_ARRAYS]
|
||||||
for key in keys:
|
for key in keys:
|
||||||
if isinstance(flat_config[key], collections.Sequence) and not isinstance(flat_config[key], basestring):
|
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:
|
else:
|
||||||
flat_config[key] = encryptor.dec(flat_config[key])
|
flat_config[key] = encryptor.dec(flat_config[key])
|
||||||
return flat_config
|
return flat_config
|
||||||
|
@ -992,4 +1010,19 @@ class ConfigService:
|
||||||
config_arr = config_arr[config_key_part]
|
config_arr = config_arr[config_key_part]
|
||||||
|
|
||||||
for i in range(len(config_arr)):
|
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
|
||||||
|
|
Loading…
Reference in New Issue