Everything implemented on backend

This commit is contained in:
Itay Mizeretz 2017-11-27 15:20:59 +02:00
parent 82e30040eb
commit ce10ef00e4
5 changed files with 197 additions and 82 deletions

View File

@ -292,3 +292,21 @@ class NodeService:
{'_id': node_id}, {'_id': node_id},
{'$push': {'creds': creds}} {'$push': {'creds': creds}}
) )
@staticmethod
def get_node_or_monkey_by_ip(ip_address):
node = NodeService.get_node_by_ip(ip_address)
if node is not None:
return node
return NodeService.get_monkey_by_ip(ip_address)
@staticmethod
def get_node_or_monkey_by_id(node_id):
node = NodeService.get_node_by_id(node_id)
if node is not None:
return node
return NodeService.get_monkey_by_id(node_id)
@staticmethod
def get_node_hostname(node):
return node['hostname'] if 'hostname' in node else node['os']['version']

View File

@ -1,9 +1,9 @@
import datetime import ipaddress
from cc.database import mongo from cc.database import mongo
from cc.services.config import ConfigService
from cc.services.edge import EdgeService from cc.services.edge import EdgeService
from cc.services.node import NodeService from cc.services.node import NodeService
from cc.utils import local_ip_addresses, get_subnets
__author__ = "itay.mizeretz" __author__ = "itay.mizeretz"
@ -38,25 +38,20 @@ class ReportService:
st += "%d minutes and %d seconds" % (minutes, seconds) st += "%d minutes and %d seconds" % (minutes, seconds)
return st return st
@staticmethod
def get_breach_count():
return mongo.db.edge.count({'exploits.result': True})
@staticmethod
def get_successful_exploit_types():
exploit_types = mongo.db.command({'distinct': 'edge', 'key': 'exploits.exploiter'})['values']
return [exploit for exploit in exploit_types if ReportService.did_exploit_type_succeed(exploit)]
@staticmethod @staticmethod
def get_tunnels(): def get_tunnels():
return [ return [
(NodeService.get_monkey_label_by_id(tunnel['_id']), NodeService.get_monkey_label_by_id(tunnel['tunnel'])) {
'type': 'tunnel',
'origin': NodeService.get_node_hostname(NodeService.get_node_or_monkey_by_id(tunnel['_id'])),
'dest': NodeService.get_node_hostname(NodeService.get_node_or_monkey_by_id(tunnel['tunnel']))
}
for tunnel in mongo.db.monkey.find({'tunnel': {'$exists': True}}, {'tunnel': 1})] for tunnel in mongo.db.monkey.find({'tunnel': {'$exists': True}}, {'tunnel': 1})]
@staticmethod @staticmethod
def get_scanned(): def get_scanned():
nodes =\ nodes = \
[NodeService.get_displayed_node_by_id(node['_id']) for node in mongo.db.node.find({}, {'_id': 1})]\ [NodeService.get_displayed_node_by_id(node['_id']) for node in mongo.db.node.find({}, {'_id': 1})] \
+ [NodeService.get_displayed_node_by_id(monkey['_id']) for monkey in mongo.db.monkey.find({}, {'_id': 1})] + [NodeService.get_displayed_node_by_id(monkey['_id']) for monkey in mongo.db.monkey.find({}, {'_id': 1})]
nodes = [ nodes = [
{ {
@ -73,32 +68,17 @@ class ReportService:
return nodes return nodes
@staticmethod
def get_reused_passwords():
password_dict = {}
password_list = ConfigService.get_config_value(['basic', 'credentials', 'exploit_password_list'])
for password in password_list:
machines_with_password =\
[
NodeService.get_monkey_label_by_id(node['_id'])
for node in mongo.db.monkey.find({'creds.password': password}, {'_id': 1})
]
if len(machines_with_password) >= 2:
password_dict[password] = machines_with_password
return password_dict
@staticmethod @staticmethod
def get_exploited(): def get_exploited():
exploited =\ exploited = \
[NodeService.get_displayed_node_by_id(monkey['_id']) for monkey in mongo.db.monkey.find({}, {'_id': 1}) [NodeService.get_displayed_node_by_id(monkey['_id']) for monkey in mongo.db.monkey.find({}, {'_id': 1})
if not NodeService.get_monkey_manual_run(NodeService.get_monkey_by_id(monkey['_id']))]\ if not NodeService.get_monkey_manual_run(NodeService.get_monkey_by_id(monkey['_id']))] \
+ [NodeService.get_displayed_node_by_id(node['_id']) + [NodeService.get_displayed_node_by_id(node['_id'])
for node in mongo.db.node.find({'exploited': True}, {'_id': 1})] for node in mongo.db.node.find({'exploited': True}, {'_id': 1})]
exploited = [ exploited = [
{ {
'label': monkey['hostname'] if 'hostname' in monkey else monkey['os']['version'], 'label': NodeService.get_node_hostname(monkey),
'ip_addresses': monkey['ip_addresses'], 'ip_addresses': monkey['ip_addresses'],
'exploits': [exploit['exploiter'] for exploit in monkey['exploits'] if exploit['result']] 'exploits': [exploit['exploiter'] for exploit in monkey['exploits'] if exploit['result']]
} }
@ -130,6 +110,146 @@ class ReportService:
) )
return creds return creds
@staticmethod
def process_general_exploit(exploit):
ip_addr = exploit['data']['machine']['ip_addr']
return {'machine': NodeService.get_node_hostname(NodeService.get_node_or_monkey_by_ip(ip_addr)),
'ip_address': ip_addr}
@staticmethod
def process_general_creds_exploit(exploit):
processed_exploit = ReportService.process_general_exploit(exploit)
for attempt in exploit['data']['attempts']:
if attempt['result']:
processed_exploit['username'] = attempt['user']
if len(attempt['password']) > 0:
processed_exploit['type'] = 'password'
else:
processed_exploit['type'] = 'hash'
return processed_exploit
@staticmethod
def process_smb_exploit(exploit):
processed_exploit = ReportService.process_general_creds_exploit(exploit)
if processed_exploit['type'] == 'password':
processed_exploit['type'] = 'smb_password'
else:
processed_exploit['type'] = 'smb_pth'
return processed_exploit
@staticmethod
def process_wmi_exploit(exploit):
processed_exploit = ReportService.process_general_creds_exploit(exploit)
if processed_exploit['type'] == 'password':
processed_exploit['type'] = 'wmi_password'
else:
processed_exploit['type'] = 'wmi_pth'
return processed_exploit
@staticmethod
def process_ssh_exploit(exploit):
processed_exploit = ReportService.process_general_creds_exploit(exploit)
processed_exploit['type'] = 'ssh'
return processed_exploit
@staticmethod
def process_rdp_exploit(exploit):
processed_exploit = ReportService.process_general_creds_exploit(exploit)
processed_exploit['type'] = 'rdp'
return processed_exploit
@staticmethod
def process_sambacry_exploit(exploit):
processed_exploit = ReportService.process_general_creds_exploit(exploit)
processed_exploit['type'] = 'sambacry'
return processed_exploit
@staticmethod
def process_elastic_exploit(exploit):
processed_exploit = ReportService.process_general_exploit(exploit)
processed_exploit['type'] = 'elastic'
return processed_exploit
@staticmethod
def process_conficker_exploit(exploit):
processed_exploit = ReportService.process_general_exploit(exploit)
processed_exploit['type'] = 'conficker'
return processed_exploit
@staticmethod
def process_shellshock_exploit(exploit):
processed_exploit = ReportService.process_general_exploit(exploit)
processed_exploit['type'] = 'shellshock'
urls = exploit['data']['info']['vulnerable_urls']
processed_exploit['port'] = urls[0].split(':')[2].split('/')[0]
processed_exploit['paths'] = ['/' + url.split(':')[2].split('/')[1] for url in urls]
return processed_exploit
@staticmethod
def process_exploit(exploit):
exploiter_type = exploit['data']['exploiter']
if exploiter_type == 'SmbExploiter':
return ReportService.process_smb_exploit(exploit)
if exploiter_type == 'WmiExploiter':
return ReportService.process_wmi_exploit(exploit)
if exploiter_type == 'SSHExploiter':
return ReportService.process_ssh_exploit(exploit)
if exploiter_type == 'RdpExploiter':
return ReportService.process_rdp_exploit(exploit)
if exploiter_type == 'SambaCryExploiter':
return ReportService.process_sambacry_exploit(exploit)
if exploiter_type == 'ElasticGroovyExploiter':
return ReportService.process_elastic_exploit(exploit)
if exploiter_type == 'Ms08_067_Exploiter':
return ReportService.process_conficker_exploit(exploit)
if exploiter_type == 'ShellShockExploiter':
return ReportService.process_shellshock_exploit(exploit)
@staticmethod
def get_exploits():
return [ReportService.process_exploit(exploit) for
exploit in mongo.db.telemetry.find({'telem_type': 'exploit', 'data.result': True})]
@staticmethod
def get_monkey_subnets(monkey_guid):
return \
[
ipaddress.ip_interface(unicode(network['addr'] + '/' + network['netmask'])).network
for network in
mongo.db.telemetry.find_one(
{'telem_type': 'system_info_collection', 'monkey_guid': monkey_guid},
{'data.network_info.networks': 1}
)['data']['network_info']['networks']
]
@staticmethod
def get_cross_segment_issues():
issues = []
island_ips = local_ip_addresses()
for monkey in mongo.db.monkey.find({'tunnel': {'$exists': False}}, {'tunnel': 1, 'guid': 1, 'hostname': 1}):
found_good_ip = False
monkey_subnets = ReportService.get_monkey_subnets(monkey['guid'])
for subnet in monkey_subnets:
for ip in island_ips:
if ipaddress.ip_address(unicode(ip)) in subnet:
found_good_ip = True
break
if found_good_ip:
break
if not found_good_ip:
issues.append(
{'type': 'cross_segment', 'machine': monkey['hostname'],
'networks': [str(subnet) for subnet in monkey_subnets],
'server_networks': [str(subnet) for subnet in get_subnets()]}
)
return issues
@staticmethod
def get_issues():
return ReportService.get_exploits() + ReportService.get_tunnels() + ReportService.get_cross_segment_issues()
@staticmethod @staticmethod
def get_report(): def get_report():
return \ return \
@ -149,42 +269,9 @@ class ReportService:
}, },
'recommendations': 'recommendations':
{ {
'issues': 'issues': ReportService.get_issues()
[
{'type': 'smb_password', 'machine': 'Monkey-SMB',
'ip_addresses': ['192.168.0.1', '10.0.0.18'], 'username': 'Administrator'},
{'type': 'smb_pth', 'machine': 'Monkey-SMB2', 'ip_addresses': ['192.168.0.1', '10.0.0.18'],
'username': 'Administrator'},
{'type': 'wmi_password', 'machine': 'Monkey-WMI',
'ip_addresses': ['192.168.0.1', '10.0.0.18'], 'username': 'Administrator'},
{'type': 'wmi_pth', 'machine': 'Monkey-WMI2', 'ip_addresses': ['192.168.0.1', '10.0.0.18'],
'username': 'Administrator'},
{'type': 'ssh', 'machine': 'Monkey-SMB', 'ip_addresses': ['192.168.0.1', '10.0.0.18'],
'username': 'Administrator'},
{'type': 'rdp', 'machine': 'Monkey-SMB', 'ip_addresses': ['192.168.0.1', '10.0.0.18'],
'username': 'Administrator'},
{'type': 'sambacry', 'machine': 'Monkey-SMB', 'ip_addresses': ['192.168.0.1', '10.0.0.18'],
'username': 'Administrator'},
{'type': 'elastic', 'machine': 'Monkey-SMB', 'ip_addresses': ['192.168.0.1', '10.0.0.18']},
{'type': 'shellshock', 'machine': 'Monkey-SMB', 'ip_addresses': ['192.168.0.1', '10.0.0.18'],
'port': 8080, 'paths': ['/cgi/backserver.cgi', '/cgi/login.cgi']},
{'type': 'conficker', 'machine': 'Monkey-SMB', 'ip_addresses': ['192.168.0.1', '10.0.0.18']},
{'type': 'cross_segment', 'machine': 'Monkey-SMB', 'network': '192.168.0.0/24',
'server_network': '172.168.0.0/24'},
{'type': 'tunnel', 'origin': 'Monkey-SSH', 'dest': 'Monkey-SambaCry'}
]
} }
} }
# TODO: put implementation in template
"""
return \
{
'breach_count': ReportService.get_breach_count(),
'successful_exploit_types': ReportService.get_successful_exploit_types(),
'tunnels': ReportService.get_tunnels(),
'reused_passwords': ReportService.get_reused_passwords()
}
"""
@staticmethod @staticmethod
def did_exploit_type_succeed(exploit_type): def did_exploit_type_succeed(exploit_type):

View File

@ -65,8 +65,8 @@ class ReportPageComponent extends React.Component {
}); });
} }
generateIpListBadges(ip_addresses) { generateInfoBadges(data_array) {
return ip_addresses.map(ip_address => <span className="label label-info" style={{margin: '2px'}}>{ip_address}</span>); return data_array.map(badge_data => <span className="label label-info" style={{margin: '2px'}}>{badge_data}</span>);
} }
generateShellshockPathListBadges(paths) { generateShellshockPathListBadges(paths) {
@ -76,7 +76,7 @@ class ReportPageComponent extends React.Component {
generateSmbPasswordIssue(issue) { generateSmbPasswordIssue(issue) {
return ( return (
<div> <div>
The machine <span className="label label-primary">{issue.machine}</span> with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a <span className="label label-danger">SMB</span> attack. The machine <span className="label label-primary">{issue.machine}</span> with the following IP address <span className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span> was vulnerable to a <span className="label label-danger">SMB</span> attack.
<br /> <br />
The attack succeeded by authenticating over SMB protocol with user <span className="label label-success">{issue.username}</span> and its password. The attack succeeded by authenticating over SMB protocol with user <span className="label label-success">{issue.username}</span> and its password.
<br /> <br />
@ -91,7 +91,7 @@ class ReportPageComponent extends React.Component {
generateSmbPthIssue(issue) { generateSmbPthIssue(issue) {
return ( return (
<div> <div>
The machine <span className="label label-primary">{issue.machine}</span> with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a <span className="label label-danger">SMB</span> attack. The machine <span className="label label-primary">{issue.machine}</span> with the following IP address <span className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span> was vulnerable to a <span className="label label-danger">SMB</span> attack.
<br /> <br />
The attack succeeded by using a pass-the-hash attack over SMB protocol with user <span className="label label-success">{issue.username}</span>. The attack succeeded by using a pass-the-hash attack over SMB protocol with user <span className="label label-success">{issue.username}</span>.
<br /> <br />
@ -106,7 +106,7 @@ class ReportPageComponent extends React.Component {
generateWmiPasswordIssue(issue) { generateWmiPasswordIssue(issue) {
return ( return (
<div> <div>
The machine <span className="label label-primary">{issue.machine}</span> with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a <span className="label label-danger">WMI</span> attack. The machine <span className="label label-primary">{issue.machine}</span> with the following IP address <span className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span> was vulnerable to a <span className="label label-danger">WMI</span> attack.
<br /> <br />
The attack succeeded by authenticating over WMI protocol with user <span className="label label-success">{issue.username}</span> and its password. The attack succeeded by authenticating over WMI protocol with user <span className="label label-success">{issue.username}</span> and its password.
<br /> <br />
@ -121,7 +121,7 @@ class ReportPageComponent extends React.Component {
generateWmiPthIssue(issue) { generateWmiPthIssue(issue) {
return ( return (
<div> <div>
The machine <span className="label label-primary">{issue.machine}</span> with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a <span className="label label-danger">WMI</span> attack. The machine <span className="label label-primary">{issue.machine}</span> with the following IP address <span className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span> was vulnerable to a <span className="label label-danger">WMI</span> attack.
<br /> <br />
The attack succeeded by using a pass-the-hash attack over WMI protocol with user <span className="label label-success">{issue.username}</span>. The attack succeeded by using a pass-the-hash attack over WMI protocol with user <span className="label label-success">{issue.username}</span>.
<br /> <br />
@ -136,7 +136,7 @@ class ReportPageComponent extends React.Component {
generateSshIssue(issue) { generateSshIssue(issue) {
return ( return (
<div> <div>
The machine <span className="label label-primary">{issue.machine}</span> with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a <span className="label label-danger">SSH</span> attack. The machine <span className="label label-primary">{issue.machine}</span> with the following IP address <span className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span> was vulnerable to a <span className="label label-danger">SSH</span> attack.
<br /> <br />
The attack succeeded by authenticating over SSH protocol with user <span className="label label-success">{issue.username}</span> and its password. The attack succeeded by authenticating over SSH protocol with user <span className="label label-success">{issue.username}</span> and its password.
<br /> <br />
@ -151,7 +151,7 @@ class ReportPageComponent extends React.Component {
generateRdpIssue(issue) { generateRdpIssue(issue) {
return ( return (
<div> <div>
The machine <span className="label label-primary">{issue.machine}</span> with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a <span className="label label-danger">RDP</span> attack. The machine <span className="label label-primary">{issue.machine}</span> with the following IP address <span className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span> was vulnerable to a <span className="label label-danger">RDP</span> attack.
<br /> <br />
The attack succeeded by authenticating over RDP protocol with user <span className="label label-success">{issue.username}</span> and its password. The attack succeeded by authenticating over RDP protocol with user <span className="label label-success">{issue.username}</span> and its password.
<br /> <br />
@ -166,7 +166,7 @@ class ReportPageComponent extends React.Component {
generateSambaCryIssue(issue) { generateSambaCryIssue(issue) {
return ( return (
<div> <div>
The machine <span className="label label-primary">{issue.machine}</span> with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a <span className="label label-danger">SambaCry</span> attack. The machine <span className="label label-primary">{issue.machine}</span> with the following IP address <span className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span> was vulnerable to a <span className="label label-danger">SambaCry</span> attack.
<br /> <br />
The attack succeeded by authenticating over SMB protocol with user <span className="label label-success">{issue.username}</span> and its password, and by using the SambaCry vulnerability. The attack succeeded by authenticating over SMB protocol with user <span className="label label-success">{issue.username}</span> and its password, and by using the SambaCry vulnerability.
<br /> <br />
@ -182,7 +182,7 @@ class ReportPageComponent extends React.Component {
generateElasticIssue(issue) { generateElasticIssue(issue) {
return ( return (
<div> <div>
The machine <span className="label label-primary">{issue.machine}</span> with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to an <span className="label label-danger">Elastic Groovy</span> attack. The machine <span className="label label-primary">{issue.machine}</span> with the following IP address <span className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span> was vulnerable to an <span className="label label-danger">Elastic Groovy</span> attack.
<br /> <br />
The attack succeeded because the Elastic Search server was not parched against CVE-2015-1427. The attack succeeded because the Elastic Search server was not parched against CVE-2015-1427.
<br /> <br />
@ -197,7 +197,7 @@ class ReportPageComponent extends React.Component {
generateShellshockIssue(issue) { generateShellshockIssue(issue) {
return ( return (
<div> <div>
The machine <span className="label label-primary">{issue.machine}</span> with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a <span className="label label-danger">ShellShock</span> attack. The machine <span className="label label-primary">{issue.machine}</span> with the following IP address <span className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span> was vulnerable to a <span className="label label-danger">ShellShock</span> attack.
<br /> <br />
The attack succeeded because the HTTP server running on port <span className="label label-info">{issue.port}</span> was vulnerable to a shell injection attack on the paths: {this.generateShellshockPathListBadges(issue.paths)}. The attack succeeded because the HTTP server running on port <span className="label label-info">{issue.port}</span> was vulnerable to a shell injection attack on the paths: {this.generateShellshockPathListBadges(issue.paths)}.
<br /> <br />
@ -212,7 +212,7 @@ class ReportPageComponent extends React.Component {
generateConfickerIssue(issue) { generateConfickerIssue(issue) {
return ( return (
<div> <div>
The machine <span className="label label-primary">{issue.machine}</span> with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a <span className="label label-danger">Conficker</span> attack. The machine <span className="label label-primary">{issue.machine}</span> with the following address <span className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span> was vulnerable to a <span className="label label-danger">Conficker</span> attack.
<br /> <br />
The attack succeeded because the target machine uses an outdated and unpatched operating system vulnerable to Conficker. The attack succeeded because the target machine uses an outdated and unpatched operating system vulnerable to Conficker.
<br /> <br />
@ -227,7 +227,7 @@ class ReportPageComponent extends React.Component {
generateCrossSegmentIssue(issue) { generateCrossSegmentIssue(issue) {
return ( return (
<div> <div>
The network can probably be segmented. A monkey instance on <span className="label label-primary">{issue.machine}</span> in the <span className="label label-info">{issue.network}</span> network could directly access the Monkey Island C&C server in the <span className="label label-info">{issue.server_network}</span> network. The network can probably be segmented. A monkey instance on <span className="label label-primary">{issue.machine}</span> in the networks {this.generateInfoBadges(issue.networks)} could directly access the Monkey Island C&C server in the networks {this.generateInfoBadges(issue.server_networks)}.
<br /> <br />
In order to protect the network, the following steps should be performed: In order to protect the network, the following steps should be performed:
<ul className="report"> <ul className="report">

View File

@ -4,6 +4,7 @@ import sys
import array import array
import struct import struct
import ipaddress
from netifaces import interfaces, ifaddresses, AF_INET from netifaces import interfaces, ifaddresses, AF_INET
from cc.database import mongo from cc.database import mongo
@ -56,3 +57,11 @@ def local_ip_addresses():
addresses = ifaddresses(interface).get(AF_INET, []) addresses = ifaddresses(interface).get(AF_INET, [])
ip_list.extend([link['addr'] for link in addresses if link['addr'] != '127.0.0.1']) ip_list.extend([link['addr'] for link in addresses if link['addr'] != '127.0.0.1'])
return ip_list return ip_list
def get_subnets():
subnets = []
for interface in interfaces():
addresses = ifaddresses(interface).get(AF_INET, [])
subnets.extend([ipaddress.ip_interface(link['addr'] + '/' + link['netmask']).network for link in addresses if link['addr'] != '127.0.0.1'])
return subnets

View File

@ -10,3 +10,4 @@ Flask-Pymongo
Flask-Restful Flask-Restful
jsonschema jsonschema
netifaces netifaces
ipaddress