From 22105eabe3d76096c5b6f65f23dd53e7a4976683 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Sun, 15 Oct 2017 20:06:26 +0300
Subject: [PATCH 01/92] Add basic report logic
---
monkey_island/cc/app.py | 3 +-
monkey_island/cc/resources/report.py | 10 +++++++
monkey_island/cc/services/report.py | 41 ++++++++++++++++++++++++++++
3 files changed, 53 insertions(+), 1 deletion(-)
create mode 100644 monkey_island/cc/resources/report.py
create mode 100644 monkey_island/cc/services/report.py
diff --git a/monkey_island/cc/app.py b/monkey_island/cc/app.py
index 6cfea1502..70001521a 100644
--- a/monkey_island/cc/app.py
+++ b/monkey_island/cc/app.py
@@ -15,7 +15,7 @@ from cc.resources.monkey_download import MonkeyDownload
from cc.resources.netmap import NetMap
from cc.resources.edge import Edge
from cc.resources.node import Node
-
+from cc.resources.report import Report
from cc.resources.root import Root
from cc.services.config import ConfigService
@@ -88,5 +88,6 @@ def init_app(mongo_url):
api.add_resource(NetMap, '/api/netmap', '/api/netmap/')
api.add_resource(Edge, '/api/netmap/edge', '/api/netmap/edge/')
api.add_resource(Node, '/api/netmap/node', '/api/netmap/node/')
+ api.add_resource(Report, '/api/report', '/api/report/')
return app
diff --git a/monkey_island/cc/resources/report.py b/monkey_island/cc/resources/report.py
new file mode 100644
index 000000000..e967b207f
--- /dev/null
+++ b/monkey_island/cc/resources/report.py
@@ -0,0 +1,10 @@
+import flask_restful
+
+from cc.services.report import ReportService
+
+__author__ = "itay.mizeretz"
+
+
+class Report(flask_restful.Resource):
+ def get(self):
+ return ReportService.get_report()
diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py
new file mode 100644
index 000000000..4356c84fd
--- /dev/null
+++ b/monkey_island/cc/services/report.py
@@ -0,0 +1,41 @@
+from cc.database import mongo
+
+__author__ = "itay.mizeretz"
+
+
+class ReportService:
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def get_first_monkey_time():
+ return mongo.db.telemetry.find({}, {'timestamp': 1}).sort([('$natural', 1)]).limit(1)[0]['timestamp']
+
+ @staticmethod
+ def get_last_monkey_dead_time():
+ return mongo.db.telemetry.find({}, {'timestamp': 1}).sort([('$natural', -1)]).limit(1)[0]['timestamp']
+
+ @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
+ def get_report():
+ return \
+ {
+ 'first_monkey_time': ReportService.get_first_monkey_time(),
+ 'last_monkey_dead_time': ReportService.get_last_monkey_dead_time(),
+ 'breach_count': ReportService.get_breach_count(),
+ 'successful_exploit_types': ReportService.get_successful_exploit_types(),
+ }
+
+ @staticmethod
+ def did_exploit_type_succeed(exploit_type):
+ return mongo.db.edge.count(
+ {'exploits': {'$elemMatch': {'exploiter': exploit_type, 'result': True}}},
+ limit=1) > 0
From 739edeff2a95defdafed49c3bdd07dd4407d4e77 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Mon, 16 Oct 2017 10:40:07 +0300
Subject: [PATCH 02/92] Add option to debug server
---
monkey_island/cc/island_config.py | 3 ++-
monkey_island/cc/main.py | 20 ++++++++++++--------
2 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/monkey_island/cc/island_config.py b/monkey_island/cc/island_config.py
index c53d27004..0a8f33bac 100644
--- a/monkey_island/cc/island_config.py
+++ b/monkey_island/cc/island_config.py
@@ -1,4 +1,5 @@
__author__ = 'itay.mizeretz'
ISLAND_PORT = 5000
-DEFAULT_MONGO_URL = "mongodb://localhost:27017/monkeyisland"
\ No newline at end of file
+DEFAULT_MONGO_URL = "mongodb://localhost:27017/monkeyisland"
+DEBUG_SERVER = False
diff --git a/monkey_island/cc/main.py b/monkey_island/cc/main.py
index dd133cfd1..68fae4e72 100644
--- a/monkey_island/cc/main.py
+++ b/monkey_island/cc/main.py
@@ -9,7 +9,7 @@ if BASE_PATH not in sys.path:
from cc.app import init_app
from cc.utils import local_ip_addresses
-from cc.island_config import DEFAULT_MONGO_URL, ISLAND_PORT
+from cc.island_config import DEFAULT_MONGO_URL, ISLAND_PORT, DEBUG_SERVER
if __name__ == '__main__':
from tornado.wsgi import WSGIContainer
@@ -17,10 +17,14 @@ if __name__ == '__main__':
from tornado.ioloop import IOLoop
app = init_app(os.environ.get('MONGO_URL', DEFAULT_MONGO_URL))
- http_server = HTTPServer(WSGIContainer(app),
- ssl_options={'certfile': os.environ.get('SERVER_CRT', 'server.crt'),
- 'keyfile': os.environ.get('SERVER_KEY', 'server.key')})
- http_server.listen(ISLAND_PORT)
- print('Monkey Island C&C Server is running on https://{}:{}'.format(local_ip_addresses()[0], ISLAND_PORT))
- IOLoop.instance().start()
- # app.run(host='0.0.0.0', debug=True, ssl_context=('server.crt', 'server.key'))
+
+ if DEBUG_SERVER:
+ app.run(host='0.0.0.0', debug=True, ssl_context=('server.crt', 'server.key'))
+ else:
+ http_server = HTTPServer(WSGIContainer(app),
+ ssl_options={'certfile': os.environ.get('SERVER_CRT', 'server.crt'),
+ 'keyfile': os.environ.get('SERVER_KEY', 'server.key')})
+ http_server.listen(ISLAND_PORT)
+ print('Monkey Island C&C Server is running on https://{}:{}'.format(local_ip_addresses()[0], ISLAND_PORT))
+ IOLoop.instance().start()
+
From 04c79d1b3ca3762dfe4b90296a02a591ea704ba2 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 7 Nov 2017 10:30:28 +0200
Subject: [PATCH 03/92] Fix shellshock bug where service name wasn't available
---
chaos_monkey/exploit/shellshock.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/chaos_monkey/exploit/shellshock.py b/chaos_monkey/exploit/shellshock.py
index 97c950a18..bca03b6ea 100644
--- a/chaos_monkey/exploit/shellshock.py
+++ b/chaos_monkey/exploit/shellshock.py
@@ -38,8 +38,10 @@ class ShellShockExploiter(HostExploiter):
def exploit_host(self):
# start by picking ports
- candidate_services = {service: self.host.services[service] for service in self.host.services if
- self.host.services[service]['name'] == 'http'}
+ candidate_services = {
+ service: self.host.services[service] for service in self.host.services if
+ ('name' in self.host.services[service]) and (self.host.services[service]['name'] == 'http')
+ }
valid_ports = [(port, candidate_services['tcp-' + str(port)]['data'][1]) for port in self.HTTP if
'tcp-' + str(port) in candidate_services]
From e9b6b39a2177761756d96f828884aa962a459695 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 7 Nov 2017 13:17:02 +0200
Subject: [PATCH 04/92] Add tunnel info to report
---
monkey_island/cc/services/node.py | 4 ++++
monkey_island/cc/services/report.py | 8 ++++++++
2 files changed, 12 insertions(+)
diff --git a/monkey_island/cc/services/node.py b/monkey_island/cc/services/node.py
index 3acd66b75..af92eaa42 100644
--- a/monkey_island/cc/services/node.py
+++ b/monkey_island/cc/services/node.py
@@ -89,6 +89,10 @@ class NodeService:
return True
+ @staticmethod
+ def get_monkey_label_by_id(monkey_id):
+ return NodeService.get_monkey_label(NodeService.get_monkey_by_id(monkey_id))
+
@staticmethod
def get_monkey_label(monkey):
label = monkey["hostname"] + " : " + monkey["ip_addresses"][0]
diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py
index 4356c84fd..fd8bc3bdb 100644
--- a/monkey_island/cc/services/report.py
+++ b/monkey_island/cc/services/report.py
@@ -1,4 +1,5 @@
from cc.database import mongo
+from cc.services.node import NodeService
__author__ = "itay.mizeretz"
@@ -24,6 +25,12 @@ class ReportService:
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
+ def get_tunnels():
+ return [
+ (NodeService.get_monkey_label_by_id(tunnel['_id']), NodeService.get_monkey_label_by_id(tunnel['tunnel']))
+ for tunnel in mongo.db.monkey.find({'tunnel': {'$exists': True}}, {'tunnel': 1})]
+
@staticmethod
def get_report():
return \
@@ -32,6 +39,7 @@ class ReportService:
'last_monkey_dead_time': ReportService.get_last_monkey_dead_time(),
'breach_count': ReportService.get_breach_count(),
'successful_exploit_types': ReportService.get_successful_exploit_types(),
+ 'tunnels': ReportService.get_tunnels()
}
@staticmethod
From 8d9068fe40025f597c710e892bb69e0c0d994584 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 7 Nov 2017 14:52:13 +0200
Subject: [PATCH 05/92] Add known credentials to monkey documents
---
monkey_island/cc/resources/monkey.py | 3 +++
monkey_island/cc/resources/telemetry.py | 11 +++++++++++
monkey_island/cc/services/node.py | 15 +++++++++++++++
3 files changed, 29 insertions(+)
diff --git a/monkey_island/cc/resources/monkey.py b/monkey_island/cc/resources/monkey.py
index 2e2da8a5d..0c6a8ddf1 100644
--- a/monkey_island/cc/resources/monkey.py
+++ b/monkey_island/cc/resources/monkey.py
@@ -53,6 +53,7 @@ class Monkey(flask_restful.Resource):
def post(self, **kw):
monkey_json = json.loads(request.data)
+ monkey_json['creds'] = {}
if 'keepalive' in monkey_json:
monkey_json['keepalive'] = dateutil.parser.parse(monkey_json['keepalive'])
else:
@@ -119,6 +120,8 @@ class Monkey(flask_restful.Resource):
node_id = existing_node["_id"]
for edge in mongo.db.edge.find({"to": node_id}):
mongo.db.edge.update({"_id": edge["_id"]}, {"$set": {"to": new_monkey_id}})
+ for user in existing_node['creds']:
+ NodeService.add_credentials_to_monkey(new_monkey_id, user, existing_node['creds'][user])
mongo.db.node.remove({"_id": node_id})
return {"id": new_monkey_id}
diff --git a/monkey_island/cc/resources/telemetry.py b/monkey_island/cc/resources/telemetry.py
index 88b144333..6df6649fa 100644
--- a/monkey_island/cc/resources/telemetry.py
+++ b/monkey_island/cc/resources/telemetry.py
@@ -115,6 +115,15 @@ class Telemetry(flask_restful.Resource):
if new_exploit['result']:
EdgeService.set_edge_exploited(edge)
+ for attempt in telemetry_json['data']['attempts']:
+ if attempt['result']:
+ attempt.pop('result')
+ user = attempt.pop('user')
+ for field in ['password', 'lm_hash', 'ntlm_hash']:
+ if len(attempt[field]) == 0:
+ attempt.pop(field)
+ NodeService.add_credentials_to_node(edge['to'], user, attempt)
+
@staticmethod
def process_scan_telemetry(telemetry_json):
edge = Telemetry.get_edge_by_scan_or_exploit_telemetry(telemetry_json)
@@ -151,6 +160,8 @@ class Telemetry(flask_restful.Resource):
creds = telemetry_json['data']['credentials']
for user in creds:
ConfigService.creds_add_username(user)
+ NodeService.add_credentials_to_monkey(
+ NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])['_id'], user, creds[user])
if 'password' in creds[user]:
ConfigService.creds_add_password(creds[user]['password'])
if 'lm_hash' in creds[user]:
diff --git a/monkey_island/cc/services/node.py b/monkey_island/cc/services/node.py
index af92eaa42..5777bcc36 100644
--- a/monkey_island/cc/services/node.py
+++ b/monkey_island/cc/services/node.py
@@ -170,6 +170,7 @@ class NodeService:
{
"ip_addresses": [ip_address],
"exploited": False,
+ "creds": {},
"os":
{
"type": "unknown",
@@ -277,3 +278,17 @@ class NodeService:
@staticmethod
def is_any_monkey_exists():
return mongo.db.monkey.find_one({}) is not None
+
+ @staticmethod
+ def add_credentials_to_monkey(monkey_id, user, creds):
+ mongo.db.monkey.update(
+ {'_id': monkey_id},
+ {'$set': {'creds.' + user: creds}}
+ )
+
+ @staticmethod
+ def add_credentials_to_node(node_id, user, creds):
+ mongo.db.node.update(
+ {'_id': node_id},
+ {'$set': {'creds.' + user: creds}}
+ )
From 1ad37b1dade83b622ddeb862ee8f9a2fdc6692ff Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 7 Nov 2017 14:54:11 +0200
Subject: [PATCH 06/92] Fix bug where 'dead' property of monkey wasn't defined
---
monkey_island/cc/resources/monkey.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/monkey_island/cc/resources/monkey.py b/monkey_island/cc/resources/monkey.py
index 2e2da8a5d..c87adc245 100644
--- a/monkey_island/cc/resources/monkey.py
+++ b/monkey_island/cc/resources/monkey.py
@@ -53,6 +53,7 @@ class Monkey(flask_restful.Resource):
def post(self, **kw):
monkey_json = json.loads(request.data)
+ monkey_json['dead'] = False
if 'keepalive' in monkey_json:
monkey_json['keepalive'] = dateutil.parser.parse(monkey_json['keepalive'])
else:
From b284467fbcadeeec4bfe3d50936d98cd6ed42ce7 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 7 Nov 2017 16:33:26 +0200
Subject: [PATCH 07/92] Add scanned and exploited to report
---
monkey_island/cc/services/report.py | 38 ++++++++++++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py
index fd8bc3bdb..ca7a55234 100644
--- a/monkey_island/cc/services/report.py
+++ b/monkey_island/cc/services/report.py
@@ -31,6 +31,40 @@ class ReportService:
(NodeService.get_monkey_label_by_id(tunnel['_id']), NodeService.get_monkey_label_by_id(tunnel['tunnel']))
for tunnel in mongo.db.monkey.find({'tunnel': {'$exists': True}}, {'tunnel': 1})]
+ @staticmethod
+ def get_scanned():
+ nodes =\
+ [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})]
+ nodes = [
+ {
+ 'label': node['label'],
+ 'ip_addresses': node['ip_addresses'],
+ 'accessible_from_nodes': node['accessible_from_nodes'],
+ 'services': node['services']
+ }
+ for node in nodes]
+
+ return nodes
+
+ @staticmethod
+ def get_exploited():
+ exploited =\
+ [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']))]\
+ + [NodeService.get_displayed_node_by_id(node['_id'])
+ for node in mongo.db.node.find({'exploited': True}, {'_id': 1})]
+
+ exploited = [
+ {
+ 'label': monkey['label'],
+ 'ip_addresses': monkey['ip_addresses'],
+ 'exploits': [exploit['exploiter'] for exploit in monkey['exploits'] if exploit['result']]
+ }
+ for monkey in exploited]
+
+ return exploited
+
@staticmethod
def get_report():
return \
@@ -39,7 +73,9 @@ class ReportService:
'last_monkey_dead_time': ReportService.get_last_monkey_dead_time(),
'breach_count': ReportService.get_breach_count(),
'successful_exploit_types': ReportService.get_successful_exploit_types(),
- 'tunnels': ReportService.get_tunnels()
+ 'tunnels': ReportService.get_tunnels(),
+ 'scanned': ReportService.get_scanned(),
+ 'exploited': ReportService.get_exploited()
}
@staticmethod
From be8d20b2f5559f3bf7cb12ba8b02f664d2cfdd0a Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 7 Nov 2017 17:02:45 +0200
Subject: [PATCH 08/92] Change creds format in monkey document
---
monkey_island/cc/resources/monkey.py | 6 +++---
monkey_island/cc/resources/telemetry.py | 6 +++---
monkey_island/cc/services/node.py | 10 +++++-----
3 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/monkey_island/cc/resources/monkey.py b/monkey_island/cc/resources/monkey.py
index 0c6a8ddf1..93fb0a0be 100644
--- a/monkey_island/cc/resources/monkey.py
+++ b/monkey_island/cc/resources/monkey.py
@@ -53,7 +53,7 @@ class Monkey(flask_restful.Resource):
def post(self, **kw):
monkey_json = json.loads(request.data)
- monkey_json['creds'] = {}
+ monkey_json['creds'] = []
if 'keepalive' in monkey_json:
monkey_json['keepalive'] = dateutil.parser.parse(monkey_json['keepalive'])
else:
@@ -120,8 +120,8 @@ class Monkey(flask_restful.Resource):
node_id = existing_node["_id"]
for edge in mongo.db.edge.find({"to": node_id}):
mongo.db.edge.update({"_id": edge["_id"]}, {"$set": {"to": new_monkey_id}})
- for user in existing_node['creds']:
- NodeService.add_credentials_to_monkey(new_monkey_id, user, existing_node['creds'][user])
+ for creds in existing_node['creds']:
+ NodeService.add_credentials_to_monkey(new_monkey_id, creds)
mongo.db.node.remove({"_id": node_id})
return {"id": new_monkey_id}
diff --git a/monkey_island/cc/resources/telemetry.py b/monkey_island/cc/resources/telemetry.py
index 6df6649fa..8698cd45f 100644
--- a/monkey_island/cc/resources/telemetry.py
+++ b/monkey_island/cc/resources/telemetry.py
@@ -118,11 +118,10 @@ class Telemetry(flask_restful.Resource):
for attempt in telemetry_json['data']['attempts']:
if attempt['result']:
attempt.pop('result')
- user = attempt.pop('user')
for field in ['password', 'lm_hash', 'ntlm_hash']:
if len(attempt[field]) == 0:
attempt.pop(field)
- NodeService.add_credentials_to_node(edge['to'], user, attempt)
+ NodeService.add_credentials_to_node(edge['to'], attempt)
@staticmethod
def process_scan_telemetry(telemetry_json):
@@ -160,8 +159,9 @@ class Telemetry(flask_restful.Resource):
creds = telemetry_json['data']['credentials']
for user in creds:
ConfigService.creds_add_username(user)
+ creds[user]['user'] = user
NodeService.add_credentials_to_monkey(
- NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])['_id'], user, creds[user])
+ NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])['_id'], creds[user])
if 'password' in creds[user]:
ConfigService.creds_add_password(creds[user]['password'])
if 'lm_hash' in creds[user]:
diff --git a/monkey_island/cc/services/node.py b/monkey_island/cc/services/node.py
index 5777bcc36..dc30d60d5 100644
--- a/monkey_island/cc/services/node.py
+++ b/monkey_island/cc/services/node.py
@@ -170,7 +170,7 @@ class NodeService:
{
"ip_addresses": [ip_address],
"exploited": False,
- "creds": {},
+ "creds": [],
"os":
{
"type": "unknown",
@@ -280,15 +280,15 @@ class NodeService:
return mongo.db.monkey.find_one({}) is not None
@staticmethod
- def add_credentials_to_monkey(monkey_id, user, creds):
+ def add_credentials_to_monkey(monkey_id, creds):
mongo.db.monkey.update(
{'_id': monkey_id},
- {'$set': {'creds.' + user: creds}}
+ {'$push': {'creds': creds}}
)
@staticmethod
- def add_credentials_to_node(node_id, user, creds):
+ def add_credentials_to_node(node_id, creds):
mongo.db.node.update(
{'_id': node_id},
- {'$set': {'creds.' + user: creds}}
+ {'$push': {'creds': creds}}
)
From 0bc666824288e4cdfcaa859f54df479268e3fb75 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sat, 11 Nov 2017 20:32:12 +0200
Subject: [PATCH 09/92] 0001-Rename-to-check tcp-udp port-and-refactor
---
chaos_monkey/exploit/rdpgrinder.py | 6 +++---
chaos_monkey/exploit/smbexec.py | 6 +++---
chaos_monkey/exploit/sshexec.py | 4 ++--
chaos_monkey/exploit/win_ms08_067.py | 4 ++--
chaos_monkey/network/sshfinger.py | 7 ++++---
chaos_monkey/network/tcp_scanner.py | 7 ++++---
chaos_monkey/network/tools.py | 20 ++++++++++----------
chaos_monkey/tunnel.py | 4 ++--
8 files changed, 30 insertions(+), 28 deletions(-)
diff --git a/chaos_monkey/exploit/rdpgrinder.py b/chaos_monkey/exploit/rdpgrinder.py
index 207564778..606f44f90 100644
--- a/chaos_monkey/exploit/rdpgrinder.py
+++ b/chaos_monkey/exploit/rdpgrinder.py
@@ -13,7 +13,7 @@ from exploit import HostExploiter
from exploit.tools import HTTPTools, get_monkey_depth
from exploit.tools import get_target_monkey
from model import RDP_CMDLINE_HTTP_BITS, RDP_CMDLINE_HTTP_VBS
-from network.tools import check_port_tcp
+from network.tools import check_tcp_port
from tools import build_monkey_commandline
__author__ = 'hoffer'
@@ -245,7 +245,7 @@ class RdpExploiter(HostExploiter):
return True
if not self.host.os.get('type'):
- is_open, _ = check_port_tcp(self.host.ip_addr, RDP_PORT)
+ is_open, _ = check_tcp_port(self.host.ip_addr, RDP_PORT)
if is_open:
self.host.os['type'] = 'windows'
return True
@@ -254,7 +254,7 @@ class RdpExploiter(HostExploiter):
def exploit_host(self):
global g_reactor
- is_open, _ = check_port_tcp(self.host.ip_addr, RDP_PORT)
+ is_open, _ = check_tcp_port(self.host.ip_addr, RDP_PORT)
if not is_open:
LOG.info("RDP port is closed on %r, skipping", self.host)
return False
diff --git a/chaos_monkey/exploit/smbexec.py b/chaos_monkey/exploit/smbexec.py
index f5fa2b26b..b76a7bce6 100644
--- a/chaos_monkey/exploit/smbexec.py
+++ b/chaos_monkey/exploit/smbexec.py
@@ -7,7 +7,7 @@ from exploit import HostExploiter
from exploit.tools import SmbTools, get_target_monkey, get_monkey_depth
from model import MONKEY_CMDLINE_DETACHED_WINDOWS, DROPPER_CMDLINE_DETACHED_WINDOWS
from network import SMBFinger
-from network.tools import check_port_tcp
+from network.tools import check_tcp_port
from tools import build_monkey_commandline
LOG = getLogger(__name__)
@@ -31,12 +31,12 @@ class SmbExploiter(HostExploiter):
return True
if not self.host.os.get('type'):
- is_smb_open, _ = check_port_tcp(self.host.ip_addr, 445)
+ is_smb_open, _ = check_tcp_port(self.host.ip_addr, 445)
if is_smb_open:
smb_finger = SMBFinger()
smb_finger.get_host_fingerprint(self.host)
else:
- is_nb_open, _ = check_port_tcp(self.host.ip_addr, 139)
+ is_nb_open, _ = check_tcp_port(self.host.ip_addr, 139)
if is_nb_open:
self.host.os['type'] = 'windows'
return self.host.os.get('type') in self._TARGET_OS_TYPE
diff --git a/chaos_monkey/exploit/sshexec.py b/chaos_monkey/exploit/sshexec.py
index f58e5677b..b93970ca9 100644
--- a/chaos_monkey/exploit/sshexec.py
+++ b/chaos_monkey/exploit/sshexec.py
@@ -7,7 +7,7 @@ import monkeyfs
from exploit import HostExploiter
from exploit.tools import get_target_monkey, get_monkey_depth
from model import MONKEY_ARG
-from network.tools import check_port_tcp
+from network.tools import check_tcp_port
from tools import build_monkey_commandline
__author__ = 'hoffer'
@@ -41,7 +41,7 @@ class SSHExploiter(HostExploiter):
if servdata.get('name') == 'ssh' and servkey.startswith('tcp-'):
port = int(servkey.replace('tcp-', ''))
- is_open, _ = check_port_tcp(self.host.ip_addr, port)
+ is_open, _ = check_tcp_port(self.host.ip_addr, port)
if not is_open:
LOG.info("SSH port is closed on %r, skipping", self.host)
return False
diff --git a/chaos_monkey/exploit/win_ms08_067.py b/chaos_monkey/exploit/win_ms08_067.py
index 3ed553931..51393ea69 100644
--- a/chaos_monkey/exploit/win_ms08_067.py
+++ b/chaos_monkey/exploit/win_ms08_067.py
@@ -17,7 +17,7 @@ from impacket.dcerpc.v5 import transport
from exploit.tools import SmbTools, get_target_monkey, get_monkey_depth
from model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS
from network import SMBFinger
-from network.tools import check_port_tcp
+from network.tools import check_tcp_port
from tools import build_monkey_commandline
from . import HostExploiter
@@ -168,7 +168,7 @@ class Ms08_067_Exploiter(HostExploiter):
if not self.host.os.get('type') or (
self.host.os.get('type') in self._TARGET_OS_TYPE and not self.host.os.get('version')):
- is_smb_open, _ = check_port_tcp(self.host.ip_addr, 445)
+ is_smb_open, _ = check_tcp_port(self.host.ip_addr, 445)
if is_smb_open:
smb_finger = SMBFinger()
if smb_finger.get_host_fingerprint(self.host):
diff --git a/chaos_monkey/network/sshfinger.py b/chaos_monkey/network/sshfinger.py
index 75a3380ca..89c3092d7 100644
--- a/chaos_monkey/network/sshfinger.py
+++ b/chaos_monkey/network/sshfinger.py
@@ -1,7 +1,8 @@
import re
-from network import HostFinger
-from network.tools import check_port_tcp
+
from model.host import VictimHost
+from network import HostFinger
+from network.tools import check_tcp_port
SSH_PORT = 22
SSH_SERVICE_DEFAULT = 'tcp-22'
@@ -38,7 +39,7 @@ class SSHFinger(HostFinger):
self._banner_match(name, host, banner)
return
- is_open, banner = check_port_tcp(host.ip_addr, SSH_PORT, TIMEOUT, True)
+ is_open, banner = check_tcp_port(host.ip_addr, SSH_PORT, TIMEOUT, True)
if is_open:
host.services[SSH_SERVICE_DEFAULT] = {}
diff --git a/chaos_monkey/network/tcp_scanner.py b/chaos_monkey/network/tcp_scanner.py
index 8ce715f7f..4a6b8c40e 100644
--- a/chaos_monkey/network/tcp_scanner.py
+++ b/chaos_monkey/network/tcp_scanner.py
@@ -1,8 +1,9 @@
import time
from random import shuffle
-from network import HostScanner, HostFinger
+
from model.host import VictimHost
-from network.tools import check_port_tcp
+from network import HostScanner, HostFinger
+from network.tools import check_tcp_port
__author__ = 'itamar'
@@ -26,7 +27,7 @@ class TcpScanner(HostScanner, HostFinger):
for target_port in target_ports:
- is_open, banner = check_port_tcp(host.ip_addr,
+ is_open, banner = check_tcp_port(host.ip_addr,
target_port,
self._config.tcp_scan_timeout / 1000.0,
self._config.tcp_scan_get_banner)
diff --git a/chaos_monkey/network/tools.py b/chaos_monkey/network/tools.py
index 66f4eef57..b89002802 100644
--- a/chaos_monkey/network/tools.py
+++ b/chaos_monkey/network/tools.py
@@ -1,6 +1,6 @@
-import socket
-import select
import logging
+import select
+import socket
import struct
DEFAULT_TIMEOUT = 10
@@ -32,10 +32,10 @@ def struct_unpack_tracker_string(data, index):
"""
ascii_len = data[index:].find('\0')
fmt = "%ds" % ascii_len
- return struct_unpack_tracker(data,index,fmt)
+ return struct_unpack_tracker(data, index, fmt)
-def check_port_tcp(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
+def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
@@ -43,7 +43,7 @@ def check_port_tcp(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
sock.connect((ip, port))
except socket.timeout:
return False, None
- except socket.error, exc:
+ except socket.error as exc:
LOG.debug("Check port: %s:%s, Exception: %s", ip, port, exc)
return False, None
@@ -56,23 +56,23 @@ def check_port_tcp(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
banner = sock.recv(BANNER_READ)
except:
pass
-
+
sock.close()
return True, banner
-def check_port_udp(ip, port, timeout=DEFAULT_TIMEOUT):
+def check_udp_port(ip, port, timeout=DEFAULT_TIMEOUT):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(timeout)
-
+
data = None
is_open = False
-
+
try:
sock.sendto("-", (ip, port))
data, _ = sock.recvfrom(BANNER_READ)
is_open = True
- except:
+ except socket.error:
pass
sock.close()
diff --git a/chaos_monkey/tunnel.py b/chaos_monkey/tunnel.py
index 7f7edec03..9a50679ff 100644
--- a/chaos_monkey/tunnel.py
+++ b/chaos_monkey/tunnel.py
@@ -8,7 +8,7 @@ from threading import Thread
from model import VictimHost
from network.firewall import app as firewall
from network.info import local_ips, get_free_tcp_port
-from network.tools import check_port_tcp
+from network.tools import check_tcp_port
from transport.base import get_last_serve_time
__author__ = 'hoffer'
@@ -40,7 +40,7 @@ def _check_tunnel(address, port, existing_sock=None):
sock = existing_sock
LOG.debug("Checking tunnel %s:%s", address, port)
- is_open, _ = check_port_tcp(address, int(port))
+ is_open, _ = check_tcp_port(address, int(port))
if not is_open:
LOG.debug("Could not connect to %s:%s", address, port)
if not existing_sock:
From b9809f1e1f4b484c5d75d345c404eec1baa2e031 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sat, 11 Nov 2017 20:36:56 +0200
Subject: [PATCH 10/92] Move tcp scanner to use new check_tcp_pors
---
chaos_monkey/network/tcp_scanner.py | 28 +++----
chaos_monkey/network/tools.py | 118 +++++++++++++++++++++++++++-
2 files changed, 128 insertions(+), 18 deletions(-)
diff --git a/chaos_monkey/network/tcp_scanner.py b/chaos_monkey/network/tcp_scanner.py
index 4a6b8c40e..e149da8e7 100644
--- a/chaos_monkey/network/tcp_scanner.py
+++ b/chaos_monkey/network/tcp_scanner.py
@@ -1,9 +1,7 @@
-import time
from random import shuffle
-from model.host import VictimHost
from network import HostScanner, HostFinger
-from network.tools import check_tcp_port
+from network.tools import check_tcp_ports
__author__ = 'itamar'
@@ -18,29 +16,25 @@ class TcpScanner(HostScanner, HostFinger):
return self.get_host_fingerprint(host, True)
def get_host_fingerprint(self, host, only_one_port=False):
- assert isinstance(host, VictimHost)
+ """
+ Scans a target host to see if it's alive using the tcp_target_ports specified in the configuration.
+ :param host: VictimHost structure
+ :param only_one_port: Currently unused.
+ :return: T/F if there is at least one open port. In addition, the host object is updated to mark those services as alive.
+ """
- count = 0
# maybe hide under really bad detection systems
target_ports = self._config.tcp_target_ports[:]
shuffle(target_ports)
- for target_port in target_ports:
-
- is_open, banner = check_tcp_port(host.ip_addr,
- target_port,
- self._config.tcp_scan_timeout / 1000.0,
- self._config.tcp_scan_get_banner)
-
- if is_open:
- count += 1
+ ports, banners = check_tcp_ports(host.ip_addr, target_ports, self._config.tcp_scan_timeout / 1000.0)
+ if len(ports) != 0:
+ for target_port, banner in zip(ports, banners):
service = 'tcp-' + str(target_port)
host.services[service] = {}
if banner:
host.services[service]['banner'] = banner
if only_one_port:
break
- else:
- time.sleep(self._config.tcp_scan_interval / 1000.0)
- return count != 0
+ return len(ports) != 0
diff --git a/chaos_monkey/network/tools.py b/chaos_monkey/network/tools.py
index b89002802..6aea850b5 100644
--- a/chaos_monkey/network/tools.py
+++ b/chaos_monkey/network/tools.py
@@ -2,6 +2,7 @@ import logging
import select
import socket
import struct
+import time
DEFAULT_TIMEOUT = 10
BANNER_READ = 1024
@@ -36,6 +37,14 @@ def struct_unpack_tracker_string(data, index):
def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
+ """
+ Checks if a given TCP port is open
+ :param ip: Target IP
+ :param port: Target Port
+ :param timeout: Timeout for socket connection
+ :param get_banner: if true, pulls first BANNER_READ bytes from the socket.
+ :return: Tuple, T/F + banner if requested.
+ """
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
@@ -54,7 +63,7 @@ def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
read_ready, _, _ = select.select([sock], [], [], timeout)
if len(read_ready) > 0:
banner = sock.recv(BANNER_READ)
- except:
+ except socket.error:
pass
sock.close()
@@ -62,6 +71,13 @@ def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
def check_udp_port(ip, port, timeout=DEFAULT_TIMEOUT):
+ """
+ Checks if a given UDP port is open by checking if it replies to an empty message
+ :param ip: Target IP
+ :param port: Target port
+ :param timeout: Timeout to wait
+ :return: Tuple, T/F + banner
+ """
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(timeout)
@@ -77,3 +93,103 @@ def check_udp_port(ip, port, timeout=DEFAULT_TIMEOUT):
sock.close()
return is_open, data
+
+
+def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False):
+ """
+ Checks whether any of the given ports are open on a target IP.
+ :param ip: IP of host to attack
+ :param ports: List of ports to attack. Must not be empty.
+ :param timeout: Amount of time to wait for connection
+ :param get_banner: T/F if to get first packets from server
+ :return: list of open ports. If get_banner=True, then a matching list of banners.
+ """
+ sockets = [socket.socket(socket.AF_INET, socket.SOCK_STREAM) for _ in range(len(ports))]
+ [s.setblocking(0) for s in sockets]
+ good_ports = []
+ try:
+ for sock, port in zip(sockets, ports):
+ LOG.debug("Connecting to port %d" % port)
+ err = sock.connect_ex((ip, port))
+ if err == 0:
+ good_ports.append((port, sock))
+ if err == 10035: # WSAEWOULDBLOCK is valid, see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
+ good_ports.append((port, sock))
+
+ if len(good_ports) != 0:
+ time.sleep(timeout)
+ read_sockets, write_sockets, errored_sockets = \
+ select.select(
+ [s[1] for s in good_ports],
+ [s[1] for s in good_ports],
+ [s[1] for s in good_ports],
+ 0) # no timeout because we've already slept
+ connected_ports_sockets = [x for x in good_ports if x[1] in write_sockets]
+ LOG.debug(
+ "On host %s discovered the following ports %s" %
+ (str(ip), ",".join([str(x) for x in connected_ports_sockets])))
+ banners = []
+ if get_banner:
+ # read first X bytes
+ banners = [sock.recv(BANNER_READ) if sock in read_sockets else ""
+ for port, sock in connected_ports_sockets]
+ pass
+ # try to cleanup
+ [s[1].close() for s in good_ports]
+ return [port for port, sock in connected_ports_sockets], banners
+ else:
+ return [], []
+
+ except socket.error as exc:
+ LOG.warning("Exception when checking ports on host %s, Exception: %s", str(ip), exc)
+ return [], []
+
+
+def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False):
+ """
+ Checks whether any of the given ports are open on a target IP.
+ :param ip: IP of host to attack
+ :param ports: List of ports to attack. Must not be empty.
+ :param timeout: Amount of time to wait for connection
+ :param get_banner: T/F if to get first packets from server
+ :return: list of open ports. If get_banner=True, then a matching list of banners.
+ """
+ sockets = [socket.socket(socket.AF_INET, socket.SOCK_STREAM) for _ in range(len(ports))]
+ [s.setblocking(0) for s in sockets]
+ good_ports = []
+ try:
+ for sock, port in zip(sockets, ports):
+ LOG.debug("Connecting to port %d" % port)
+ err = sock.connect_ex((ip, port))
+ if err == 0:
+ good_ports.append((port, sock))
+ if err == 10035: # WSAEWOULDBLOCK is valid, see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
+ good_ports.append((port, sock))
+
+ if len(good_ports) != 0:
+ time.sleep(timeout)
+ read_sockets, write_sockets, errored_sockets = \
+ select.select(
+ [s[1] for s in good_ports],
+ [s[1] for s in good_ports],
+ [s[1] for s in good_ports],
+ 0) # no timeout because we've already slept
+ connected_ports_sockets = [x for x in good_ports if x[1] in write_sockets]
+ LOG.debug(
+ "On host %s discovered the following ports %s" %
+ (str(ip), ",".join([str(x) for x in connected_ports_sockets])))
+ banners = []
+ if get_banner:
+ # read first X bytes
+ banners = [sock.recv(BANNER_READ) if sock in read_sockets else ""
+ for port, sock in connected_ports_sockets]
+ pass
+ # try to cleanup
+ [s[1].close() for s in good_ports]
+ return [port for port, sock in connected_ports_sockets], banners
+ else:
+ return [], []
+
+ except socket.error as exc:
+ LOG.warning("Exception when checking ports on host %s, Exception: %s", str(ip), exc)
+ return [], []
From 2b17eca6149449a0e8357bdf89d9bc9589818185 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sat, 11 Nov 2017 20:36:56 +0200
Subject: [PATCH 11/92] Move tcp scanner to use new check_tcp_pors
---
chaos_monkey/network/tcp_scanner.py | 28 +++++-------
chaos_monkey/network/tools.py | 68 ++++++++++++++++++++++++++++-
2 files changed, 78 insertions(+), 18 deletions(-)
diff --git a/chaos_monkey/network/tcp_scanner.py b/chaos_monkey/network/tcp_scanner.py
index 4a6b8c40e..e149da8e7 100644
--- a/chaos_monkey/network/tcp_scanner.py
+++ b/chaos_monkey/network/tcp_scanner.py
@@ -1,9 +1,7 @@
-import time
from random import shuffle
-from model.host import VictimHost
from network import HostScanner, HostFinger
-from network.tools import check_tcp_port
+from network.tools import check_tcp_ports
__author__ = 'itamar'
@@ -18,29 +16,25 @@ class TcpScanner(HostScanner, HostFinger):
return self.get_host_fingerprint(host, True)
def get_host_fingerprint(self, host, only_one_port=False):
- assert isinstance(host, VictimHost)
+ """
+ Scans a target host to see if it's alive using the tcp_target_ports specified in the configuration.
+ :param host: VictimHost structure
+ :param only_one_port: Currently unused.
+ :return: T/F if there is at least one open port. In addition, the host object is updated to mark those services as alive.
+ """
- count = 0
# maybe hide under really bad detection systems
target_ports = self._config.tcp_target_ports[:]
shuffle(target_ports)
- for target_port in target_ports:
-
- is_open, banner = check_tcp_port(host.ip_addr,
- target_port,
- self._config.tcp_scan_timeout / 1000.0,
- self._config.tcp_scan_get_banner)
-
- if is_open:
- count += 1
+ ports, banners = check_tcp_ports(host.ip_addr, target_ports, self._config.tcp_scan_timeout / 1000.0)
+ if len(ports) != 0:
+ for target_port, banner in zip(ports, banners):
service = 'tcp-' + str(target_port)
host.services[service] = {}
if banner:
host.services[service]['banner'] = banner
if only_one_port:
break
- else:
- time.sleep(self._config.tcp_scan_interval / 1000.0)
- return count != 0
+ return len(ports) != 0
diff --git a/chaos_monkey/network/tools.py b/chaos_monkey/network/tools.py
index b89002802..ed0c1d4ca 100644
--- a/chaos_monkey/network/tools.py
+++ b/chaos_monkey/network/tools.py
@@ -2,6 +2,7 @@ import logging
import select
import socket
import struct
+import time
DEFAULT_TIMEOUT = 10
BANNER_READ = 1024
@@ -36,6 +37,14 @@ def struct_unpack_tracker_string(data, index):
def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
+ """
+ Checks if a given TCP port is open
+ :param ip: Target IP
+ :param port: Target Port
+ :param timeout: Timeout for socket connection
+ :param get_banner: if true, pulls first BANNER_READ bytes from the socket.
+ :return: Tuple, T/F + banner if requested.
+ """
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
@@ -54,7 +63,7 @@ def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
read_ready, _, _ = select.select([sock], [], [], timeout)
if len(read_ready) > 0:
banner = sock.recv(BANNER_READ)
- except:
+ except socket.error:
pass
sock.close()
@@ -62,6 +71,13 @@ def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False):
def check_udp_port(ip, port, timeout=DEFAULT_TIMEOUT):
+ """
+ Checks if a given UDP port is open by checking if it replies to an empty message
+ :param ip: Target IP
+ :param port: Target port
+ :param timeout: Timeout to wait
+ :return: Tuple, T/F + banner
+ """
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(timeout)
@@ -77,3 +93,53 @@ def check_udp_port(ip, port, timeout=DEFAULT_TIMEOUT):
sock.close()
return is_open, data
+
+
+def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False):
+ """
+ Checks whether any of the given ports are open on a target IP.
+ :param ip: IP of host to attack
+ :param ports: List of ports to attack. Must not be empty.
+ :param timeout: Amount of time to wait for connection
+ :param get_banner: T/F if to get first packets from server
+ :return: list of open ports. If get_banner=True, then a matching list of banners.
+ """
+ sockets = [socket.socket(socket.AF_INET, socket.SOCK_STREAM) for _ in range(len(ports))]
+ [s.setblocking(0) for s in sockets]
+ good_ports = []
+ try:
+ for sock, port in zip(sockets, ports):
+ LOG.debug("Connecting to port %d" % port)
+ err = sock.connect_ex((ip, port))
+ if err == 0:
+ good_ports.append((port, sock))
+ if err == 10035: # WSAEWOULDBLOCK is valid, see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
+ good_ports.append((port, sock))
+
+ if len(good_ports) != 0:
+ time.sleep(timeout)
+ read_sockets, write_sockets, _ = \
+ select.select(
+ [s[1] for s in good_ports],
+ [s[1] for s in good_ports],
+ [s[1] for s in good_ports],
+ 0) # no timeout because we've already slept
+ connected_ports_sockets = [x for x in good_ports if x[1] in write_sockets]
+ LOG.debug(
+ "On host %s discovered the following ports %s" %
+ (str(ip), ",".join([str(x) for x in connected_ports_sockets])))
+ banners = []
+ if get_banner:
+ # read first X bytes
+ banners = [sock.recv(BANNER_READ) if sock in read_sockets else ""
+ for port, sock in connected_ports_sockets]
+ pass
+ # try to cleanup
+ [s[1].close() for s in good_ports]
+ return [port for port, sock in connected_ports_sockets], banners
+ else:
+ return [], []
+
+ except socket.error as exc:
+ LOG.warning("Exception when checking ports on host %s, Exception: %s", str(ip), exc)
+ return [], []
From be8feeb3ee124083895b2bb57c4b54711c2ca9b4 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Sun, 12 Nov 2017 16:11:12 +0200
Subject: [PATCH 12/92] Add get config value function
---
monkey_island/cc/services/config.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py
index 200e24029..2145011c3 100644
--- a/monkey_island/cc/services/config.py
+++ b/monkey_island/cc/services/config.py
@@ -806,6 +806,14 @@ class ConfigService:
config.pop(field, None)
return config
+ @staticmethod
+ def get_config_value(config_key_as_arr):
+ config_key = reduce(lambda x, y: x+'.'+y, config_key_as_arr)
+ config = mongo.db.config.find_one({'name': 'newconfig'}, {config_key: 1})
+ for config_key_part in config_key_as_arr:
+ config = config[config_key_part]
+ return config
+
@staticmethod
def get_flat_config():
config_json = ConfigService.get_config()
From 80b709b2ac8805a8fbc640725f118b6a9d7747d6 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Sun, 12 Nov 2017 16:13:40 +0200
Subject: [PATCH 13/92] Add reused passwords
---
monkey_island/cc/services/report.py | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py
index ca7a55234..0e0a2779f 100644
--- a/monkey_island/cc/services/report.py
+++ b/monkey_island/cc/services/report.py
@@ -1,4 +1,5 @@
from cc.database import mongo
+from cc.services.config import ConfigService
from cc.services.node import NodeService
__author__ = "itay.mizeretz"
@@ -47,6 +48,17 @@ class ReportService:
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
def get_exploited():
exploited =\
@@ -75,7 +87,8 @@ class ReportService:
'successful_exploit_types': ReportService.get_successful_exploit_types(),
'tunnels': ReportService.get_tunnels(),
'scanned': ReportService.get_scanned(),
- 'exploited': ReportService.get_exploited()
+ 'exploited': ReportService.get_exploited(),
+ 'reused_passwords': ReportService.get_reused_passwords()
}
@staticmethod
From 545b49919d483cec0f4444dcca8d62eb855ce1b5 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Sun, 12 Nov 2017 16:20:15 +0200
Subject: [PATCH 14/92] Remove mimikatz's stolen credentials from machine's
list of stolen creds
---
monkey_island/cc/resources/telemetry.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/monkey_island/cc/resources/telemetry.py b/monkey_island/cc/resources/telemetry.py
index 8698cd45f..666bfc16c 100644
--- a/monkey_island/cc/resources/telemetry.py
+++ b/monkey_island/cc/resources/telemetry.py
@@ -160,8 +160,6 @@ class Telemetry(flask_restful.Resource):
for user in creds:
ConfigService.creds_add_username(user)
creds[user]['user'] = user
- NodeService.add_credentials_to_monkey(
- NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])['_id'], creds[user])
if 'password' in creds[user]:
ConfigService.creds_add_password(creds[user]['password'])
if 'lm_hash' in creds[user]:
From 7a523bdd75b942b0449028c767681b045029f343 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sun, 12 Nov 2017 18:06:13 +0200
Subject: [PATCH 15/92] Oppertunistic wait
---
chaos_monkey/network/tools.py | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/chaos_monkey/network/tools.py b/chaos_monkey/network/tools.py
index ed0c1d4ca..706dcc48d 100644
--- a/chaos_monkey/network/tools.py
+++ b/chaos_monkey/network/tools.py
@@ -117,13 +117,21 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False):
good_ports.append((port, sock))
if len(good_ports) != 0:
- time.sleep(timeout)
read_sockets, write_sockets, _ = \
select.select(
[s[1] for s in good_ports],
[s[1] for s in good_ports],
[s[1] for s in good_ports],
- 0) # no timeout because we've already slept
+ timeout) # wait max timeout
+ if len(write_sockets) != len(good_ports):
+ time.sleep(timeout)
+ read_sockets, write_sockets, _ = \
+ select.select(
+ [s[1] for s in good_ports],
+ [s[1] for s in good_ports],
+ [s[1] for s in good_ports],
+ 0) # no timeout because we've already slept
+
connected_ports_sockets = [x for x in good_ports if x[1] in write_sockets]
LOG.debug(
"On host %s discovered the following ports %s" %
From fddda34dcd7800eaf2b18d317408d5d49c2fecc0 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sun, 12 Nov 2017 19:04:54 +0200
Subject: [PATCH 16/92] Worst case is now timeout rather than every case
---
chaos_monkey/network/tools.py | 48 ++++++++++++++++++++---------------
1 file changed, 27 insertions(+), 21 deletions(-)
diff --git a/chaos_monkey/network/tools.py b/chaos_monkey/network/tools.py
index 706dcc48d..4ce8fed2e 100644
--- a/chaos_monkey/network/tools.py
+++ b/chaos_monkey/network/tools.py
@@ -5,6 +5,7 @@ import struct
import time
DEFAULT_TIMEOUT = 10
+SLEEP_BETWEEN_POLL = 0.5
BANNER_READ = 1024
LOG = logging.getLogger(__name__)
@@ -100,39 +101,44 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False):
Checks whether any of the given ports are open on a target IP.
:param ip: IP of host to attack
:param ports: List of ports to attack. Must not be empty.
- :param timeout: Amount of time to wait for connection
+ :param timeout: Amount of time to wait for connection.
:param get_banner: T/F if to get first packets from server
:return: list of open ports. If get_banner=True, then a matching list of banners.
"""
sockets = [socket.socket(socket.AF_INET, socket.SOCK_STREAM) for _ in range(len(ports))]
[s.setblocking(0) for s in sockets]
- good_ports = []
+ port_attempts = []
try:
for sock, port in zip(sockets, ports):
LOG.debug("Connecting to port %d" % port)
err = sock.connect_ex((ip, port))
if err == 0:
- good_ports.append((port, sock))
+ port_attempts.append((port, sock))
if err == 10035: # WSAEWOULDBLOCK is valid, see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
- good_ports.append((port, sock))
-
- if len(good_ports) != 0:
- read_sockets, write_sockets, _ = \
- select.select(
- [s[1] for s in good_ports],
- [s[1] for s in good_ports],
- [s[1] for s in good_ports],
- timeout) # wait max timeout
- if len(write_sockets) != len(good_ports):
- time.sleep(timeout)
- read_sockets, write_sockets, _ = \
+ port_attempts.append((port, sock))
+ if len(port_attempts) != 0:
+ num_replies = 0
+ timeout = int(round(timeout)) # clamp to integer, to avoid checking input
+ time_left = timeout
+ write_sockets = []
+ read_sockets = []
+ while num_replies != len(port_attempts):
+ # bad ports show up as err_sockets
+ read_sockets, write_sockets, err_sockets = \
select.select(
- [s[1] for s in good_ports],
- [s[1] for s in good_ports],
- [s[1] for s in good_ports],
- 0) # no timeout because we've already slept
+ [s[1] for s in port_attempts],
+ [s[1] for s in port_attempts],
+ [s[1] for s in port_attempts],
+ time_left)
+ # any read_socket is automatically a writesocket
+ num_replies = len(write_sockets) + len(err_sockets)
+ if num_replies == len(port_attempts) or time_left == 0:
+ break
+ else:
+ time_left -= SLEEP_BETWEEN_POLL
+ time.sleep(SLEEP_BETWEEN_POLL)
- connected_ports_sockets = [x for x in good_ports if x[1] in write_sockets]
+ connected_ports_sockets = [x for x in port_attempts if x[1] in write_sockets]
LOG.debug(
"On host %s discovered the following ports %s" %
(str(ip), ",".join([str(x) for x in connected_ports_sockets])))
@@ -143,7 +149,7 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False):
for port, sock in connected_ports_sockets]
pass
# try to cleanup
- [s[1].close() for s in good_ports]
+ [s[1].close() for s in port_attempts]
return [port for port, sock in connected_ports_sockets], banners
else:
return [], []
From 0f2c58b0aa22f7be8e1579c99728256565792482 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Sun, 12 Nov 2017 20:52:01 +0200
Subject: [PATCH 17/92] Add skeleton and more of report
---
monkey_island/cc/ui/package.json | 1 +
monkey_island/cc/ui/src/components/Main.js | 1 +
.../cc/ui/src/components/pages/ReportPage.js | 106 +++++++++++++++++-
.../report-components/BreachedServers.js | 40 +++++++
.../report-components/ScannedServers.js | 41 +++++++
monkey_island/cc/ui/src/styles/App.css | 11 +-
6 files changed, 196 insertions(+), 4 deletions(-)
create mode 100644 monkey_island/cc/ui/src/components/report-components/BreachedServers.js
create mode 100644 monkey_island/cc/ui/src/components/report-components/ScannedServers.js
diff --git a/monkey_island/cc/ui/package.json b/monkey_island/cc/ui/package.json
index 681a98bb3..71f2decd4 100644
--- a/monkey_island/cc/ui/package.json
+++ b/monkey_island/cc/ui/package.json
@@ -80,6 +80,7 @@
"react-modal-dialog": "^4.0.7",
"react-redux": "^5.0.6",
"react-router-dom": "^4.2.2",
+ "react-table": "^6.7.4",
"react-toggle": "^4.0.1",
"redux": "^3.7.2"
}
diff --git a/monkey_island/cc/ui/src/components/Main.js b/monkey_island/cc/ui/src/components/Main.js
index a4d41f2af..dd143ea3a 100644
--- a/monkey_island/cc/ui/src/components/Main.js
+++ b/monkey_island/cc/ui/src/components/Main.js
@@ -16,6 +16,7 @@ require('normalize.css/normalize.css');
require('react-data-components/css/table-twbs.css');
require('styles/App.css');
require('react-toggle/style.css');
+require('react-table/react-table.css');
let logoImage = require('../images/monkey-logo.png');
let guardicoreLogoImage = require('../images/guardicore-logo.png');
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index 633fadc12..c568aa13b 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -1,19 +1,119 @@
import React from 'react';
import {Col} from 'react-bootstrap';
+import BreachedServers from 'components/report-components/BreachedServers';
+import ScannedServers from 'components/report-components/ScannedServers';
+
+const list_item = {
+ label: 'machine 1',
+ ip_addresses: ['1.2.3.4', '5.6.7.8'],
+ accessible_from_nodes: ['machine 2', 'machine 3'],
+ services: ['tcp-80', 'tcp-443']
+};
class ReportPageComponent extends React.Component {
constructor(props) {
super(props);
+
+ this.state = {
+ report: {}
+ };
+ }
+
+ componentDidMount() {
+ fetch('/api/report')
+ .then(res => res.json())
+ .then(res => {
+ this.setState({
+ report: res
+ });
+ });
}
render() {
+ if (Object.keys(this.state.report).length === 0) {
+ return ();
+ }
return (
4. Security Report
-
- Under construction
-
+
+
+ Overview
+
+
+ {/* TODO: Replace 01/02/2017 21:45, 23:12 with data */}
+ The monkey run was started on 01/02/2017 21:45. After 23:12 minutes, all monkeys finished propagation attempts.
+
+
+ From the attacker's point of view, the network looks like this:
+ {/* TODO: Add map */}
+
+
+
* Imagine Map here :) *
+
+
+ {/* TODO: Replace 3 with data */}
+ During this simulated attack the Monkey uncovered 3 issues, detailed below. The security issues uncovered included:
+
+ {/* TODO: Replace lis with data */}
+
Weak user/passwords combinations
+
Machines not patched for the ‘Shellshock’ bug
+
+
+
+ In addition, the monkey uncovered the following possible set of issues:
+
+ {/* TODO: Replace lis with data */}
+
Machines from another segment accessed the Monkey Island
+
Network tunnels were created successfully
+
+
+
+ A full report of the Monkeys activities follows.
+
+
+
+
+ Network Overview
+
+
+ {/* TODO: Replace 6,2 with data */}
+ During the current run, the Monkey discovered 6 machines and successfully breached 2 of them.
+ In addition, it attempted to exploit the rest, any security software installed in the network should have picked up the attack attempts and logged them.
+
+
+ Detailed recommendations in the next part of the report.
+
Breached Servers
+
+
+
+
Scanned Servers
+
+ {/* TODO: Add table of scanned servers */}
+
+
+
+
+ Recommendations
+
+
+
+
Issue #1
+
+ The machine Monkey-SMB with the following IP addresses 192.168.0.110.0.0.18 was vulnerable to a SmbExploiter attack.
+ The attack succeeded because weak/stolen password was used over SMB protocol.
+
+
+
+
Issue #2
+
+ The network can probably be segmented. A monkey instance on Monkey-SMB in the 192.168.0.0/24 network could directly access the Monkey Island C&C server in the 172.168.0.0/24 network.
+
+ {/* TODO: Replace 01/02/2017 21:45, 23:12 with data */}
+ The monkey run was started on 01/02/2017 21:45. After 23:12 minutes, all monkeys finished propagation attempts.
+
+
+ From the attacker's point of view, the network looks like this:
+
+
+
+
+
+ {/* TODO: Replace 3 with data */}
+ During this simulated attack the Monkey uncovered 6 issues, detailed below. The security issues uncovered included:
+
+ {/* TODO: Replace lis with data */}
+
Weak user/passwords combinations.
+
Stolen passwords/hashes used to exploit other machines.
+
Elastic Search servers not patched for CVE-2015-1427 bug.
+
Samba servers not patched for ‘SambaCry’ bug.
+
Machines not patched for the ‘Shellshock’ bug.
+
Machines not patched for the ‘Conficker’ bug.
+
+
+
+ In addition, the monkey uncovered the following possible set of issues:
+
+ {/* TODO: Replace lis with data */}
+
Machines freely accessed the Monkey Island despite being on different networks.
+
Machines are not locked down at port level, tunnels between network segments were setup successfully.
+
+
+
+ A full report of the Monkeys activities follows.
+
+
+
+
+ Network Overview
+
+
+ {/* TODO: Replace 6,2 with data */}
+ During the current run, the Monkey discovered 6 machines and successfully breached 2 of them.
+ In addition, it attempted to exploit the rest, any security software installed in the network should have picked up the attack attempts and logged them.
+
+
+ Detailed recommendations in the next part of the report.
+
Breached Servers
+
+
+
+
Scanned Servers
+
+ {/* TODO: Add table of scanned servers */}
+
+
+
+
+ Stolen Credentials
+
+
+
+
+
+ Recommendations
+
+
+
+
Issue #1
+
+ The machine Monkey-SMB with the following IP addresses 192.168.0.110.0.0.18 was vulnerable to a SMB attack.
+
+ The attack succeeded by authenticating over SMB protocol with user Administrator and its password.
+
+
+
+
Issue #2
+
+ The machine Monkey-SMB2 with the following IP address 192.168.0.2 was vulnerable to a SMB attack.
+
+ The attack succeeded by using a pass-the-hash attack over SMB protocol with user temp.
+
+
+
+
Issue #3
+
+ The machine Monkey-WMI with the following IP address 192.168.0.3 was vulnerable to a WMI attack.
+
+ The attack succeeded by authenticating over WMI protocol with user Administrator and its password.
+
+
+
+
Issue #4
+
+ The machine Monkey-WMI2 with the following IP address 192.168.0.4 was vulnerable to a WMI attack.
+
+ The attack succeeded by using a pass-the-hash attack over WMI protocol with user Administrator.
+
+
+
+
Issue #5
+
+ The machine Monkey-SSH with the following IP address 192.168.0.5 was vulnerable to a SSH attack.
+
+ The attack succeeded by authenticating over SSH protocol with user user and its password.
+
+
+
+
Issue #6
+
+ The machine Monkey-RDP with the following IP address 192.168.0.6 was vulnerable to a RDP attack.
+
+ The attack succeeded by authenticating over RDP protocol with user Administrator and its password.
+
+
+
+
Issue #7
+
+ The machine Monkey-SambaCry with the following IP address 192.168.0.7 was vulnerable to a SambaCry attack.
+
+ The attack succeeded by authenticating over SMB protocol with user user and its password, and by using the SambaCry vulnerability.
+
+
+
+
Issue #8
+
+ The machine Monkey-Elastic with the following IP address 192.168.0.8 was vulnerable to an Elastic Groovy attack.
+
+ The attack succeeded because the Elastic Search server was not parched against the CVE-2015-1427 bug.
+
+
+
+
Issue #9
+
+ The machine Monkey-Shellshock with the following IP address 192.168.0.9 was vulnerable to a ShellShock attack.
+
+ The attack succeeded because the HTTP server running on port 8080 was vulnerable to a shell injection attack on the paths: /cgi/backserver.cgi/cgi/login.cgi.
+
+
+
+
Issue #10
+
+ The machine Monkey-Conficker with the following IP address 192.168.0.10 was vulnerable to a Conficker attack.
+
+ The attack succeeded because the target machine uses an outdated and unpatched operating system.
+
+
+
+
Issue #11
+
+ The network can probably be segmented. A monkey instance on Monkey-SMB in the 192.168.0.0/24 network could directly access the Monkey Island C&C server in the 172.168.0.0/24 network.
+
+
+
+
Issue #12
+
+ The network can probably be segmented. A monkey instance on Monkey-SSH in the 192.168.0.0/24 network could directly access the Monkey Island C&C server in the 172.168.0.0/24 network.
+
+
+
+
Issue #13
+
+ Machines are not locked down at port level. Network tunnel was set up from Monkey-SSH to Monkey-SambaCry.
+
+
+
+
+
+ );
}
return (
4. Security Report
-
-
- Overview
-
-
- {/* TODO: Replace 01/02/2017 21:45, 23:12 with data */}
- The monkey run was started on 01/02/2017 21:45. After 23:12 minutes, all monkeys finished propagation attempts.
-
-
- From the attacker's point of view, the network looks like this:
-
-
-
-
-
- {/* TODO: Replace 3 with data */}
- During this simulated attack the Monkey uncovered 3 issues, detailed below. The security issues uncovered included:
-
- {/* TODO: Replace lis with data */}
-
Weak user/passwords combinations
-
Machines not patched for the ‘Shellshock’ bug
-
-
-
- In addition, the monkey uncovered the following possible set of issues:
-
- {/* TODO: Replace lis with data */}
-
Machines from another segment accessed the Monkey Island
-
Network tunnels were created successfully
-
-
-
- A full report of the Monkeys activities follows.
-
-
-
-
- Network Overview
-
-
- {/* TODO: Replace 6,2 with data */}
- During the current run, the Monkey discovered 6 machines and successfully breached 2 of them.
- In addition, it attempted to exploit the rest, any security software installed in the network should have picked up the attack attempts and logged them.
-
-
- Detailed recommendations in the next part of the report.
-
Breached Servers
-
-
-
-
Scanned Servers
-
- {/* TODO: Add table of scanned servers */}
-
-
-
-
- Recommendations
-
-
-
-
Issue #1
-
- The machine Monkey-SMB with the following IP addresses 192.168.0.110.0.0.18 was vulnerable to a SmbExploiter attack.
- The attack succeeded because weak/stolen password was used over SMB protocol.
-
-
-
-
Issue #2
-
- The network can probably be segmented. A monkey instance on Monkey-SMB in the 192.168.0.0/24 network could directly access the Monkey Island C&C server in the 172.168.0.0/24 network.
-
- {/* TODO: Replace 6,2 with data */}
- During the current run, the Monkey discovered 6 machines and successfully breached 2 of them.
- In addition, it attempted to exploit the rest, any security software installed in the network should have picked up the attack attempts and logged them.
-
- Detailed recommendations in the next part of the report.
-
Breached Servers
+
+
+ {/* TODO: Replace 6,2 with data */}
+ During the current run, the Monkey discovered 6 machines and successfully breached 2 of them.
+
+ In addition, it attempted to exploit the rest, any security software installed in the network should have picked up the attack attempts and logged them.
+
+ Detailed recommendations in the next part of the report.
+
+ )
+ }
+}
+
+export default ScannedBreachedChartComponent;
diff --git a/monkey_island/cc/ui/src/components/report-components/ScannedServers.js b/monkey_island/cc/ui/src/components/report-components/ScannedServers.js
index 9ae1b5135..b598ab537 100644
--- a/monkey_island/cc/ui/src/components/report-components/ScannedServers.js
+++ b/monkey_island/cc/ui/src/components/report-components/ScannedServers.js
@@ -9,10 +9,15 @@ let renderArray = function(val) {
};
const columns = [
- { Header: 'Machine', accessor: 'label'},
- { Header: 'IP Addresses', id: 'ip_addresses', accessor: x => renderArray(x.ip_addresses)},
- { Header: 'Accessible From', id: 'accessible_from_nodes', accessor: x => renderArray(x.accessible_from_nodes)},
- { Header: 'Services', id: 'services', accessor: x => renderArray(x.services)}
+ {
+ Header: 'Scanned Servers',
+ columns: [
+ { Header: 'Machine', accessor: 'label'},
+ { Header: 'IP Addresses', id: 'ip_addresses', accessor: x => renderArray(x.ip_addresses)},
+ { Header: 'Accessible From', id: 'accessible_from_nodes', accessor: x => renderArray(x.accessible_from_nodes)},
+ { Header: 'Services', id: 'services', accessor: x => renderArray(x.services)}
+ ]
+ }
];
const pageSize = 10;
diff --git a/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js b/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js
index c34d51bed..754b51f92 100644
--- a/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js
+++ b/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js
@@ -2,10 +2,15 @@ import React from 'react';
import ReactTable from 'react-table'
const columns = [
- { Header: 'Username', accessor: 'username'},
- { Header: 'Password/Hash', accessor: 'password'},
- { Header: 'Type', accessor: 'type'},
- { Header: 'Origin', accessor: 'origin'}
+ {
+ Header: 'Stolen Credentials',
+ columns: [
+ { Header: 'Username', accessor: 'username'},
+ { Header: 'Password/Hash', accessor: 'password'},
+ { Header: 'Type', accessor: 'type'},
+ { Header: 'Origin', accessor: 'origin'}
+ ]
+ }
];
const pageSize = 10;
From f787801ab7b922157bb7455f66a512e6f6c94c8d Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 14 Nov 2017 16:10:22 +0200
Subject: [PATCH 21/92] Add recommendations to security issues
---
.../cc/ui/src/components/pages/ReportPage.js | 68 ++++++++++++++++++-
1 file changed, 67 insertions(+), 1 deletion(-)
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index 1bf41a2e8..6bdb62fbc 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -146,6 +146,11 @@ class ReportPageComponent extends React.Component {
The machine Monkey-SMB with the following IP addresses 192.168.0.110.0.0.18 was vulnerable to a SMB attack.
The attack succeeded by authenticating over SMB protocol with user Administrator and its password.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Use a complex one-use password that is not shared with other computers on the network.
+
@@ -154,6 +159,11 @@ class ReportPageComponent extends React.Component {
The machine Monkey-SMB2 with the following IP address 192.168.0.2 was vulnerable to a SMB attack.
The attack succeeded by using a pass-the-hash attack over SMB protocol with user temp.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Use a complex one-use password that is not shared with other computers on the network.
+
@@ -162,6 +172,11 @@ class ReportPageComponent extends React.Component {
The machine Monkey-WMI with the following IP address 192.168.0.3 was vulnerable to a WMI attack.
The attack succeeded by authenticating over WMI protocol with user Administrator and its password.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Use a complex one-use password that is not shared with other computers on the network.
+
@@ -170,6 +185,11 @@ class ReportPageComponent extends React.Component {
The machine Monkey-WMI2 with the following IP address 192.168.0.4 was vulnerable to a WMI attack.
The attack succeeded by using a pass-the-hash attack over WMI protocol with user Administrator.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Use a complex one-use password that is not shared with other computers on the network.
+
@@ -178,6 +198,11 @@ class ReportPageComponent extends React.Component {
The machine Monkey-SSH with the following IP address 192.168.0.5 was vulnerable to a SSH attack.
The attack succeeded by authenticating over SSH protocol with user user and its password.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Use a complex one-use password that is not shared with other computers on the network.
+
@@ -186,6 +211,11 @@ class ReportPageComponent extends React.Component {
The machine Monkey-RDP with the following IP address 192.168.0.6 was vulnerable to a RDP attack.
The attack succeeded by authenticating over RDP protocol with user Administrator and its password.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Use a complex one-use password that is not shared with other computers on the network.
+
@@ -194,6 +224,12 @@ class ReportPageComponent extends React.Component {
The machine Monkey-SambaCry with the following IP address 192.168.0.7 was vulnerable to a SambaCry attack.
The attack succeeded by authenticating over SMB protocol with user user and its password, and by using the SambaCry vulnerability.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up.
+
Use a complex one-use password that is not shared with other computers on the network.
+
@@ -202,6 +238,11 @@ class ReportPageComponent extends React.Component {
The machine Monkey-Elastic with the following IP address 192.168.0.8 was vulnerable to an Elastic Groovy attack.
The attack succeeded because the Elastic Search server was not parched against the CVE-2015-1427 bug.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Update your Elastic Search server to version 1.4.3 and up.
+
@@ -210,6 +251,11 @@ class ReportPageComponent extends React.Component {
The machine Monkey-Shellshock with the following IP address 192.168.0.9 was vulnerable to a ShellShock attack.
The attack succeeded because the HTTP server running on port 8080 was vulnerable to a shell injection attack on the paths: /cgi/backserver.cgi/cgi/login.cgi.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Update your Bash to a ShellShock-patched version.
+
@@ -217,25 +263,45 @@ class ReportPageComponent extends React.Component {
The machine Monkey-Conficker with the following IP address 192.168.0.10 was vulnerable to a Conficker attack.
- The attack succeeded because the target machine uses an outdated and unpatched operating system.
+ The attack succeeded because the target machine uses an outdated and unpatched operating system vulnerable to Conficker.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Install the latest Windows updates or upgrade to a newer operating system.
+
Issue #11
The network can probably be segmented. A monkey instance on Monkey-SMB in the 192.168.0.0/24 network could directly access the Monkey Island C&C server in the 172.168.0.0/24 network.
+
+ In order to protect the network, the following steps should be performed:
+
+
Segment your network. Make sure machines can't access machines from other segments.
+
Issue #12
The network can probably be segmented. A monkey instance on Monkey-SSH in the 192.168.0.0/24 network could directly access the Monkey Island C&C server in the 172.168.0.0/24 network.
+
+ In order to protect the network, the following steps should be performed:
+
+
Segment your network. Make sure machines can't access machines from other segments.
+
Issue #13
Machines are not locked down at port level. Network tunnel was set up from Monkey-SSH to Monkey-SambaCry.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Use micro-segmentation policies to disable communication other than the required.
{/* TODO: Replace 3 with data */}
- During this simulated attack the Monkey uncovered 6 issues, detailed below. The security issues uncovered included:
+ During this simulated attack the Monkey uncovered 6 issues, detailed below. The security issues uncovered include:
{/* TODO: Replace lis with data */}
-
Weak user/passwords combinations.
-
Stolen passwords/hashes used to exploit other machines.
-
Elastic Search servers not patched for CVE-2015-1427 bug.
-
Samba servers not patched for ‘SambaCry’ bug.
-
Machines not patched for the ‘Shellshock’ bug.
-
Machines not patched for the ‘Conficker’ bug.
+
Users with weak passwords.
+
Stolen passwords/hashes were used to exploit other machines.
+
Elastic Search servers not patched for CVE-2015-1427.
+
Samba servers not patched for ‘SambaCry’ (CVE-2017-7494).
+
Machines not patched for the ‘Shellshock’ (CVE-2014-6271).
+
Machines not patched for the ‘Conficker’ (MS08-067).
In addition, the monkey uncovered the following possible set of issues:
{/* TODO: Replace lis with data */}
-
Machines freely accessed the Monkey Island despite being on different networks.
-
Machines are not locked down at port level, tunnels between network segments were setup successfully.
+
Possible cross segment traffic. Infected machines could communicate with the Monkey Island despite crossing segment boundaries using unused ports.
+
Lack of port level segmentation, machines successfully tunneled monkey activity using unused ports.
@@ -111,9 +111,9 @@ class ReportPageComponent extends React.Component {
{/* TODO: Replace 6,2 with data */}
- During the current run, the Monkey discovered 6 machines and successfully breached 2 of them.
+ The Monkey discovered 6 machines and successfully breached 2 of them.
- In addition, it attempted to exploit the rest, any security software installed in the network should have picked up the attack attempts and logged them.
+ In addition, while attempting to exploit additional hosts , security software installed in the network should have picked up the attack attempts and logged them.
Detailed recommendations in the next part of the report.
@@ -142,7 +142,7 @@ class ReportPageComponent extends React.Component {
Issue #1
-
+
The machine Monkey-SMB with the following IP addresses 192.168.0.110.0.0.18 was vulnerable to a SMB attack.
The attack succeeded by authenticating over SMB protocol with user Administrator and its password.
@@ -151,11 +151,11 @@ class ReportPageComponent extends React.Component {
Use a complex one-use password that is not shared with other computers on the network.
-
+
Issue #2
-
+
The machine Monkey-SMB2 with the following IP address 192.168.0.2 was vulnerable to a SMB attack.
The attack succeeded by using a pass-the-hash attack over SMB protocol with user temp.
@@ -164,11 +164,11 @@ class ReportPageComponent extends React.Component {
Use a complex one-use password that is not shared with other computers on the network.
-
+
Issue #3
-
+
The machine Monkey-WMI with the following IP address 192.168.0.3 was vulnerable to a WMI attack.
The attack succeeded by authenticating over WMI protocol with user Administrator and its password.
@@ -177,11 +177,11 @@ class ReportPageComponent extends React.Component {
Use a complex one-use password that is not shared with other computers on the network.
-
+
Issue #4
-
+
The machine Monkey-WMI2 with the following IP address 192.168.0.4 was vulnerable to a WMI attack.
The attack succeeded by using a pass-the-hash attack over WMI protocol with user Administrator.
@@ -190,11 +190,11 @@ class ReportPageComponent extends React.Component {
Use a complex one-use password that is not shared with other computers on the network.
-
+
Issue #5
-
+
The machine Monkey-SSH with the following IP address 192.168.0.5 was vulnerable to a SSH attack.
The attack succeeded by authenticating over SSH protocol with user user and its password.
@@ -203,11 +203,11 @@ class ReportPageComponent extends React.Component {
Use a complex one-use password that is not shared with other computers on the network.
-
+
Issue #6
-
+
The machine Monkey-RDP with the following IP address 192.168.0.6 was vulnerable to a RDP attack.
The attack succeeded by authenticating over RDP protocol with user Administrator and its password.
@@ -216,11 +216,11 @@ class ReportPageComponent extends React.Component {
Use a complex one-use password that is not shared with other computers on the network.
-
+
Issue #7
-
+
The machine Monkey-SambaCry with the following IP address 192.168.0.7 was vulnerable to a SambaCry attack.
The attack succeeded by authenticating over SMB protocol with user user and its password, and by using the SambaCry vulnerability.
@@ -230,24 +230,24 @@ class ReportPageComponent extends React.Component {
Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up.
Use a complex one-use password that is not shared with other computers on the network.
-
+
Issue #8
-
+
The machine Monkey-Elastic with the following IP address 192.168.0.8 was vulnerable to an Elastic Groovy attack.
- The attack succeeded because the Elastic Search server was not parched against the CVE-2015-1427 bug.
+ The attack succeeded because the Elastic Search server was not parched against CVE-2015-1427.
In order to protect the machine, the following steps should be performed:
Update your Elastic Search server to version 1.4.3 and up.
-
+
Issue #9
-
+
The machine Monkey-Shellshock with the following IP address 192.168.0.9 was vulnerable to a ShellShock attack.
The attack succeeded because the HTTP server running on port 8080 was vulnerable to a shell injection attack on the paths: /cgi/backserver.cgi/cgi/login.cgi.
@@ -256,11 +256,11 @@ class ReportPageComponent extends React.Component {
Update your Bash to a ShellShock-patched version.
-
+
Issue #10
-
+
The machine Monkey-Conficker with the following IP address 192.168.0.10 was vulnerable to a Conficker attack.
The attack succeeded because the target machine uses an outdated and unpatched operating system vulnerable to Conficker.
@@ -269,40 +269,40 @@ class ReportPageComponent extends React.Component {
Install the latest Windows updates or upgrade to a newer operating system.
-
+
Issue #11
-
+
The network can probably be segmented. A monkey instance on Monkey-SMB in the 192.168.0.0/24 network could directly access the Monkey Island C&C server in the 172.168.0.0/24 network.
In order to protect the network, the following steps should be performed:
Segment your network. Make sure machines can't access machines from other segments.
-
+
Issue #12
-
+
The network can probably be segmented. A monkey instance on Monkey-SSH in the 192.168.0.0/24 network could directly access the Monkey Island C&C server in the 172.168.0.0/24 network.
In order to protect the network, the following steps should be performed:
Segment your network. Make sure machines can't access machines from other segments.
-
+
Issue #13
-
+
Machines are not locked down at port level. Network tunnel was set up from Monkey-SSH to Monkey-SambaCry.
In order to protect the machine, the following steps should be performed:
Use micro-segmentation policies to disable communication other than the required.
+ The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a SMB attack.
+
+ The attack succeeded by authenticating over SMB protocol with user {issue.username} and its password.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Use a complex one-use password that is not shared with other computers on the network.
+ The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a SMB attack.
+
+ The attack succeeded by using a pass-the-hash attack over SMB protocol with user {issue.username}.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Use a complex one-use password that is not shared with other computers on the network.
+ The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a WMI attack.
+
+ The attack succeeded by authenticating over WMI protocol with user {issue.username} and its password.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Use a complex one-use password that is not shared with other computers on the network.
+ The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a WMI attack.
+
+ The attack succeeded by using a pass-the-hash attack over WMI protocol with user {issue.username}.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Use a complex one-use password that is not shared with other computers on the network.
+ The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a SSH attack.
+
+ The attack succeeded by authenticating over SSH protocol with user {issue.username} and its password.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Use a complex one-use password that is not shared with other computers on the network.
+ The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a RDP attack.
+
+ The attack succeeded by authenticating over RDP protocol with user {issue.username} and its password.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Use a complex one-use password that is not shared with other computers on the network.
+ The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a SambaCry attack.
+
+ The attack succeeded by authenticating over SMB protocol with user {issue.username} and its password, and by using the SambaCry vulnerability.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up.
+
Use a complex one-use password that is not shared with other computers on the network.
+ The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to an Elastic Groovy attack.
+
+ The attack succeeded because the Elastic Search server was not parched against CVE-2015-1427.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Update your Elastic Search server to version 1.4.3 and up.
+ The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a ShellShock attack.
+
+ The attack succeeded because the HTTP server running on port {issue.port} was vulnerable to a shell injection attack on the paths: {this.generateShellshockPathListBadges(issue.paths)}.
+
+ In order to protect the machine, the following steps should be performed:
+
+ The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a Conficker attack.
+
+ The attack succeeded because the target machine uses an outdated and unpatched operating system vulnerable to Conficker.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Install the latest Windows updates or upgrade to a newer operating system.
+ The network can probably be segmented. A monkey instance on {issue.machine} in the {issue.network} network could directly access the Monkey Island C&C server in the {issue.server_network} network.
+
+ In order to protect the network, the following steps should be performed:
+
+
Segment your network. Make sure machines can't access machines from other segments.
+ Machines are not locked down at port level. Network tunnel was set up from {issue.origin} to {issue.dest}.
+
+ In order to protect the machine, the following steps should be performed:
+
+
Use micro-segmentation policies to disable communication other than the required.
+
+
+ );
+ }
+
+ generateIssue = (issue, index) => {
+ let data;
+ switch (issue.type) {
+ case 'smb_password':
+ data = this.generateSmbPasswordIssue(issue);
+ break;
+ case 'smb_pth':
+ data = this.generateSmbPthIssue(issue);
+ break;
+ case 'wmi_password':
+ data = this.generateWmiPasswordIssue(issue);
+ break;
+ case 'wmi_pth':
+ data = this.generateWmiPthIssue(issue);
+ break;
+ case 'ssh':
+ data = this.generateSshIssue(issue);
+ break;
+ case 'rdp':
+ data = this.generateRdpIssue(issue);
+ break;
+ case 'sambacry':
+ data = this.generateSambaCryIssue(issue);
+ break;
+ case 'elastic':
+ data = this.generateElasticIssue(issue);
+ break;
+ case 'shellshock':
+ data = this.generateShellshockIssue(issue);
+ break;
+ case 'conficker':
+ data = this.generateConfickerIssue(issue);
+ break;
+ case 'cross_segment':
+ data = this.generateCrossSegmentIssue(issue);
+ break;
+ case 'tunnel':
+ data = this.generateTunnelIssue(issue);
+ break;
+ }
+ return (
+
- {/* TODO: Replace 01/02/2017 21:45, 23:12 with data */}
- The monkey run was started on 01/02/2017 21:45. After 23:12 minutes, all monkeys finished propagation attempts.
+ The monkey run was started on {this.state.report.overview.monkey_start_time}. After {this.state.report.overview.monkey_duration}, all monkeys finished propagation attempts.
From the attacker's point of view, the network looks like this:
@@ -79,24 +363,21 @@ class ReportPageComponent extends React.Component {
- {/* TODO: Replace 3 with data */}
- During this simulated attack the Monkey uncovered 6 issues, detailed below. The security issues uncovered include:
+ During this simulated attack the Monkey uncovered {this.state.report.overview.issues.filter(function(x){return x===true;}).length}, detailed below. The security issues uncovered include:
- {/* TODO: Replace lis with data */}
-
Users with weak passwords.
-
Stolen passwords/hashes were used to exploit other machines.
-
Elastic Search servers not patched for CVE-2015-1427.
-
Samba servers not patched for ‘SambaCry’ (CVE-2017-7494).
-
Machines not patched for the ‘Shellshock’ (CVE-2014-6271).
-
Machines not patched for the ‘Conficker’ (MS08-067).
Lack of port level segmentation, machines successfully tunneled monkey activity using unused ports.
: null}
@@ -110,8 +391,7 @@ class ReportPageComponent extends React.Component {
- {/* TODO: Replace 6,2 with data */}
- The Monkey discovered 6 machines and successfully breached 2 of them.
+ The Monkey discovered {this.state.report.glance.scanned.length} machines and successfully breached {this.state.report.glance.exploited.length} of them.
In addition, while attempting to exploit additional hosts , security software installed in the network should have picked up the attack attempts and logged them.
@@ -120,191 +400,27 @@ class ReportPageComponent extends React.Component {
-
+
-
+
-
- {/* TODO: Add table of scanned servers */}
+
-
+
Recommendations
-
-
Issue #1
-
- The machine Monkey-SMB with the following IP addresses 192.168.0.110.0.0.18 was vulnerable to a SMB attack.
-
- The attack succeeded by authenticating over SMB protocol with user Administrator and its password.
-
- In order to protect the machine, the following steps should be performed:
-
-
Use a complex one-use password that is not shared with other computers on the network.
- The machine Monkey-SMB2 with the following IP address 192.168.0.2 was vulnerable to a SMB attack.
-
- The attack succeeded by using a pass-the-hash attack over SMB protocol with user temp.
-
- In order to protect the machine, the following steps should be performed:
-
-
Use a complex one-use password that is not shared with other computers on the network.
-
-
-
-
-
Issue #3
-
- The machine Monkey-WMI with the following IP address 192.168.0.3 was vulnerable to a WMI attack.
-
- The attack succeeded by authenticating over WMI protocol with user Administrator and its password.
-
- In order to protect the machine, the following steps should be performed:
-
-
Use a complex one-use password that is not shared with other computers on the network.
-
-
-
-
-
Issue #4
-
- The machine Monkey-WMI2 with the following IP address 192.168.0.4 was vulnerable to a WMI attack.
-
- The attack succeeded by using a pass-the-hash attack over WMI protocol with user Administrator.
-
- In order to protect the machine, the following steps should be performed:
-
-
Use a complex one-use password that is not shared with other computers on the network.
-
-
-
-
-
Issue #5
-
- The machine Monkey-SSH with the following IP address 192.168.0.5 was vulnerable to a SSH attack.
-
- The attack succeeded by authenticating over SSH protocol with user user and its password.
-
- In order to protect the machine, the following steps should be performed:
-
-
Use a complex one-use password that is not shared with other computers on the network.
-
-
-
-
-
Issue #6
-
- The machine Monkey-RDP with the following IP address 192.168.0.6 was vulnerable to a RDP attack.
-
- The attack succeeded by authenticating over RDP protocol with user Administrator and its password.
-
- In order to protect the machine, the following steps should be performed:
-
-
Use a complex one-use password that is not shared with other computers on the network.
-
-
-
-
-
Issue #7
-
- The machine Monkey-SambaCry with the following IP address 192.168.0.7 was vulnerable to a SambaCry attack.
-
- The attack succeeded by authenticating over SMB protocol with user user and its password, and by using the SambaCry vulnerability.
-
- In order to protect the machine, the following steps should be performed:
-
-
Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up.
-
Use a complex one-use password that is not shared with other computers on the network.
-
-
-
-
-
Issue #8
-
- The machine Monkey-Elastic with the following IP address 192.168.0.8 was vulnerable to an Elastic Groovy attack.
-
- The attack succeeded because the Elastic Search server was not parched against CVE-2015-1427.
-
- In order to protect the machine, the following steps should be performed:
-
-
Update your Elastic Search server to version 1.4.3 and up.
-
-
-
-
-
Issue #9
-
- The machine Monkey-Shellshock with the following IP address 192.168.0.9 was vulnerable to a ShellShock attack.
-
- The attack succeeded because the HTTP server running on port 8080 was vulnerable to a shell injection attack on the paths: /cgi/backserver.cgi/cgi/login.cgi.
-
- In order to protect the machine, the following steps should be performed:
-
-
Update your Bash to a ShellShock-patched version.
-
-
-
-
-
Issue #10
-
- The machine Monkey-Conficker with the following IP address 192.168.0.10 was vulnerable to a Conficker attack.
-
- The attack succeeded because the target machine uses an outdated and unpatched operating system vulnerable to Conficker.
-
- In order to protect the machine, the following steps should be performed:
-
-
Install the latest Windows updates or upgrade to a newer operating system.
-
-
-
-
-
Issue #11
-
- The network can probably be segmented. A monkey instance on Monkey-SMB in the 192.168.0.0/24 network could directly access the Monkey Island C&C server in the 172.168.0.0/24 network.
-
- In order to protect the network, the following steps should be performed:
-
-
Segment your network. Make sure machines can't access machines from other segments.
-
-
-
-
-
Issue #12
-
- The network can probably be segmented. A monkey instance on Monkey-SSH in the 192.168.0.0/24 network could directly access the Monkey Island C&C server in the 172.168.0.0/24 network.
-
- In order to protect the network, the following steps should be performed:
-
-
Segment your network. Make sure machines can't access machines from other segments.
-
-
-
-
-
Issue #13
-
- Machines are not locked down at port level. Network tunnel was set up from Monkey-SSH to Monkey-SambaCry.
-
- In order to protect the machine, the following steps should be performed:
-
-
Use micro-segmentation policies to disable communication other than the required.
- The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a SMB attack.
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SMB attack.
The attack succeeded by authenticating over SMB protocol with user {issue.username} and its password.
@@ -91,7 +91,7 @@ class ReportPageComponent extends React.Component {
generateSmbPthIssue(issue) {
return (
- The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a SMB attack.
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SMB attack.
The attack succeeded by using a pass-the-hash attack over SMB protocol with user {issue.username}.
@@ -106,7 +106,7 @@ class ReportPageComponent extends React.Component {
generateWmiPasswordIssue(issue) {
return (
- The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a WMI attack.
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a WMI attack.
The attack succeeded by authenticating over WMI protocol with user {issue.username} and its password.
@@ -121,7 +121,7 @@ class ReportPageComponent extends React.Component {
generateWmiPthIssue(issue) {
return (
- The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a WMI attack.
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a WMI attack.
The attack succeeded by using a pass-the-hash attack over WMI protocol with user {issue.username}.
@@ -136,7 +136,7 @@ class ReportPageComponent extends React.Component {
generateSshIssue(issue) {
return (
- The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a SSH attack.
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SSH attack.
The attack succeeded by authenticating over SSH protocol with user {issue.username} and its password.
@@ -151,7 +151,7 @@ class ReportPageComponent extends React.Component {
generateRdpIssue(issue) {
return (
- The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a RDP attack.
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a RDP attack.
The attack succeeded by authenticating over RDP protocol with user {issue.username} and its password.
@@ -166,7 +166,7 @@ class ReportPageComponent extends React.Component {
generateSambaCryIssue(issue) {
return (
- The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a SambaCry attack.
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SambaCry attack.
The attack succeeded by authenticating over SMB protocol with user {issue.username} and its password, and by using the SambaCry vulnerability.
@@ -182,7 +182,7 @@ class ReportPageComponent extends React.Component {
generateElasticIssue(issue) {
return (
- The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to an Elastic Groovy attack.
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to an Elastic Groovy attack.
The attack succeeded because the Elastic Search server was not parched against CVE-2015-1427.
@@ -197,7 +197,7 @@ class ReportPageComponent extends React.Component {
generateShellshockIssue(issue) {
return (
- The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a ShellShock attack.
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a ShellShock attack.
The attack succeeded because the HTTP server running on port {issue.port} was vulnerable to a shell injection attack on the paths: {this.generateShellshockPathListBadges(issue.paths)}.
@@ -212,7 +212,7 @@ class ReportPageComponent extends React.Component {
generateConfickerIssue(issue) {
return (
- The machine {issue.machine} with the following IP addresses {this.generateIpListBadges(issue.ip_addresses)} was vulnerable to a Conficker attack.
+ The machine {issue.machine} with the following address {issue.ip_address} was vulnerable to a Conficker attack.
The attack succeeded because the target machine uses an outdated and unpatched operating system vulnerable to Conficker.
@@ -227,7 +227,7 @@ class ReportPageComponent extends React.Component {
generateCrossSegmentIssue(issue) {
return (
- The network can probably be segmented. A monkey instance on {issue.machine} in the {issue.network} network could directly access the Monkey Island C&C server in the {issue.server_network} network.
+ The network can probably be segmented. A monkey instance on {issue.machine} in the networks {this.generateInfoBadges(issue.networks)} could directly access the Monkey Island C&C server in the networks {this.generateInfoBadges(issue.server_networks)}.
In order to protect the network, the following steps should be performed:
diff --git a/monkey_island/cc/utils.py b/monkey_island/cc/utils.py
index 34026b157..d59c23825 100644
--- a/monkey_island/cc/utils.py
+++ b/monkey_island/cc/utils.py
@@ -4,6 +4,7 @@ import sys
import array
import struct
+import ipaddress
from netifaces import interfaces, ifaddresses, AF_INET
from cc.database import mongo
@@ -56,3 +57,11 @@ def local_ip_addresses():
addresses = ifaddresses(interface).get(AF_INET, [])
ip_list.extend([link['addr'] for link in addresses if link['addr'] != '127.0.0.1'])
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
diff --git a/monkey_island/requirements.txt b/monkey_island/requirements.txt
index 275c8b96a..1aa7288c5 100644
--- a/monkey_island/requirements.txt
+++ b/monkey_island/requirements.txt
@@ -9,4 +9,5 @@ flask
Flask-Pymongo
Flask-Restful
jsonschema
-netifaces
\ No newline at end of file
+netifaces
+ipaddress
\ No newline at end of file
From f72b32bb671abcadf2c2b60dc2527fa084ce8560 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Mon, 27 Nov 2017 15:51:56 +0200
Subject: [PATCH 30/92] Removed duplicate issues
---
monkey_island/cc/services/report.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py
index 609b17c31..30cdf14ec 100644
--- a/monkey_island/cc/services/report.py
+++ b/monkey_island/cc/services/report.py
@@ -208,8 +208,12 @@ class ReportService:
@staticmethod
def get_exploits():
- return [ReportService.process_exploit(exploit) for
- exploit in mongo.db.telemetry.find({'telem_type': 'exploit', 'data.result': True})]
+ exploits = []
+ for exploit in mongo.db.telemetry.find({'telem_type': 'exploit', 'data.result': True}):
+ new_exploit = ReportService.process_exploit(exploit)
+ if new_exploit not in exploits:
+ exploits.append(new_exploit)
+ return exploits
@staticmethod
def get_monkey_subnets(monkey_guid):
From 4f6ed955017d4ce1cc6565064fbc90fb542e984b Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 28 Nov 2017 13:40:51 +0200
Subject: [PATCH 31/92] Fix bug with exploited nodes
---
monkey_island/cc/services/report.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py
index 30cdf14ec..3a3509a40 100644
--- a/monkey_island/cc/services/report.py
+++ b/monkey_island/cc/services/report.py
@@ -78,7 +78,7 @@ class ReportService:
exploited = [
{
- 'label': NodeService.get_node_hostname(monkey),
+ 'label': NodeService.get_node_hostname(NodeService.get_node_or_monkey_by_id(monkey['id'])),
'ip_addresses': monkey['ip_addresses'],
'exploits': [exploit['exploiter'] for exploit in monkey['exploits'] if exploit['result']]
}
From 2aadb1281555d7a6a76be824c4a266153c853a0b Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 28 Nov 2017 14:16:16 +0200
Subject: [PATCH 32/92] Change page structure
---
.../cc/ui/src/components/pages/ReportPage.js | 49 ++++++++++++-------
1 file changed, 30 insertions(+), 19 deletions(-)
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index 305d39fd6..be5c0e9e3 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -311,16 +311,21 @@ class ReportPageComponent extends React.Component {
Overview
- The monkey run was started on {this.state.report.overview.monkey_start_time}. After {this.state.report.overview.monkey_duration}, all monkeys finished propagation attempts.
+ The first monkey run was started on {this.state.report.overview.monkey_start_time}. After {this.state.report.overview.monkey_duration}, all monkeys finished propagation attempts.
- From the attacker's point of view, the network looks like this:
+ A full report of the Monkeys activities follows.
-
-
-
+
+
+
+ Security Findings
+
- During this simulated attack the Monkey uncovered {this.state.report.overview.issues.filter(function(x){return x===true;}).length}, detailed below. The security issues uncovered include:
+
+ Immediate Threats
+
+ During this simulated attack the Monkey uncovered {this.state.report.overview.issues.filter(function(x){return x===true;}).length} issues, detailed below. The security issues uncovered include:
- Machines are not locked down at port level. Network tunnel was set up from {issue.origin} to {issue.dest}.
+ Machines are not locked down at port level. Network tunnel was set up from {issue.machine} to {issue.dest}.
In order to protect the machine, the following steps should be performed:
From dff90ab5340f600e7e8c530252e9bbbbc8309d31 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 28 Nov 2017 14:37:11 +0200
Subject: [PATCH 35/92] Remove duplicate exploits on breached servers
---
monkey_island/cc/services/report.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py
index 867feeda6..5af051e72 100644
--- a/monkey_island/cc/services/report.py
+++ b/monkey_island/cc/services/report.py
@@ -80,7 +80,7 @@ class ReportService:
{
'label': NodeService.get_node_hostname(NodeService.get_node_or_monkey_by_id(monkey['id'])),
'ip_addresses': monkey['ip_addresses'],
- 'exploits': [exploit['exploiter'] for exploit in monkey['exploits'] if exploit['result']]
+ 'exploits': list(set([exploit['exploiter'] for exploit in monkey['exploits'] if exploit['result']]))
}
for monkey in exploited]
From 96972aeac9c55266d835ba9fe1b30d9117821d85 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 28 Nov 2017 14:47:54 +0200
Subject: [PATCH 36/92] Micro segmentation, not port level segmentation
---
monkey_island/cc/ui/src/components/pages/ReportPage.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index 3966311f2..6c9c7cde3 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -342,7 +342,7 @@ class ReportPageComponent extends React.Component {
The monkey uncovered the following possible set of issues:
Lack of Micro-segmentation, machines successfully tunneled monkey activity using unused ports.
: null}
From da55b0b26ba0bbd5a4344fae90c0c7f53ab7fad1 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 28 Nov 2017 17:11:00 +0200
Subject: [PATCH 37/92] Group recommendations by machine. Show recommendation
with collapsible incident
---
monkey_island/cc/services/report.py | 9 +-
.../cc/ui/src/components/pages/ReportPage.js | 229 ++++++++++--------
.../report-components/CollapsibleWell.js | 30 +++
3 files changed, 168 insertions(+), 100 deletions(-)
create mode 100644 monkey_island/cc/ui/src/components/report-components/CollapsibleWell.js
diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py
index 5af051e72..01205e71a 100644
--- a/monkey_island/cc/services/report.py
+++ b/monkey_island/cc/services/report.py
@@ -252,8 +252,13 @@ class ReportService:
@staticmethod
def get_issues():
issues = ReportService.get_exploits() + ReportService.get_tunnels() + ReportService.get_cross_segment_issues()
- issues.sort(lambda x, y: 1 if x['machine'] > y['machine'] else -1 if x['machine'] < y['machine'] else 0)
- return issues
+ issues_dict = {}
+ for issue in issues:
+ machine = issue['machine']
+ if machine not in issues_dict:
+ issues_dict[machine] = []
+ issues_dict[machine].append(issue)
+ return issues_dict
@staticmethod
def get_report():
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index 6c9c7cde3..24c666c68 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -6,6 +6,7 @@ import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
import {options, edgeGroupToColor} from 'components/map/MapOptions';
import StolenPasswords from 'components/report-components/StolenPasswords';
import ScannedBreachedChart from 'components/report-components/ScannedBreachedChart';
+import CollapsableWellComponent from "../report-components/CollapsibleWell";
class ReportPageComponent extends React.Component {
@@ -70,20 +71,22 @@ class ReportPageComponent extends React.Component {
}
generateShellshockPathListBadges(paths) {
- return paths.map(path => {path});
+ return paths.map(path => {path});
}
generateSmbPasswordIssue(issue) {
return (
- The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SMB attack.
-
- The attack succeeded by authenticating over SMB protocol with user {issue.username} and its password.
-
- In order to protect the machine, the following steps should be performed:
-
-
Use a complex one-use password that is not shared with other computers on the network.
-
+ Change {issue.username}'s password to a complex one-use password
+ that is not shared with other computers on the network.
+
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SMB attack.
+
+ The attack succeeded by authenticating over SMB protocol with user {issue.username} and its password.
+
- The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SMB attack.
-
- The attack succeeded by using a pass-the-hash attack over SMB protocol with user {issue.username}.
-
- In order to protect the machine, the following steps should be performed:
-
-
Use a complex one-use password that is not shared with other computers on the network.
-
+ Change {issue.username}'s password to a complex one-use password
+ that is not shared with other computers on the network.
+
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SMB attack.
+
+ The attack succeeded by using a pass-the-hash attack over SMB protocol with user {issue.username}.
+
- The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a WMI attack.
-
- The attack succeeded by authenticating over WMI protocol with user {issue.username} and its password.
-
- In order to protect the machine, the following steps should be performed:
-
-
Use a complex one-use password that is not shared with other computers on the network.
-
+ Change {issue.username}'s password to a complex one-use password
+ that is not shared with other computers on the network.
+
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a WMI attack.
+
+ The attack succeeded by authenticating over WMI protocol with user {issue.username} and its password.
+
- The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a WMI attack.
-
- The attack succeeded by using a pass-the-hash attack over WMI protocol with user {issue.username}.
-
- In order to protect the machine, the following steps should be performed:
-
-
Use a complex one-use password that is not shared with other computers on the network.
-
+ Change {issue.username}'s password to a complex one-use password
+ that is not shared with other computers on the network.
+
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a WMI attack.
+
+ The attack succeeded by using a pass-the-hash attack over WMI protocol with user {issue.username}.
+
- The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SSH attack.
-
- The attack succeeded by authenticating over SSH protocol with user {issue.username} and its password.
-
- In order to protect the machine, the following steps should be performed:
-
-
Use a complex one-use password that is not shared with other computers on the network.
-
+ Change {issue.username}'s password to a complex one-use password
+ that is not shared with other computers on the network.
+
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SSH attack.
+
+ The attack succeeded by authenticating over SSH protocol with user {issue.username} and its password.
+
- The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a RDP attack.
-
- The attack succeeded by authenticating over RDP protocol with user {issue.username} and its password.
-
- In order to protect the machine, the following steps should be performed:
-
-
Use a complex one-use password that is not shared with other computers on the network.
-
+ Change {issue.username}'s password to a complex one-use password
+ that is not shared with other computers on the network.
+
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a RDP attack.
+
+ The attack succeeded by authenticating over RDP protocol with user {issue.username} and its password.
+
- The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SambaCry attack.
-
- The attack succeeded by authenticating over SMB protocol with user {issue.username} and its password, and by using the SambaCry vulnerability.
-
- In order to protect the machine, the following steps should be performed:
-
-
Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up.
-
Use a complex one-use password that is not shared with other computers on the network.
-
+ Change {issue.username}'s password to a complex one-use password
+ that is not shared with other computers on the network.
+
+ Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up.
+
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SambaCry attack.
+
+ The attack succeeded by authenticating over SMB protocol with user {issue.username} and its password, and by using the SambaCry
+ vulnerability.
+
- The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to an Elastic Groovy attack.
-
- The attack succeeded because the Elastic Search server was not parched against CVE-2015-1427.
-
- In order to protect the machine, the following steps should be performed:
-
-
Update your Elastic Search server to version 1.4.3 and up.
-
+ Update your Elastic Search server to version 1.4.3 and up.
+
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to an Elastic Groovy attack.
+
+ The attack succeeded because the Elastic Search server was not parched against CVE-2015-1427.
+
- The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a ShellShock attack.
-
- The attack succeeded because the HTTP server running on port {issue.port} was vulnerable to a shell injection attack on the paths: {this.generateShellshockPathListBadges(issue.paths)}.
-
- In order to protect the machine, the following steps should be performed:
-
-
Update your Bash to a ShellShock-patched version.
-
+ Update your Bash to a ShellShock-patched version.
+
+ The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a ShellShock attack.
+
+ The attack succeeded because the HTTP server running on port {issue.port} was vulnerable to a shell injection attack on the
+ paths: {this.generateShellshockPathListBadges(issue.paths)}.
+
- The machine {issue.machine} with the following address {issue.ip_address} was vulnerable to a Conficker attack.
-
- The attack succeeded because the target machine uses an outdated and unpatched operating system vulnerable to Conficker.
-
- In order to protect the machine, the following steps should be performed:
-
-
Install the latest Windows updates or upgrade to a newer operating system.
-
+ Install the latest Windows updates or upgrade to a newer operating system.
+
+ The machine {issue.machine} with the following address {issue.ip_address} was vulnerable to a Conficker attack.
+
+ The attack succeeded because the target machine uses an outdated and unpatched operating system vulnerable to
+ Conficker.
+
- The network can probably be segmented. A monkey instance on {issue.machine} in the networks {this.generateInfoBadges(issue.networks)} could directly access the Monkey Island C&C server in the networks {this.generateInfoBadges(issue.server_networks)}.
-
- In order to protect the network, the following steps should be performed:
-
-
Segment your network. Make sure machines can't access machines from other segments.
-
+ Segment your network. Make sure machines can't access machines from other segments.
+
+ The network can probably be segmented. A monkey instance on {issue.machine} in the
+ networks {this.generateInfoBadges(issue.networks)}
+ could directly access the Monkey Island C&C server in the
+ networks {this.generateInfoBadges(issue.server_networks)}.
+
- Machines are not locked down at port level. Network tunnel was set up from {issue.machine} to {issue.dest}.
-
- In order to protect the machine, the following steps should be performed:
-
-
Use micro-segmentation policies to disable communication other than the required.
-
+ Use micro-segmentation policies to disable communication other than the required.
+
+ Machines are not locked down at port level. Network tunnel was set up from {issue.machine} to {issue.dest}.
+
+ );
+ }
+}
+
+export default CollapsibleWellComponent;
From 881cf5e7931e08372131dc8d849f8409743641cd Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 28 Nov 2017 17:12:48 +0200
Subject: [PATCH 38/92] Collapsible-Collapsable
---
.../cc/ui/src/components/pages/ReportPage.js | 50 +++++++++----------
1 file changed, 25 insertions(+), 25 deletions(-)
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index 24c666c68..cea8c8d87 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -6,7 +6,7 @@ import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
import {options, edgeGroupToColor} from 'components/map/MapOptions';
import StolenPasswords from 'components/report-components/StolenPasswords';
import ScannedBreachedChart from 'components/report-components/ScannedBreachedChart';
-import CollapsableWellComponent from "../report-components/CollapsibleWell";
+import CollapsibleWellComponent from "../report-components/CollapsibleWell";
class ReportPageComponent extends React.Component {
@@ -79,14 +79,14 @@ class ReportPageComponent extends React.Component {
Change {issue.username}'s password to a complex one-use password
that is not shared with other computers on the network.
-
+
The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SMB attack.
The attack succeeded by authenticating over SMB protocol with user {issue.username} and its password.
-
+
Change {issue.username}'s password to a complex one-use password
that is not shared with other computers on the network.
-
+
The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SMB attack.
The attack succeeded by using a pass-the-hash attack over SMB protocol with user {issue.username}.
-
+
Change {issue.username}'s password to a complex one-use password
that is not shared with other computers on the network.
-
+
The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a WMI attack.
The attack succeeded by authenticating over WMI protocol with user {issue.username} and its password.
-
+
Change {issue.username}'s password to a complex one-use password
that is not shared with other computers on the network.
-
+
The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a WMI attack.
The attack succeeded by using a pass-the-hash attack over WMI protocol with user {issue.username}.
-
+
Change {issue.username}'s password to a complex one-use password
that is not shared with other computers on the network.
-
+
The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SSH attack.
The attack succeeded by authenticating over SSH protocol with user {issue.username} and its password.
-
+
Change {issue.username}'s password to a complex one-use password
that is not shared with other computers on the network.
-
+
The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a RDP attack.
The attack succeeded by authenticating over RDP protocol with user {issue.username} and its password.
-
+
);
}
@@ -183,7 +183,7 @@ class ReportPageComponent extends React.Component {
that is not shared with other computers on the network.
Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up.
-
+
The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a SambaCry attack.
@@ -191,7 +191,7 @@ class ReportPageComponent extends React.Component {
The attack succeeded by authenticating over SMB protocol with user {issue.username} and its password, and by using the SambaCry
vulnerability.
-
+
Update your Elastic Search server to version 1.4.3 and up.
-
+
The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to an Elastic Groovy attack.
The attack succeeded because the Elastic Search server was not parched against CVE-2015-1427.
-
+
Update your Bash to a ShellShock-patched version.
-
+
The machine {issue.machine} with the following IP address {issue.ip_address} was vulnerable to a ShellShock attack.
@@ -223,7 +223,7 @@ class ReportPageComponent extends React.Component {
The attack succeeded because the HTTP server running on port {issue.port} was vulnerable to a shell injection attack on the
paths: {this.generateShellshockPathListBadges(issue.paths)}.
-
+
Install the latest Windows updates or upgrade to a newer operating system.
-
+
The machine {issue.machine} with the following address {issue.ip_address} was vulnerable to a Conficker attack.
The attack succeeded because the target machine uses an outdated and unpatched operating system vulnerable to
Conficker.
-
+
Segment your network. Make sure machines can't access machines from other segments.
-
+
The network can probably be segmented. A monkey instance on {issue.machine} in the
networks {this.generateInfoBadges(issue.networks)}
could directly access the Monkey Island C&C server in the
networks {this.generateInfoBadges(issue.server_networks)}.
-
+
Use micro-segmentation policies to disable communication other than the required.
-
+
Machines are not locked down at port level. Network tunnel was set up from {issue.machine} to {issue.dest}.
-
+
);
}
From 88ea57dc8874e88f9ffb664b2f1e9470d190fcec Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 28 Nov 2017 17:16:58 +0200
Subject: [PATCH 39/92] Fix most printing format issues Improve CSS Shorten
lines
---
.../cc/ui/src/components/pages/ReportPage.js | 81 +++++++++++++------
monkey_island/cc/ui/src/styles/App.css | 63 +++++++++++----
2 files changed, 104 insertions(+), 40 deletions(-)
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index cea8c8d87..4e34dd3a8 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -3,10 +3,10 @@ import {Col} from 'react-bootstrap';
import BreachedServers from 'components/report-components/BreachedServers';
import ScannedServers from 'components/report-components/ScannedServers';
import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
-import {options, edgeGroupToColor} from 'components/map/MapOptions';
+import {edgeGroupToColor, options} from 'components/map/MapOptions';
import StolenPasswords from 'components/report-components/StolenPasswords';
import ScannedBreachedChart from 'components/report-components/ScannedBreachedChart';
-import CollapsibleWellComponent from "../report-components/CollapsibleWell";
+import CollapsibleWellComponent from 'components/report-components/CollapsibleWell';
class ReportPageComponent extends React.Component {
@@ -338,13 +338,16 @@ class ReportPageComponent extends React.Component {
} else {
content =
(
-
+
- Overview
+ Executive Summary
- The first monkey run was started on {this.state.report.overview.monkey_start_time}. After {this.state.report.overview.monkey_duration}, all monkeys finished propagation attempts.
+ The first monkey run was started on {this.state.report.overview.monkey_start_time}. After {this.state.report.overview.monkey_duration}, all monkeys finished
+ propagation attempts.
A full report of the Monkeys activities follows.
@@ -358,14 +361,31 @@ class ReportPageComponent extends React.Component {
Immediate Threats
- During this simulated attack the Monkey uncovered {this.state.report.overview.issues.filter(function(x){return x===true;}).length} issues, detailed below. The security issues uncovered include:
-
Lack of Micro-segmentation, machines successfully tunneled monkey activity
+ using unused ports.
: null}
@@ -394,33 +418,38 @@ class ReportPageComponent extends React.Component {
- The Monkey discovered {this.state.report.glance.scanned.length} machines and successfully breached {this.state.report.glance.exploited.length} of them.
-
- In addition, while attempting to exploit additional hosts , security software installed in the network should have picked up the attack attempts and logged them.
-
+ The Monkey discovered {this.state.report.glance.scanned.length} machines and
+ successfully breached {this.state.report.glance.exploited.length} of them.
+
+ In addition, while attempting to exploit additional hosts , security software installed in the
+ network should have picked up the attack attempts and logged them.
+
Detailed recommendations in the next part of the report.
-
+
From the attacker's point of view, the network looks like this:
@@ -416,25 +419,22 @@ class ReportPageComponent extends React.Component {
The Network from the Monkey's Eyes
-
-
- The Monkey discovered {this.state.report.glance.scanned.length} machines and
- successfully breached {this.state.report.glance.exploited.length} of them.
-
- In addition, while attempting to exploit additional hosts , security software installed in the
- network should have picked up the attack attempts and logged them.
-
- Detailed recommendations in the next part of the report.
-
-
-
-
-
-
-
+
+ The Monkey discovered {this.state.report.glance.scanned.length} machines and
+ successfully breached {this.state.report.glance.exploited.length} of them.
+
+ In addition, while attempting to exploit additional hosts , security software installed in the
+ network should have picked up the attack attempts and logged them.
+
+ Detailed recommendations in the next part of the report.
+
+
+
+ {Math.round(exploitPercentage)}% of machines exploited
+
Change {issue.username}'s password to a complex one-use password
that is not shared with other computers on the network.
@@ -87,13 +87,13 @@ class ReportPageComponent extends React.Component {
The attack succeeded by authenticating over SMB protocol with user {issue.username} and its password.
-
+
);
}
generateSmbPthIssue(issue) {
return (
-
+
Change {issue.username}'s password to a complex one-use password
that is not shared with other computers on the network.
@@ -104,13 +104,13 @@ class ReportPageComponent extends React.Component {
The attack succeeded by using a pass-the-hash attack over SMB protocol with user {issue.username}.
-
Change {issue.username}'s password to a complex one-use password
that is not shared with other computers on the network.
@@ -121,13 +121,13 @@ class ReportPageComponent extends React.Component {
The attack succeeded by authenticating over WMI protocol with user {issue.username} and its password.
-
+
);
}
generateWmiPthIssue(issue) {
return (
-
+
Change {issue.username}'s password to a complex one-use password
that is not shared with other computers on the network.
@@ -138,13 +138,13 @@ class ReportPageComponent extends React.Component {
The attack succeeded by using a pass-the-hash attack over WMI protocol with user {issue.username}.
-
+
);
}
generateSshIssue(issue) {
return (
-
+
Change {issue.username}'s password to a complex one-use password
that is not shared with other computers on the network.
@@ -155,13 +155,13 @@ class ReportPageComponent extends React.Component {
The attack succeeded by authenticating over SSH protocol with user {issue.username} and its password.
-
+
);
}
generateRdpIssue(issue) {
return (
-
+
Change {issue.username}'s password to a complex one-use password
that is not shared with other computers on the network.
@@ -172,13 +172,13 @@ class ReportPageComponent extends React.Component {
The attack succeeded by authenticating over RDP protocol with user {issue.username} and its password.
-
+
);
}
generateSambaCryIssue(issue) {
return (
-
+
Change {issue.username}'s password to a complex one-use password
that is not shared with other computers on the network.
@@ -192,13 +192,13 @@ class ReportPageComponent extends React.Component {
className="label label-success">{issue.username} and its password, and by using the SambaCry
vulnerability.
-
+
);
}
generateElasticIssue(issue) {
return (
-
+
Update your Elastic Search server to version 1.4.3 and up.
The machine {issue.machine} with the following IP address
The attack succeeded because the Elastic Search server was not parched against CVE-2015-1427.
-
Update your Bash to a ShellShock-patched version.
The machine {issue.machine} with the following IP address {issue.port} was vulnerable to a shell injection attack on the
paths: {this.generateShellshockPathListBadges(issue.paths)}.
-
+
);
}
generateConfickerIssue(issue) {
return (
-
+
Install the latest Windows updates or upgrade to a newer operating system.
The machine {issue.machine} with the following address
-
Segment your network. Make sure machines can't access machines from other segments.
The network can probably be segmented. A monkey instance on
-
+
);
}
generateTunnelIssue(issue) {
return (
-
+
Use micro-segmentation policies to disable communication other than the required.
Machines are not locked down at port level. Network tunnel was set up from {issue.machine} to {issue.dest}.
-
- The first monkey run was started on {this.state.report.overview.monkey_start_time}. After {this.state.report.overview.monkey_duration}, all monkeys finished
- propagation attempts.
-
-
- A full report of the Monkeys activities follows.
-
+
+
+
-
-
- Security Findings
-
-
-
- Immediate Threats
-
- During this simulated attack the Monkey uncovered {this.state.report.overview.issues.filter(function (x) {
- return x === true;
- }).length} issues, detailed below. The security issues uncovered include:
-
- The Monkey discovered {this.state.report.glance.scanned.length} machines and
- successfully breached {this.state.report.glance.exploited.length} of them.
-
- In addition, while attempting to exploit additional hosts , security software installed in the
- network should have picked up the attack attempts and logged them.
-
- Detailed recommendations in the next part of the report.
+
+
+ Critical security issues found by Infection Monkey!
+
) :
+ (
+
+ Infection Monkey did not find any critical security issues.
+
)
+ }
+
+
+ To improve the monkey's success rate, try adding users and passwords, and enabling the "Local
+ network scan" config value under "Basic - Network"
-
-
- {Math.round(exploitPercentage)}% of scanned machines exploited
+
+ The first monkey run was started on {this.state.report.overview.monkey_start_time}. After {this.state.report.overview.monkey_duration}, all monkeys finished
+ propagation attempts.
+
+
+ The monkey started propagating from the following machines where it was manually installed:
+
+ The Monkey discovered {this.state.report.glance.scanned.length} machines and
+ successfully breached {this.state.report.glance.exploited.length} of them.
+
+ In addition, while attempting to exploit additional hosts , security software installed in the
+ network should have picked up the attack attempts and logged them.
+
+ Detailed recommendations in the next part of the report.
+
+
+
+ {Math.round(exploitPercentage)}% of scanned machines exploited
+
+
+
+ From the attacker's point of view, the network looks like this:
+
+
+ Some monkeys are still running. To get the best report it's best to wait for all of them to finish
+ running.
+
)
+ }
-
+
To improve the monkey's success rate, try adding users and passwords, and enabling the "Local
network scan" config value under "Basic - Network"
From eadf8d0eba70aed0122903e02348f7ff5d31cbfa Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 12 Dec 2017 10:33:13 +0200
Subject: [PATCH 45/92] Add support for both supplying monkey island dir or
using current directory as monkey island dir Fix openssl.cnf not found
---
monkey_island/windows/create_certificate.bat | 22 +++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/monkey_island/windows/create_certificate.bat b/monkey_island/windows/create_certificate.bat
index ac6555f0b..18738726b 100644
--- a/monkey_island/windows/create_certificate.bat
+++ b/monkey_island/windows/create_certificate.bat
@@ -1,3 +1,19 @@
-bin\openssl\openssl.exe genrsa -out cc\server.key 1024
-bin\openssl\openssl.exe req -new -config bin\openssl\openssl.cfg -key cc\server.key -out cc\server.csr -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com"
-bin\openssl\openssl.exe x509 -req -days 366 -in cc\server.csr -signkey cc\server.key -out cc\server.crt
\ No newline at end of file
+@echo off
+
+SET OPENSSL_CONF=bin\openssl\openssl.cfg
+
+IF [%1] == [] (
+ set dir=%cd%\
+) ELSE (
+ set dir=%1
+ REM - Remove double quotes -
+ set dir=%dir:"=%
+)
+
+echo Monkey Island folder: %dir%
+
+@echo on
+
+"%dir%bin\openssl\openssl.exe" genrsa -out "%dir%cc\server.key" 1024
+"%dir%bin\openssl\openssl.exe" req -new -config "%dir%bin\openssl\openssl.cfg" -key "%dir%cc\server.key" -out "%dir%cc\server.csr" -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com"
+"%dir%bin\openssl\openssl.exe" x509 -req -days 366 -in "%dir%cc\server.csr" -signkey "%dir%cc\server.key" -out "%dir%cc\server.crt"
\ No newline at end of file
From 1f2a0c0e21889f967c7eb465b081e65cc4aee8bd Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 12 Dec 2017 11:34:51 +0200
Subject: [PATCH 46/92] add openssl.cfg to git and update path to it on
create_certificate.bat
---
monkey_island/windows/create_certificate.bat | 2 +-
monkey_island/windows/openssl.cfg | 350 +++++++++++++++++++
2 files changed, 351 insertions(+), 1 deletion(-)
create mode 100644 monkey_island/windows/openssl.cfg
diff --git a/monkey_island/windows/create_certificate.bat b/monkey_island/windows/create_certificate.bat
index 18738726b..cd1aaaa58 100644
--- a/monkey_island/windows/create_certificate.bat
+++ b/monkey_island/windows/create_certificate.bat
@@ -1,6 +1,6 @@
@echo off
-SET OPENSSL_CONF=bin\openssl\openssl.cfg
+SET OPENSSL_CONF=windows\openssl.cfg
IF [%1] == [] (
set dir=%cd%\
diff --git a/monkey_island/windows/openssl.cfg b/monkey_island/windows/openssl.cfg
new file mode 100644
index 000000000..1eb86c401
--- /dev/null
+++ b/monkey_island/windows/openssl.cfg
@@ -0,0 +1,350 @@
+#
+# OpenSSL example configuration file.
+# This is mostly being used for generation of certificate requests.
+#
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+
+# Extra OBJECT IDENTIFIER info:
+#oid_file = $ENV::HOME/.oid
+oid_section = new_oids
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions =
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca', 'req' and 'ts'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+# Policies used by the TSA examples.
+tsa_policy1 = 1.2.3.4.1
+tsa_policy2 = 1.2.3.4.5.6
+tsa_policy3 = 1.2.3.4.5.7
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = ./demoCA # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+#unique_subject = no # Set to 'no' to allow creation of
+ # several ctificates with same subject.
+new_certs_dir = $dir/newcerts # default place for new certs.
+
+certificate = $dir/cacert.pem # The CA certificate
+serial = $dir/serial # The current serial number
+crlnumber = $dir/crlnumber # the current crl number
+ # must be commented out to leave a V1 CRL
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/cakey.pem# The private key
+RANDFILE = $dir/private/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+# Comment out the following two lines for the "traditional"
+# (and highly broken) format.
+name_opt = ca_default # Subject Name options
+cert_opt = ca_default # Certificate field options
+
+# Extension copying option: use with caution.
+# copy_extensions = copy
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crlnumber must also be commented out to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 365 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = default # use public key default MD
+preserve = no # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = 2048
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString (PKIX recommendation before 2004)
+# utf8only: only UTF8Strings (PKIX recommendation after 2004).
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings.
+string_mask = utf8only
+
+# req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = AU
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = Some-State
+
+localityName = Locality Name (eg, city)
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = Internet Widgits Pty Ltd
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+
+commonName = Common Name (e.g. server FQDN or YOUR name)
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+
+# SET-ex3 = SET extension number 3
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "OpenSSL Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+# An alternative to produce certificates that aren't
+# deprecated according to PKIX.
+# subjectAltName=email:move
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+# This is required for TSA certificates.
+# extendedKeyUsage = critical,timeStamping
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+
+# PKIX recommendation.
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ proxy_cert_ext ]
+# These extensions should be added when creating a proxy certificate
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "OpenSSL Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+# An alternative to produce certificates that aren't
+# deprecated according to PKIX.
+# subjectAltName=email:move
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+# This really needs to be in place for it to be a proxy certificate.
+proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
+
+####################################################################
+[ tsa ]
+
+default_tsa = tsa_config1 # the default TSA section
+
+[ tsa_config1 ]
+
+# These are used by the TSA reply generation only.
+dir = ./demoCA # TSA root directory
+serial = $dir/tsaserial # The current serial number (mandatory)
+crypto_device = builtin # OpenSSL engine to use for signing
+signer_cert = $dir/tsacert.pem # The TSA signing certificate
+ # (optional)
+certs = $dir/cacert.pem # Certificate chain to include in reply
+ # (optional)
+signer_key = $dir/private/tsakey.pem # The TSA private key (optional)
+
+default_policy = tsa_policy1 # Policy if request did not specify it
+ # (optional)
+other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional)
+digests = md5, sha1 # Acceptable message digests (mandatory)
+accuracy = secs:1, millisecs:500, microsecs:100 # (optional)
+clock_precision_digits = 0 # number of digits after dot. (optional)
+ordering = yes # Is ordering defined for timestamps?
+ # (optional, default: no)
+tsa_name = yes # Must the TSA name be included in the reply?
+ # (optional, default: no)
+ess_cert_id_chain = no # Must the ESS cert id chain be included?
+ # (optional, default: no)
From c8e553721f7972c4de1abdce01a52baf7a87396d Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 12 Dec 2017 12:07:37 +0200
Subject: [PATCH 47/92] Report content fix
---
monkey_island/cc/services/report.py | 2 +-
.../cc/ui/src/components/pages/ReportPage.js | 16 ++++++++--------
.../report-components/StolenPasswords.js | 2 +-
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py
index 830197444..048e2fb12 100644
--- a/monkey_island/cc/services/report.py
+++ b/monkey_island/cc/services/report.py
@@ -89,7 +89,7 @@ class ReportService:
@staticmethod
def get_stolen_creds():
- PASS_TYPE_DICT = {'password': 'Clear Password', 'lm_hash': 'LM', 'ntlm_hash': 'NTLM'}
+ PASS_TYPE_DICT = {'password': 'Clear Password', 'lm_hash': 'LM hash', 'ntlm_hash': 'NTLM hash'}
creds = []
for telem in mongo.db.telemetry.find(
{'telem_type': 'system_info_collection', 'data.credentials': {'$exists': True}},
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index e29d9388f..f1daa6d3c 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -367,11 +367,11 @@ class ReportPageComponent extends React.Component {
this.state.report.glance.exploited.length > 0 ?
(
- Critical security issues found by Infection Monkey!
+ Critical security issues were detected!
) :
(
- Infection Monkey did not find any critical security issues.
+ No critical security issues were detected.
- To improve the monkey's success rate, try adding users and passwords, and enabling the "Local
- network scan" config value under "Basic - Network"
+ To improve the monkey's detection rates, try adding users and passwords and enable the "Local network
+ scan" config value under Basic - Network.
The first monkey run was started on 0 ?
- Use the following exploit methods:
+ Used the following exploit methods:
Elastic Search servers not patched for CVE-2015-1427.
diff --git a/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js b/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js
index 7c42b6ea5..fde46f85a 100644
--- a/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js
+++ b/monkey_island/cc/ui/src/components/report-components/StolenPasswords.js
@@ -7,7 +7,7 @@ const columns = [
columns: [
{ Header: 'Username', accessor: 'username'},
{ Header: 'Type', accessor: 'type'},
- { Header: 'Origin', accessor: 'origin'}
+ { Header: 'Stolen From', accessor: 'origin'}
]
}
];
From 2c8b510b0c16e42ab73b8f31e25c8c2dbf38ac86 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 12 Dec 2017 12:45:53 +0200
Subject: [PATCH 48/92] Exploits used are listed only if they're not the
default configuration. Suggestion to improve monkey success rate appears only
if no critical issues were found
---
monkey_island/cc/services/report.py | 11 ++++-
.../cc/ui/src/components/pages/ReportPage.js | 45 ++++++++++++++-----
2 files changed, 43 insertions(+), 13 deletions(-)
diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py
index 048e2fb12..6b0e408ce 100644
--- a/monkey_island/cc/services/report.py
+++ b/monkey_island/cc/services/report.py
@@ -276,6 +276,15 @@ class ReportService:
@staticmethod
def get_config_exploits():
+ exploits_config_value = ['exploits', 'general', 'exploiter_classes']
+ default_exploits = ConfigService.get_default_config()
+ for namespace in exploits_config_value:
+ default_exploits = default_exploits[namespace]
+ exploits = ConfigService.get_config_value(exploits_config_value)
+
+ if exploits == default_exploits:
+ return ['default']
+
exploit_display_dict = \
{
'SmbExploiter': 'SMB Exploiter',
@@ -288,7 +297,7 @@ class ReportService:
'ShellShockExploiter': 'ShellShock Exploiter',
}
return [exploit_display_dict[exploit] for exploit in
- ConfigService.get_config_value(['exploits', 'general', 'exploiter_classes'])]
+ exploits]
@staticmethod
def get_config_ips():
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index f1daa6d3c..2a12587f6 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -330,7 +330,7 @@ class ReportPageComponent extends React.Component {
generateIssues = (issues) => {
let issuesDivArray = [];
- for (var machine of Object.keys(issues)) {
+ for (let machine of Object.keys(issues)) {
issuesDivArray.push(
{machine}
@@ -343,6 +343,17 @@ class ReportPageComponent extends React.Component {
return
{issuesDivArray}
;
};
+ didMonkeyFindIssues = () => {
+ for (let issue of Object.keys(this.state.report.overview.issues)) {
+ if (this.state.report.overview.issues[issue]) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+
+
render() {
let content;
if (Object.keys(this.state.report).length === 0) {
@@ -384,11 +395,16 @@ class ReportPageComponent extends React.Component {
running.
)
}
-
-
- To improve the monkey's detection rates, try adding users and passwords and enable the "Local network
- scan" config value under Basic - Network.
-
+ {
+ this.didMonkeyFindIssues() ?
+ ''
+ :
+
+
+ To improve the monkey's detection rates, try adding users and passwords and enable the "Local network
+ scan" config value under Basic - Network.
+
+ }
The first monkey run was started on {this.state.report.overview.monkey_start_time}. After 0 ?
-
- To improve the monkey's detection rates, try adding users and passwords and enable the "Local network
+ To improve the monkey's detection rates, try adding users and passwords and enable the "Local
+ network
scan" config value under Basic - Network.
+
+ You have to run a monkey before generating a report!
+
;
+ }
} else {
let exploitPercentage =
(100 * this.state.report.glance.exploited.length) / this.state.report.glance.scanned.length;
From f2b631745d34f389a52c5b0702b0c7e38a4cf54e Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 12 Dec 2017 15:45:32 +0200
Subject: [PATCH 53/92] Fix bug where stolen credentials had '.' in username
---
monkey_island/cc/resources/telemetry.py | 14 ++++++++++++--
monkey_island/cc/services/report.py | 15 +++++++++------
2 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/monkey_island/cc/resources/telemetry.py b/monkey_island/cc/resources/telemetry.py
index 666bfc16c..7b3a6e616 100644
--- a/monkey_island/cc/resources/telemetry.py
+++ b/monkey_island/cc/resources/telemetry.py
@@ -39,7 +39,6 @@ class Telemetry(flask_restful.Resource):
telemetry_json = json.loads(request.data)
telemetry_json['timestamp'] = datetime.now()
- telem_id = mongo.db.telemetry.insert(telemetry_json)
monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])
try:
@@ -53,6 +52,7 @@ class Telemetry(flask_restful.Resource):
print("Exception caught while processing telemetry: %s" % str(ex))
traceback.print_exc()
+ telem_id = mongo.db.telemetry.insert(telemetry_json)
return mongo.db.telemetry.find_one_or_404({"_id": telem_id})
@staticmethod
@@ -70,6 +70,11 @@ class Telemetry(flask_restful.Resource):
monkey_label = telem_monkey_guid
x["monkey"] = monkey_label
objects.append(x)
+ if x['telem_type'] == 'system_info_collection' and 'credentials' in x['data']:
+ for user in x['data']['credentials']:
+ if -1 != user.find(','):
+ new_user = user.replace(',', '.')
+ x['data']['credentials'][new_user] = x['data']['credentials'].pop(user)
return objects
@@ -159,7 +164,6 @@ class Telemetry(flask_restful.Resource):
creds = telemetry_json['data']['credentials']
for user in creds:
ConfigService.creds_add_username(user)
- creds[user]['user'] = user
if 'password' in creds[user]:
ConfigService.creds_add_password(creds[user]['password'])
if 'lm_hash' in creds[user]:
@@ -167,11 +171,17 @@ class Telemetry(flask_restful.Resource):
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)
+
@staticmethod
def process_trace_telemetry(telemetry_json):
# Nothing to do
return
+
TELEM_PROCESS_DICT = \
{
'tunnel': Telemetry.process_tunnel_telemetry,
diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py
index 8261b7e7b..ab6c9fb13 100644
--- a/monkey_island/cc/services/report.py
+++ b/monkey_island/cc/services/report.py
@@ -117,7 +117,7 @@ class ReportService:
for pass_type in monkey_creds[user]:
creds.append(
{
- 'username': user,
+ 'username': user.replace(',', '.'),
'type': PASS_TYPE_DICT[pass_type],
'origin': origin
}
@@ -231,14 +231,17 @@ class ReportService:
@staticmethod
def get_monkey_subnets(monkey_guid):
+ network_info = mongo.db.telemetry.find_one(
+ {'telem_type': 'system_info_collection', 'monkey_guid': monkey_guid},
+ {'data.network_info.networks': 1}
+ )
+ if network_info is None:
+ return []
+
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']
+ for network in network_info['data']['network_info']['networks']
]
@staticmethod
From 434c72f69f808bf9f50e2988644762a05667ef91 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 12 Dec 2017 16:33:16 +0200
Subject: [PATCH 54/92] Implemented issues and warnings on overview
---
monkey_island/cc/services/report.py | 68 ++++++++++++--
.../cc/ui/src/components/pages/ReportPage.js | 94 ++++++++++++-------
2 files changed, 119 insertions(+), 43 deletions(-)
diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py
index ab6c9fb13..4b3bf8573 100644
--- a/monkey_island/cc/services/report.py
+++ b/monkey_island/cc/services/report.py
@@ -25,6 +25,18 @@ class ReportService:
'ShellShockExploiter': 'ShellShock Exploiter',
}
+ class ISSUES_DICT:
+ WEAK_PASSWORD = 0
+ STOLEN_CREDS = 1
+ ELASTIC = 2
+ SAMBACRY = 3
+ SHELLSHOCK = 4
+ CONFICKER = 5
+
+ class WARNINGS_DICT:
+ CROSS_SEGMENT = 0
+ TUNNEL = 1
+
@staticmethod
def get_first_monkey_time():
return mongo.db.telemetry.find({}, {'timestamp': 1}).sort([('$natural', 1)]).limit(1)[0]['timestamp']
@@ -139,6 +151,7 @@ class ReportService:
processed_exploit['username'] = attempt['user']
if len(attempt['password']) > 0:
processed_exploit['type'] = 'password'
+ processed_exploit['password'] = attempt['password']
else:
processed_exploit['type'] = 'hash'
return processed_exploit
@@ -232,9 +245,9 @@ class ReportService:
@staticmethod
def get_monkey_subnets(monkey_guid):
network_info = mongo.db.telemetry.find_one(
- {'telem_type': 'system_info_collection', 'monkey_guid': monkey_guid},
- {'data.network_info.networks': 1}
- )
+ {'telem_type': 'system_info_collection', 'monkey_guid': monkey_guid},
+ {'data.network_info.networks': 1}
+ )
if network_info is None:
return []
@@ -315,22 +328,61 @@ class ReportService:
def get_config_scan():
return ConfigService.get_config_value(['basic_network', 'general', 'local_network_scan'], True)
+ @staticmethod
+ def get_issues_overview(issues, config_users, config_passwords):
+ issues_byte_array = [False] * 6
+
+ for machine in issues:
+ for issue in issues[machine]:
+ if issue['type'] == 'elastic':
+ issues_byte_array[ReportService.ISSUES_DICT.ELASTIC] = True
+ elif issue['type'] == 'sambacry':
+ issues_byte_array[ReportService.ISSUES_DICT.SAMBACRY] = True
+ elif issue['type'] == 'shellshock':
+ issues_byte_array[ReportService.ISSUES_DICT.SHELLSHOCK] = True
+ elif issue['type'] == 'conficker':
+ issues_byte_array[ReportService.ISSUES_DICT.CONFICKER] = 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] = True
+ elif issue['type'].endswith('_pth') or issue['type'].endswith('_password'):
+ issues_byte_array[ReportService.ISSUES_DICT.STOLEN_CREDS] = True
+
+ return issues_byte_array
+
+ @staticmethod
+ def get_warnings_overview(issues):
+ warnings_byte_array = [False] * 2
+
+ for machine in issues:
+ for issue in issues[machine]:
+ if issue['type'] == 'cross_segment':
+ warnings_byte_array[ReportService.WARNINGS_DICT.CROSS_SEGMENT] = True
+ elif issue['type'] == 'tunnel':
+ warnings_byte_array[ReportService.WARNINGS_DICT.TUNNEL] = True
+
+ return warnings_byte_array
+
@staticmethod
def get_report():
+ issues = ReportService.get_issues()
+ config_users = ReportService.get_config_users()
+ config_passwords = ReportService.get_config_passwords()
+
return \
{
'overview':
{
'manual_monkeys': ReportService.get_manual_monkeys(),
- 'config_users': ReportService.get_config_users(),
- 'config_passwords': ReportService.get_config_passwords(),
+ 'config_users': config_users,
+ 'config_passwords': config_passwords,
'config_exploits': ReportService.get_config_exploits(),
'config_ips': ReportService.get_config_ips(),
'config_scan': ReportService.get_config_scan(),
'monkey_start_time': ReportService.get_first_monkey_time().strftime("%d/%m/%Y %H:%M:%S"),
'monkey_duration': ReportService.get_monkey_duration(),
- 'issues': [False, True, True, True, False, True],
- 'warnings': [True, True]
+ 'issues': ReportService.get_issues_overview(issues, config_users, config_passwords),
+ 'warnings': ReportService.get_warnings_overview(issues)
},
'glance':
{
@@ -340,7 +392,7 @@ class ReportService:
},
'recommendations':
{
- 'issues': ReportService.get_issues()
+ 'issues': issues
}
}
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index a058ce516..e5e1fd3af 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -498,46 +498,70 @@ class ReportPageComponent extends React.Component {
Immediate Threats
- During this simulated attack the Monkey uncovered {this.state.report.overview.issues.filter(function (x) {
- return x === true;
- }).length} issues:
-
Lack of Micro-segmentation, machines successfully tunneled monkey activity
+ using unused ports.
: null}
+
+
+ :
+
+ The monkey did not find any issues.
+
+ }
From 8ed439e24ed3ec7563a4acc61458d25775f94016 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 12 Dec 2017 16:39:38 +0200
Subject: [PATCH 55/92] Remove irrelevant sentence+link
---
monkey_island/cc/ui/src/components/pages/ReportPage.js | 2 --
1 file changed, 2 deletions(-)
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index e5e1fd3af..2d3022adc 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -585,8 +585,6 @@ class ReportPageComponent extends React.Component {
In addition, while attempting to exploit additional hosts , security software installed in the
network should have picked up the attack attempts and logged them.
-
- Detailed recommendations in the next part of the report.
Date: Tue, 12 Dec 2017 17:05:57 +0200
Subject: [PATCH 56/92] Show minimal info on services. Make optimization for
machine label on report
---
monkey_island/cc/services/edge.py | 19 +++++++++++--------
monkey_island/cc/services/node.py | 18 ++++++++++--------
monkey_island/cc/services/report.py | 17 ++++++++---------
3 files changed, 29 insertions(+), 25 deletions(-)
diff --git a/monkey_island/cc/services/edge.py b/monkey_island/cc/services/edge.py
index 308a57e55..520808be8 100644
--- a/monkey_island/cc/services/edge.py
+++ b/monkey_island/cc/services/edge.py
@@ -11,22 +11,22 @@ class EdgeService:
pass
@staticmethod
- def get_displayed_edge_by_id(edge_id):
+ def get_displayed_edge_by_id(edge_id, for_report=False):
edge = mongo.db.edge.find({"_id": ObjectId(edge_id)})[0]
- return EdgeService.edge_to_displayed_edge(edge)
+ return EdgeService.edge_to_displayed_edge(edge, for_report)
@staticmethod
- def get_displayed_edges_by_to(to):
+ def get_displayed_edges_by_to(to, for_report=False):
edges = mongo.db.edge.find({"to": ObjectId(to)})
- return [EdgeService.edge_to_displayed_edge(edge) for edge in edges]
+ return [EdgeService.edge_to_displayed_edge(edge, for_report) for edge in edges]
@staticmethod
- def edge_to_displayed_edge(edge):
+ def edge_to_displayed_edge(edge, for_report=False):
services = []
os = {}
if len(edge["scans"]) > 0:
- services = EdgeService.services_to_displayed_services(edge["scans"][-1]["data"]["services"])
+ services = EdgeService.services_to_displayed_services(edge["scans"][-1]["data"]["services"], for_report)
os = edge["scans"][-1]["data"]["os"]
displayed_edge = EdgeService.edge_to_net_edge(edge)
@@ -104,8 +104,11 @@ class EdgeService:
return edges
@staticmethod
- def services_to_displayed_services(services):
- return [x + ": " + (services[x]['name'] if services[x].has_key('name') else 'unknown') for x in services]
+ def services_to_displayed_services(services, for_report=False):
+ if for_report:
+ return [x for x in services]
+ else:
+ return [x + ": " + (services[x]['name'] if 'name' in services[x] else 'unknown') for x in services]
@staticmethod
def edge_to_net_edge(edge):
diff --git a/monkey_island/cc/services/node.py b/monkey_island/cc/services/node.py
index 21c2ef194..a6ce09438 100644
--- a/monkey_island/cc/services/node.py
+++ b/monkey_island/cc/services/node.py
@@ -12,11 +12,11 @@ class NodeService:
pass
@staticmethod
- def get_displayed_node_by_id(node_id):
+ def get_displayed_node_by_id(node_id, for_report=False):
if ObjectId(node_id) == NodeService.get_monkey_island_pseudo_id():
return NodeService.get_monkey_island_node()
- edges = EdgeService.get_displayed_edges_by_to(node_id)
+ edges = EdgeService.get_displayed_edges_by_to(node_id, for_report)
accessible_from_nodes = []
exploits = []
@@ -29,14 +29,14 @@ class NodeService:
return new_node
# node is infected
- new_node = NodeService.monkey_to_net_node(monkey)
+ new_node = NodeService.monkey_to_net_node(monkey, for_report)
for key in monkey:
if key not in ['_id', 'modifytime', 'parent', 'dead', 'description']:
new_node[key] = monkey[key]
else:
# node is uninfected
- new_node = NodeService.node_to_net_node(node)
+ new_node = NodeService.node_to_net_node(node, for_report)
new_node["ip_addresses"] = node["ip_addresses"]
for edge in edges:
@@ -119,22 +119,24 @@ class NodeService:
return "%s_%s" % (node_type, node_os)
@staticmethod
- def monkey_to_net_node(monkey):
+ def monkey_to_net_node(monkey, for_report=False):
+ label = monkey['hostname'] if for_report else NodeService.get_monkey_label(monkey)
return \
{
"id": monkey["_id"],
- "label": NodeService.get_monkey_label(monkey),
+ "label": label,
"group": NodeService.get_monkey_group(monkey),
"os": NodeService.get_monkey_os(monkey),
"dead": monkey["dead"],
}
@staticmethod
- def node_to_net_node(node):
+ def node_to_net_node(node, for_report=False):
+ label = node['os']['version'] if for_report else NodeService.get_node_label(node)
return \
{
"id": node["_id"],
- "label": NodeService.get_node_label(node),
+ "label": label,
"group": NodeService.get_node_group(node),
"os": NodeService.get_node_os(node)
}
diff --git a/monkey_island/cc/services/report.py b/monkey_island/cc/services/report.py
index 4b3bf8573..ac445ac6a 100644
--- a/monkey_island/cc/services/report.py
+++ b/monkey_island/cc/services/report.py
@@ -76,17 +76,16 @@ class ReportService:
@staticmethod
def get_scanned():
nodes = \
- [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(node['_id'], True) for node in mongo.db.node.find({}, {'_id': 1})] \
+ + [NodeService.get_displayed_node_by_id(monkey['_id'], True) for monkey in mongo.db.monkey.find({}, {'_id': 1})]
nodes = [
{
- 'label':
- node['hostname'] if 'hostname' in node else NodeService.get_node_by_id(node['id'])['os']['version'],
+ 'label': node['label'],
'ip_addresses': node['ip_addresses'],
'accessible_from_nodes':
(x['hostname'] for x in
- (NodeService.get_displayed_node_by_id(edge['from'])
- for edge in EdgeService.get_displayed_edges_by_to(node['id']))),
+ (NodeService.get_displayed_node_by_id(edge['from'], True)
+ for edge in EdgeService.get_displayed_edges_by_to(node['id'], True))),
'services': node['services']
}
for node in nodes]
@@ -96,14 +95,14 @@ class ReportService:
@staticmethod
def get_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'], True) for monkey in mongo.db.monkey.find({}, {'_id': 1})
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'], True)
for node in mongo.db.node.find({'exploited': True}, {'_id': 1})]
exploited = [
{
- 'label': NodeService.get_node_hostname(NodeService.get_node_or_monkey_by_id(monkey['id'])),
+ 'label': monkey['label'],
'ip_addresses': monkey['ip_addresses'],
'exploits': list(set(
[ReportService.EXPLOIT_DISPLAY_DICT[exploit['exploiter']] for exploit in monkey['exploits'] if
From b0547c4f7a4c38abe80c1b55846f84018cbd8419 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 19 Dec 2017 17:58:07 +0200
Subject: [PATCH 57/92] Add legend to report map
---
monkey_island/cc/ui/src/components/pages/ReportPage.js | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index 2d3022adc..1ef8b2f30 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -596,6 +596,16 @@ class ReportPageComponent extends React.Component {
From the attacker's point of view, the network looks like this:
+
+ Legend:
+ Exploit
+ |
+ Scan
+ |
+ Tunnel
+ |
+ Island Communication
+
Lack of Micro-segmentation, machines successfully tunneled monkey activity
+
Lack of machine hardening, machines successfully tunneled monkey traffic
using unused ports.
: null}
From 1f571503ade12e6c1ffb8a08d2150dc3a396bacd Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 19 Dec 2017 18:02:43 +0200
Subject: [PATCH 59/92] Fix bug in create_certificate.bat
---
monkey_island/windows/create_certificate.bat | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/monkey_island/windows/create_certificate.bat b/monkey_island/windows/create_certificate.bat
index cd1aaaa58..0af3e9960 100644
--- a/monkey_island/windows/create_certificate.bat
+++ b/monkey_island/windows/create_certificate.bat
@@ -1,19 +1,18 @@
@echo off
-SET OPENSSL_CONF=windows\openssl.cfg
-
IF [%1] == [] (
- set dir=%cd%\
+ set mydir=%cd%\
) ELSE (
- set dir=%1
- REM - Remove double quotes -
- set dir=%dir:"=%
+ set mydir=%~1%
)
-echo Monkey Island folder: %dir%
+echo Monkey Island folder: %mydir%
+
+SET OPENSSL_CONF=%mydir%bin\openssl\openssl.cfg
+copy "%mydir%windows\openssl.cfg" "%mydir%bin\openssl\openssl.cfg"
@echo on
-"%dir%bin\openssl\openssl.exe" genrsa -out "%dir%cc\server.key" 1024
-"%dir%bin\openssl\openssl.exe" req -new -config "%dir%bin\openssl\openssl.cfg" -key "%dir%cc\server.key" -out "%dir%cc\server.csr" -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com"
-"%dir%bin\openssl\openssl.exe" x509 -req -days 366 -in "%dir%cc\server.csr" -signkey "%dir%cc\server.key" -out "%dir%cc\server.crt"
\ No newline at end of file
+"%mydir%bin\openssl\openssl.exe" genrsa -out "%mydir%cc\server.key" 1024
+"%mydir%bin\openssl\openssl.exe" req -new -config "%mydir%bin\openssl\openssl.cfg" -key "%mydir%cc\server.key" -out "%mydir%cc\server.csr" -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com"
+"%mydir%bin\openssl\openssl.exe" x509 -req -days 366 -in "%mydir%cc\server.csr" -signkey "%mydir%cc\server.key" -out "%mydir%cc\server.crt"
\ No newline at end of file
From c4f9f774afdf90e53a16213ecd1d8ecf27d42368 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 19 Dec 2017 18:43:20 +0200
Subject: [PATCH 60/92] Fix bug running from path with spaces
---
monkey_island/cc/resources/local_run.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/monkey_island/cc/resources/local_run.py b/monkey_island/cc/resources/local_run.py
index 2fe7be3d1..3f11eb2a4 100644
--- a/monkey_island/cc/resources/local_run.py
+++ b/monkey_island/cc/resources/local_run.py
@@ -36,9 +36,11 @@ def run_local_monkey():
# run the monkey
try:
- args = ["%s m0nk3y -s %s:%s" % (target_path, local_ip_addresses()[0], ISLAND_PORT)]
if sys.platform == "win32":
+ args = ['"%s" m0nk3y -s %s:%s' % (target_path, local_ip_addresses()[0], ISLAND_PORT)]
args = "".join(args)
+ else:
+ args = ['%s m0nk3y -s %s:%s' % (target_path, local_ip_addresses()[0], ISLAND_PORT)]
pid = subprocess.Popen(args, shell=True).pid
except Exception as exc:
return False, "popen failed: %s" % exc
From 0c286a3419b91556395406869e684acd8cc759c8 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 19 Dec 2017 19:02:10 +0200
Subject: [PATCH 61/92] Remove statement if there were no infections
---
.../cc/ui/src/components/pages/ReportPage.js | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index 50e0921cf..d9f3255e2 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -582,10 +582,16 @@ class ReportPageComponent extends React.Component {
className="label label-warning">{this.state.report.glance.scanned.length} machines and
successfully breached {this.state.report.glance.exploited.length} of them.
-
- In addition, while attempting to exploit additional hosts , security software installed in the
- network should have picked up the attack attempts and logged them.
+ {
+ this.state.report.glance.exploited.length > 0 ?
+
+ In addition, while attempting to exploit additional hosts , security software installed in the
+ network should have picked up the attack attempts and logged them.
+
+ :
+ ''
+ }
Date: Tue, 19 Dec 2017 20:35:06 +0200
Subject: [PATCH 62/92] Remove unecessary paragraph
---
monkey_island/cc/ui/src/components/pages/ReportPage.js | 3 ---
1 file changed, 3 deletions(-)
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index d9f3255e2..f85698f31 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -486,9 +486,6 @@ class ReportPageComponent extends React.Component {
Monkeys were configured to avoid scanning of the local network.
}
-
- A full report of the Monkeys activities follows.
-
- In addition, while attempting to exploit additional hosts , security software installed in the
+ In addition, while attempting to exploit additional hosts, security software installed in the
network should have picked up the attack attempts and logged them.
:
@@ -603,13 +604,13 @@ class ReportPageComponent extends React.Component {
Legend:
- Exploit
+ Exploit |
- Scan
+ Scan |
- Tunnel
+ Tunnel |
- Island Communication
+ Island Communication
@@ -628,7 +629,7 @@ class ReportPageComponent extends React.Component {
For questions, suggestions or any other feedback
contact: labs@guardicore.com
labs@guardicore.com
-
+
From 4af4178344d64031c47506630d02af7fa5d7898b Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sun, 31 Dec 2017 13:46:07 +0200
Subject: [PATCH 69/92] Add logging, turn mimikatz into modern class.
---
chaos_monkey/system_info/__init__.py | 12 +++++++++---
chaos_monkey/system_info/linux_info_collector.py | 10 ++++++++++
chaos_monkey/system_info/mimikatz_collector.py | 7 ++++---
chaos_monkey/system_info/windows_info_collector.py | 14 +++++++++++++-
4 files changed, 36 insertions(+), 7 deletions(-)
diff --git a/chaos_monkey/system_info/__init__.py b/chaos_monkey/system_info/__init__.py
index 0a5bf8e31..126854b8e 100644
--- a/chaos_monkey/system_info/__init__.py
+++ b/chaos_monkey/system_info/__init__.py
@@ -1,3 +1,4 @@
+import logging
import socket
import sys
@@ -6,6 +7,8 @@ from enum import IntEnum
from network.info import get_host_subnets
+LOG = logging.getLogger(__name__)
+
# Linux doesn't have WindowsError
try:
WindowsError
@@ -56,8 +59,9 @@ class InfoCollector(object):
def get_hostname(self):
"""
Adds the fully qualified computer hostname to the system information.
- :return: Nothing
+ :return: None. Updates class information
"""
+ LOG.debug("Reading hostname")
self.info['hostname'] = socket.getfqdn()
def get_process_list(self):
@@ -65,8 +69,9 @@ class InfoCollector(object):
Adds process information from the host to the system information.
Currently lists process name, ID, parent ID, command line
and the full image path of each process.
- :return: Nothing
+ :return: None. Updates class information
"""
+ LOG.debug("Reading process list")
processes = {}
for process in psutil.process_iter():
try:
@@ -95,6 +100,7 @@ class InfoCollector(object):
Adds network information from the host to the system information.
Currently updates with a list of networks accessible from host,
containing host ip and the subnet range.
- :return: None
+ :return: None. Updates class information
"""
+ LOG.debug("Reading subnets")
self.info['network_info'] = {'networks': get_host_subnets()}
diff --git a/chaos_monkey/system_info/linux_info_collector.py b/chaos_monkey/system_info/linux_info_collector.py
index 6c7570fc0..906173421 100644
--- a/chaos_monkey/system_info/linux_info_collector.py
+++ b/chaos_monkey/system_info/linux_info_collector.py
@@ -1,7 +1,11 @@
+import logging
+
from . import InfoCollector
__author__ = 'uri'
+LOG = logging.getLogger(__name__)
+
class LinuxInfoCollector(InfoCollector):
"""
@@ -12,6 +16,12 @@ class LinuxInfoCollector(InfoCollector):
super(LinuxInfoCollector, self).__init__()
def get_info(self):
+ """
+ Collect Linux system information
+ Hostname, process list and network subnets
+ :return: Dict of system information
+ """
+ LOG.debug("Running Linux collector")
self.get_hostname()
self.get_process_list()
self.get_network_info()
diff --git a/chaos_monkey/system_info/mimikatz_collector.py b/chaos_monkey/system_info/mimikatz_collector.py
index 53f42ad4c..e69bcd73e 100644
--- a/chaos_monkey/system_info/mimikatz_collector.py
+++ b/chaos_monkey/system_info/mimikatz_collector.py
@@ -1,5 +1,5 @@
-import ctypes
import binascii
+import ctypes
import logging
import socket
@@ -8,13 +8,14 @@ __author__ = 'itay.mizeretz'
LOG = logging.getLogger(__name__)
-class MimikatzCollector:
+class MimikatzCollector(object):
"""
Password collection module for Windows using Mimikatz.
"""
def __init__(self):
try:
+
self._isInit = False
self._config = __import__('config').WormConfiguration
self._dll = ctypes.WinDLL(self._config.mimikatz_dll_name)
@@ -31,9 +32,9 @@ class MimikatzCollector:
Gets the logon info from mimikatz.
Returns a dictionary of users with their known credentials.
"""
-
if not self._isInit:
return {}
+ LOG.debug("Running mimikatz collector")
try:
entry_count = self._collect()
diff --git a/chaos_monkey/system_info/windows_info_collector.py b/chaos_monkey/system_info/windows_info_collector.py
index 2ba26fd34..72e189f81 100644
--- a/chaos_monkey/system_info/windows_info_collector.py
+++ b/chaos_monkey/system_info/windows_info_collector.py
@@ -1,5 +1,10 @@
-from . import InfoCollector
+import logging
+
from mimikatz_collector import MimikatzCollector
+from . import InfoCollector
+
+LOG = logging.getLogger(__name__)
+
__author__ = 'uri'
@@ -12,6 +17,13 @@ class WindowsInfoCollector(InfoCollector):
super(WindowsInfoCollector, self).__init__()
def get_info(self):
+ """
+ Collect Windows system information
+ Hostname, process list and network subnets
+ Tries to read credential secrets using mimikatz
+ :return: Dict of system information
+ """
+ LOG.debug("Running Windows collector")
self.get_hostname()
self.get_process_list()
self.get_network_info()
From a5135c3c3f9f5b26becf6f46783cb9b5b7856a87 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg
Date: Sun, 31 Dec 2017 13:51:43 +0200
Subject: [PATCH 70/92] For the 50 lines before we can use our logger, use
modern print
---
chaos_monkey/main.py | 29 ++++++++++++++++-------------
1 file changed, 16 insertions(+), 13 deletions(-)
diff --git a/chaos_monkey/main.py b/chaos_monkey/main.py
index 231734d56..c53232b2c 100644
--- a/chaos_monkey/main.py
+++ b/chaos_monkey/main.py
@@ -1,14 +1,17 @@
-import os
-import sys
-import logging
-import traceback
-import logging.config
-from config import WormConfiguration, EXTERNAL_CONFIG_FILE
-from model import MONKEY_ARG, DROPPER_ARG
-from dropper import MonkeyDrops
-from monkey import ChaosMonkey
+from __future__ import print_function
+
import argparse
import json
+import logging
+import logging.config
+import os
+import sys
+import traceback
+
+from config import WormConfiguration, EXTERNAL_CONFIG_FILE
+from dropper import MonkeyDrops
+from model import MONKEY_ARG, DROPPER_ARG
+from monkey import ChaosMonkey
if __name__ == "__main__":
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
@@ -55,22 +58,22 @@ def main():
config_file = opts.config
if os.path.isfile(config_file):
# using print because config can also change log locations
- print "Loading config from %s." % config_file
+ print("Loading config from %s." % config_file)
try:
with open(config_file) as config_fo:
json_dict = json.load(config_fo)
WormConfiguration.from_dict(json_dict)
except ValueError as e:
- print "Error loading config: %s, using default" % (e,)
+ print("Error loading config: %s, using default" % (e,))
else:
print("Config file wasn't supplied and default path: %s wasn't found, using internal default" % (config_file,))
- print "Loaded Configuration: %r" % WormConfiguration.as_dict()
+ print("Loaded Configuration: %r" % WormConfiguration.as_dict())
# Make sure we're not in a machine that has the kill file
kill_path = os.path.expandvars(WormConfiguration.kill_file_path_windows) if sys.platform == "win32" else WormConfiguration.kill_file_path_linux
if os.path.exists(kill_path):
- print "Kill path found, finished run"
+ print("Kill path found, finished run")
return True
try:
From 109a9a5cbbc6e54ae3e4dcf4566743964eecac93 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 2 Jan 2018 12:34:59 +0200
Subject: [PATCH 71/92] Improve printed badge style
---
monkey_island/cc/ui/src/styles/App.css | 63 ++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/monkey_island/cc/ui/src/styles/App.css b/monkey_island/cc/ui/src/styles/App.css
index 8575387c6..c4bbecc62 100644
--- a/monkey_island/cc/ui/src/styles/App.css
+++ b/monkey_island/cc/ui/src/styles/App.css
@@ -420,4 +420,67 @@ body {
.pie-chart {
width: 100px;
}
+
+ .label {
+ padding: 2px 6px;
+ border: 1px solid #000;
+ color: #fff !important;
+ display: inline !important;
+ font-size: 75% !important;
+ font-weight: bold !important;
+ line-height: 1 !important;
+ text-align: center !important;
+ white-space: nowrap !important;
+ vertical-align: baseline !important;
+ border-radius: .25em !important;
+ }
+
+ .alert {
+ padding: 15px !important;
+ margin-bottom: 20px !important;
+ border: 1px solid transparent !important;
+ border-radius: 4px !important;
+ }
+
+ .alert-danger {
+ color:#a94442 !important;
+ background-color:#f2dede !important;
+ border-color:#ebccd1 !important;
+ }
+
+ .alert-success {
+ color:#3c763d !important;
+ background-color:#dff0d8 !important;
+ border-color:#d6e9c6 !important;
+ }
+
+ .alert-info {
+ color:#31708f !important;
+ background-color:#d9edf7 !important;
+ border-color:#bce8f1 !important;
+ }
+
+ .label-default {
+ background-color: #777 !important;
+ }
+
+ .label-primary {
+ background-color: #337ab7 !important;
+ }
+
+ .label-success {
+ background-color: #5cb85c !important;
+ }
+
+ .label-info {
+ background-color: #5bc0de !important;
+ }
+
+ .label-warning {
+ background-color: #f0ad4e !important;
+ }
+
+ .label-danger {
+ background-color: #d9534f !important;
+ }
}
From d16f3fee9ba644e7b579b65a16a1179e6db8992a Mon Sep 17 00:00:00 2001
From: Itay Mizeretz
Date: Tue, 2 Jan 2018 12:37:20 +0200
Subject: [PATCH 72/92] Replace monkey logo with new one
---
monkey_island/cc/ui/src/components/Main.js | 6 ++--
.../cc/ui/src/images/infection-monkey.svg | 32 ++++++++++++++++++
.../cc/ui/src/images/monkey-icon.png | Bin 38958 -> 0 bytes
.../cc/ui/src/images/monkey-icon.svg | 17 ++++++++++
.../cc/ui/src/images/monkey-logo.png | Bin 56273 -> 0 bytes
5 files changed, 53 insertions(+), 2 deletions(-)
create mode 100644 monkey_island/cc/ui/src/images/infection-monkey.svg
delete mode 100644 monkey_island/cc/ui/src/images/monkey-icon.png
create mode 100644 monkey_island/cc/ui/src/images/monkey-icon.svg
delete mode 100644 monkey_island/cc/ui/src/images/monkey-logo.png
diff --git a/monkey_island/cc/ui/src/components/Main.js b/monkey_island/cc/ui/src/components/Main.js
index 83e2b4042..cee9bc494 100644
--- a/monkey_island/cc/ui/src/components/Main.js
+++ b/monkey_island/cc/ui/src/components/Main.js
@@ -18,7 +18,8 @@ require('styles/App.css');
require('react-toggle/style.css');
require('react-table/react-table.css');
-let logoImage = require('../images/monkey-logo.png');
+let logoImage = require('../images/monkey-icon.svg');
+let infectionMonkeyImage = require('../images/infection-monkey.svg');
let guardicoreLogoImage = require('../images/guardicore-logo.png');
class AppComponent extends React.Component {
@@ -68,7 +69,8 @@ class AppComponent extends React.Component {