forked from p34709852/monkey
commit
7f5734ac67
|
@ -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]
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -15,8 +15,9 @@ 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.resources.telemetry_feed import TelemetryFeed
|
||||
from cc.services.config import ConfigService
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
@ -88,5 +89,7 @@ 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/')
|
||||
api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/')
|
||||
|
||||
return app
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
__author__ = 'itay.mizeretz'
|
||||
|
||||
ISLAND_PORT = 5000
|
||||
DEFAULT_MONGO_URL = "mongodb://localhost:27017/monkeyisland"
|
||||
DEFAULT_MONGO_URL = "mongodb://localhost:27017/monkeyisland"
|
||||
DEBUG_SERVER = False
|
||||
|
|
|
@ -11,7 +11,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
|
||||
from cc.database import is_db_server_up
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -26,11 +26,13 @@ if __name__ == '__main__':
|
|||
time.sleep(1)
|
||||
|
||||
app = init_app(mongo_url)
|
||||
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()
|
||||
|
||||
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'))
|
||||
|
|
|
@ -36,7 +36,7 @@ def run_local_monkey():
|
|||
|
||||
# run the monkey
|
||||
try:
|
||||
args = ["%s m0nk3y -s %s:%s" % (target_path, local_ip_addresses()[0], ISLAND_PORT)]
|
||||
args = ['"%s" m0nk3y -s %s:%s' % (target_path, local_ip_addresses()[0], ISLAND_PORT)]
|
||||
if sys.platform == "win32":
|
||||
args = "".join(args)
|
||||
pid = subprocess.Popen(args, shell=True).pid
|
||||
|
|
|
@ -53,6 +53,8 @@ class Monkey(flask_restful.Resource):
|
|||
|
||||
def post(self, **kw):
|
||||
monkey_json = json.loads(request.data)
|
||||
monkey_json['creds'] = []
|
||||
monkey_json['dead'] = False
|
||||
if 'keepalive' in monkey_json:
|
||||
monkey_json['keepalive'] = dateutil.parser.parse(monkey_json['keepalive'])
|
||||
else:
|
||||
|
@ -60,6 +62,8 @@ class Monkey(flask_restful.Resource):
|
|||
|
||||
monkey_json['modifytime'] = datetime.now()
|
||||
|
||||
ConfigService.save_initial_config_if_needed()
|
||||
|
||||
# if new monkey telem, change config according to "new monkeys" config.
|
||||
db_monkey = mongo.db.monkey.find_one({"guid": monkey_json["guid"]})
|
||||
if not db_monkey:
|
||||
|
@ -119,6 +123,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 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}
|
||||
|
|
|
@ -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()
|
|
@ -1,12 +1,12 @@
|
|||
from datetime import datetime
|
||||
|
||||
from flask import request, make_response, jsonify
|
||||
import flask_restful
|
||||
from flask import request, make_response, jsonify
|
||||
|
||||
from cc.database import mongo
|
||||
from cc.services.config import ConfigService
|
||||
from cc.services.node import NodeService
|
||||
|
||||
from cc.services.report import ReportService
|
||||
from cc.utils import local_ip_addresses
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
@ -18,24 +18,35 @@ class Root(flask_restful.Resource):
|
|||
action = request.args.get('action')
|
||||
|
||||
if not action:
|
||||
return jsonify(ip_addresses=local_ip_addresses(), mongo=str(mongo.db), completed_steps=self.get_completed_steps())
|
||||
|
||||
return Root.get_server_info()
|
||||
elif action == "reset":
|
||||
mongo.db.config.drop()
|
||||
mongo.db.monkey.drop()
|
||||
mongo.db.telemetry.drop()
|
||||
mongo.db.node.drop()
|
||||
mongo.db.edge.drop()
|
||||
ConfigService.init_config()
|
||||
return jsonify(status='OK')
|
||||
return Root.reset_db()
|
||||
elif action == "killall":
|
||||
mongo.db.monkey.update({'dead': False}, {'$set': {'config.alive': False, 'modifytime': datetime.now()}}, upsert=False,
|
||||
multi=True)
|
||||
return jsonify(status='OK')
|
||||
return Root.kill_all()
|
||||
else:
|
||||
return make_response(400, {'error': 'unknown action'})
|
||||
|
||||
def get_completed_steps(self):
|
||||
@staticmethod
|
||||
def get_server_info():
|
||||
return jsonify(ip_addresses=local_ip_addresses(), mongo=str(mongo.db),
|
||||
completed_steps=Root.get_completed_steps())
|
||||
|
||||
@staticmethod
|
||||
def reset_db():
|
||||
[mongo.db[x].drop() for x in ['config', 'monkey', 'telemetry', 'node', 'edge', 'report']]
|
||||
ConfigService.init_config()
|
||||
return jsonify(status='OK')
|
||||
|
||||
@staticmethod
|
||||
def kill_all():
|
||||
mongo.db.monkey.update({'dead': False}, {'$set': {'config.alive': False, 'modifytime': datetime.now()}},
|
||||
upsert=False,
|
||||
multi=True)
|
||||
return jsonify(status='OK')
|
||||
|
||||
@staticmethod
|
||||
def get_completed_steps():
|
||||
is_any_exists = NodeService.is_any_monkey_exists()
|
||||
is_any_alive = NodeService.is_any_monkey_alive()
|
||||
return dict(run_server=True, run_monkey=is_any_exists, infection_done=(is_any_exists and not is_any_alive))
|
||||
infection_done = NodeService.is_monkey_finished_running()
|
||||
report_done = ReportService.is_report_generated()
|
||||
return dict(run_server=True, run_monkey=is_any_exists, infection_done=infection_done, report_done=report_done)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import json
|
||||
import traceback
|
||||
import copy
|
||||
from datetime import datetime
|
||||
|
||||
import dateutil
|
||||
|
@ -39,7 +40,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 +53,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 +71,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
|
||||
|
||||
|
@ -103,7 +109,7 @@ class Telemetry(flask_restful.Resource):
|
|||
@staticmethod
|
||||
def process_exploit_telemetry(telemetry_json):
|
||||
edge = Telemetry.get_edge_by_scan_or_exploit_telemetry(telemetry_json)
|
||||
new_exploit = telemetry_json['data']
|
||||
new_exploit = copy.deepcopy(telemetry_json['data'])
|
||||
|
||||
new_exploit.pop('machine')
|
||||
new_exploit['timestamp'] = telemetry_json['timestamp']
|
||||
|
@ -115,10 +121,18 @@ class Telemetry(flask_restful.Resource):
|
|||
if new_exploit['result']:
|
||||
EdgeService.set_edge_exploited(edge)
|
||||
|
||||
for attempt in telemetry_json['data']['attempts']:
|
||||
if attempt['result']:
|
||||
found_creds = {'user': attempt['user']}
|
||||
for field in ['password', 'lm_hash', 'ntlm_hash']:
|
||||
if len(attempt[field]) != 0:
|
||||
found_creds[field] = attempt[field]
|
||||
NodeService.add_credentials_to_node(edge['to'], found_creds)
|
||||
|
||||
@staticmethod
|
||||
def process_scan_telemetry(telemetry_json):
|
||||
edge = Telemetry.get_edge_by_scan_or_exploit_telemetry(telemetry_json)
|
||||
data = telemetry_json['data']['machine']
|
||||
data = copy.deepcopy(telemetry_json['data']['machine'])
|
||||
ip_address = data.pop("ip_addr")
|
||||
new_scan = \
|
||||
{
|
||||
|
@ -158,11 +172,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,
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
from datetime import datetime
|
||||
|
||||
import dateutil
|
||||
import flask_restful
|
||||
from flask import request
|
||||
import flask_pymongo
|
||||
|
||||
from cc.database import mongo
|
||||
from cc.services.node import NodeService
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
|
||||
class TelemetryFeed(flask_restful.Resource):
|
||||
def get(self, **kw):
|
||||
timestamp = request.args.get('timestamp')
|
||||
if "null" == timestamp or timestamp is None: # special case to avoid ugly JS code...
|
||||
telemetries = mongo.db.telemetry.find({})
|
||||
else:
|
||||
telemetries = mongo.db.telemetry.find({'timestamp': {'$gt': dateutil.parser.parse(timestamp)}})\
|
||||
|
||||
telemetries = telemetries.sort([('timestamp', flask_pymongo.ASCENDING)])
|
||||
|
||||
return \
|
||||
{
|
||||
'telemetries': [TelemetryFeed.get_displayed_telemetry(telem) for telem in telemetries],
|
||||
'timestamp': datetime.now().isoformat()
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_displayed_telemetry(telem):
|
||||
return \
|
||||
{
|
||||
'id': telem['_id'],
|
||||
'timestamp': telem['timestamp'].strftime('%d/%m/%Y %H:%M:%S'),
|
||||
'hostname': NodeService.get_monkey_by_guid(telem['monkey_guid'])['hostname'],
|
||||
'brief': TELEM_PROCESS_DICT[telem['telem_type']](telem)
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_tunnel_telem_brief(telem):
|
||||
tunnel = telem['data']['proxy']
|
||||
if tunnel is None:
|
||||
return 'No tunnel is used.'
|
||||
else:
|
||||
tunnel_host_ip = tunnel.split(":")[-2].replace("//", "")
|
||||
tunnel_host = NodeService.get_monkey_by_ip(tunnel_host_ip)['hostname']
|
||||
return 'Tunnel set up to machine: %s.' % tunnel_host
|
||||
|
||||
@staticmethod
|
||||
def get_state_telem_brief(telem):
|
||||
if telem['data']['done']:
|
||||
return 'Monkey died.'
|
||||
else:
|
||||
return 'Monkey started.'
|
||||
|
||||
@staticmethod
|
||||
def get_exploit_telem_brief(telem):
|
||||
target = telem['data']['machine']['ip_addr']
|
||||
exploiter = telem['data']['exploiter']
|
||||
result = telem['data']['result']
|
||||
if result:
|
||||
return 'Monkey successfully exploited %s using the %s exploiter.' % (target, exploiter)
|
||||
else:
|
||||
return 'Monkey failed exploiting %s using the %s exploiter.' % (target, exploiter)
|
||||
|
||||
@staticmethod
|
||||
def get_scan_telem_brief(telem):
|
||||
return 'Monkey discovered machine %s.' % telem['data']['machine']['ip_addr']
|
||||
|
||||
@staticmethod
|
||||
def get_systeminfo_telem_brief(telem):
|
||||
return 'Monkey collected system information.'
|
||||
|
||||
@staticmethod
|
||||
def get_trace_telem_brief(telem):
|
||||
return 'Monkey reached max depth.'
|
||||
|
||||
|
||||
TELEM_PROCESS_DICT = \
|
||||
{
|
||||
'tunnel': TelemetryFeed.get_tunnel_telem_brief,
|
||||
'state': TelemetryFeed.get_state_telem_brief,
|
||||
'exploit': TelemetryFeed.get_exploit_telem_brief,
|
||||
'scan': TelemetryFeed.get_scan_telem_brief,
|
||||
'system_info_collection': TelemetryFeed.get_systeminfo_telem_brief,
|
||||
'trace': TelemetryFeed.get_trace_telem_brief
|
||||
}
|
|
@ -800,15 +800,23 @@ class ConfigService:
|
|||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_config():
|
||||
config = mongo.db.config.find_one({'name': 'newconfig'}) or {}
|
||||
def get_config(is_initial_config=False):
|
||||
config = mongo.db.config.find_one({'name': 'initial' if is_initial_config else 'newconfig'}) or {}
|
||||
for field in ('name', '_id'):
|
||||
config.pop(field, None)
|
||||
return config
|
||||
|
||||
@staticmethod
|
||||
def get_flat_config():
|
||||
config_json = ConfigService.get_config()
|
||||
def get_config_value(config_key_as_arr, is_initial_config=False):
|
||||
config_key = reduce(lambda x, y: x+'.'+y, config_key_as_arr)
|
||||
config = mongo.db.config.find_one({'name': 'initial' if is_initial_config else '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(is_initial_config=False):
|
||||
config_json = ConfigService.get_config(is_initial_config)
|
||||
flat_config_json = {}
|
||||
for i in config_json:
|
||||
for j in config_json[i]:
|
||||
|
@ -880,6 +888,16 @@ class ConfigService:
|
|||
config["cnc"]["servers"]["command_servers"] = ["%s:%d" % (ip, ISLAND_PORT) for ip in ips]
|
||||
config["cnc"]["servers"]["current_server"] = "%s:%d" % (ips[0], ISLAND_PORT)
|
||||
|
||||
@staticmethod
|
||||
def save_initial_config_if_needed():
|
||||
if mongo.db.config.find_one({'name': 'initial'}) is not None:
|
||||
return
|
||||
|
||||
initial_config = mongo.db.config.find_one({'name': 'newconfig'})
|
||||
initial_config['name'] = 'initial'
|
||||
initial_config.pop('_id')
|
||||
mongo.db.config.insert(initial_config)
|
||||
|
||||
@staticmethod
|
||||
def _extend_config_with_default(validator_class):
|
||||
validate_properties = validator_class.VALIDATORS["properties"]
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
@ -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]
|
||||
|
@ -115,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)
|
||||
}
|
||||
|
@ -166,6 +172,7 @@ class NodeService:
|
|||
{
|
||||
"ip_addresses": [ip_address],
|
||||
"exploited": False,
|
||||
"creds": [],
|
||||
"os":
|
||||
{
|
||||
"type": "unknown",
|
||||
|
@ -273,3 +280,39 @@ class NodeService:
|
|||
@staticmethod
|
||||
def is_any_monkey_exists():
|
||||
return mongo.db.monkey.find_one({}) is not None
|
||||
|
||||
@staticmethod
|
||||
def is_monkey_finished_running():
|
||||
return NodeService.is_any_monkey_exists() and not NodeService.is_any_monkey_alive()
|
||||
|
||||
@staticmethod
|
||||
def add_credentials_to_monkey(monkey_id, creds):
|
||||
mongo.db.monkey.update(
|
||||
{'_id': monkey_id},
|
||||
{'$push': {'creds': creds}}
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def add_credentials_to_node(node_id, creds):
|
||||
mongo.db.node.update(
|
||||
{'_id': node_id},
|
||||
{'$push': {'creds': creds}}
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_node_or_monkey_by_ip(ip_address):
|
||||
node = NodeService.get_node_by_ip(ip_address)
|
||||
if node is not None:
|
||||
return node
|
||||
return NodeService.get_monkey_by_ip(ip_address)
|
||||
|
||||
@staticmethod
|
||||
def get_node_or_monkey_by_id(node_id):
|
||||
node = NodeService.get_node_by_id(node_id)
|
||||
if node is not None:
|
||||
return node
|
||||
return NodeService.get_monkey_by_id(node_id)
|
||||
|
||||
@staticmethod
|
||||
def get_node_hostname(node):
|
||||
return node['hostname'] if 'hostname' in node else node['os']['version']
|
||||
|
|
|
@ -0,0 +1,418 @@
|
|||
import ipaddress
|
||||
from enum import Enum
|
||||
|
||||
from cc.database import mongo
|
||||
from cc.services.config import ConfigService
|
||||
from cc.services.edge import EdgeService
|
||||
from cc.services.node import NodeService
|
||||
from cc.utils import local_ip_addresses, get_subnets
|
||||
|
||||
__author__ = "itay.mizeretz"
|
||||
|
||||
|
||||
class ReportService:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
EXPLOIT_DISPLAY_DICT = \
|
||||
{
|
||||
'SmbExploiter': 'SMB Exploiter',
|
||||
'WmiExploiter': 'WMI Exploiter',
|
||||
'SSHExploiter': 'SSH Exploiter',
|
||||
'RdpExploiter': 'RDP Exploiter',
|
||||
'SambaCryExploiter': 'SambaCry Exploiter',
|
||||
'ElasticGroovyExploiter': 'Elastic Groovy Exploiter',
|
||||
'Ms08_067_Exploiter': 'Conficker Exploiter',
|
||||
'ShellShockExploiter': 'ShellShock Exploiter',
|
||||
}
|
||||
|
||||
class ISSUES_DICT(Enum):
|
||||
WEAK_PASSWORD = 0
|
||||
STOLEN_CREDS = 1
|
||||
ELASTIC = 2
|
||||
SAMBACRY = 3
|
||||
SHELLSHOCK = 4
|
||||
CONFICKER = 5
|
||||
|
||||
class WARNINGS_DICT(Enum):
|
||||
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']
|
||||
|
||||
@staticmethod
|
||||
def get_last_monkey_dead_time():
|
||||
return mongo.db.telemetry.find({}, {'timestamp': 1}).sort([('$natural', -1)]).limit(1)[0]['timestamp']
|
||||
|
||||
@staticmethod
|
||||
def get_monkey_duration():
|
||||
delta = ReportService.get_last_monkey_dead_time() - ReportService.get_first_monkey_time()
|
||||
st = ""
|
||||
hours, rem = divmod(delta.seconds, 60 * 60)
|
||||
minutes, seconds = divmod(rem, 60)
|
||||
|
||||
if delta.days > 0:
|
||||
st += "%d days, " % delta.days
|
||||
if hours > 0:
|
||||
st += "%d hours, " % hours
|
||||
st += "%d minutes and %d seconds" % (minutes, seconds)
|
||||
|
||||
return st
|
||||
|
||||
@staticmethod
|
||||
def get_tunnels():
|
||||
return [
|
||||
{
|
||||
'type': 'tunnel',
|
||||
'machine': NodeService.get_node_hostname(NodeService.get_node_or_monkey_by_id(tunnel['_id'])),
|
||||
'dest': NodeService.get_node_hostname(NodeService.get_node_or_monkey_by_id(tunnel['tunnel']))
|
||||
}
|
||||
for tunnel in mongo.db.monkey.find({'tunnel': {'$exists': True}}, {'tunnel': 1})]
|
||||
|
||||
@staticmethod
|
||||
def get_scanned():
|
||||
nodes = \
|
||||
[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['label'],
|
||||
'ip_addresses': node['ip_addresses'],
|
||||
'accessible_from_nodes':
|
||||
(x['hostname'] for x in
|
||||
(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]
|
||||
|
||||
return nodes
|
||||
|
||||
@staticmethod
|
||||
def get_exploited():
|
||||
exploited = \
|
||||
[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'], True)
|
||||
for node in mongo.db.node.find({'exploited': True}, {'_id': 1})]
|
||||
|
||||
exploited = [
|
||||
{
|
||||
'label': monkey['label'],
|
||||
'ip_addresses': monkey['ip_addresses'],
|
||||
'exploits': list(set(
|
||||
[ReportService.EXPLOIT_DISPLAY_DICT[exploit['exploiter']] for exploit in monkey['exploits'] if
|
||||
exploit['result']]))
|
||||
}
|
||||
for monkey in exploited]
|
||||
|
||||
return exploited
|
||||
|
||||
@staticmethod
|
||||
def get_stolen_creds():
|
||||
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}},
|
||||
{'data.credentials': 1, 'monkey_guid': 1}
|
||||
):
|
||||
monkey_creds = telem['data']['credentials']
|
||||
if len(monkey_creds) == 0:
|
||||
continue
|
||||
origin = NodeService.get_monkey_by_guid(telem['monkey_guid'])['hostname']
|
||||
for user in monkey_creds:
|
||||
for pass_type in monkey_creds[user]:
|
||||
creds.append(
|
||||
{
|
||||
'username': user.replace(',', '.'),
|
||||
'type': PASS_TYPE_DICT[pass_type],
|
||||
'origin': origin
|
||||
}
|
||||
)
|
||||
return creds
|
||||
|
||||
@staticmethod
|
||||
def process_general_exploit(exploit):
|
||||
ip_addr = exploit['data']['machine']['ip_addr']
|
||||
return {'machine': NodeService.get_node_hostname(NodeService.get_node_or_monkey_by_ip(ip_addr)),
|
||||
'ip_address': ip_addr}
|
||||
|
||||
@staticmethod
|
||||
def process_general_creds_exploit(exploit):
|
||||
processed_exploit = ReportService.process_general_exploit(exploit)
|
||||
|
||||
for attempt in exploit['data']['attempts']:
|
||||
if attempt['result']:
|
||||
processed_exploit['username'] = attempt['user']
|
||||
if len(attempt['password']) > 0:
|
||||
processed_exploit['type'] = 'password'
|
||||
processed_exploit['password'] = attempt['password']
|
||||
else:
|
||||
processed_exploit['type'] = 'hash'
|
||||
return processed_exploit
|
||||
|
||||
@staticmethod
|
||||
def process_smb_exploit(exploit):
|
||||
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
||||
if processed_exploit['type'] == 'password':
|
||||
processed_exploit['type'] = 'smb_password'
|
||||
else:
|
||||
processed_exploit['type'] = 'smb_pth'
|
||||
return processed_exploit
|
||||
|
||||
@staticmethod
|
||||
def process_wmi_exploit(exploit):
|
||||
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
||||
if processed_exploit['type'] == 'password':
|
||||
processed_exploit['type'] = 'wmi_password'
|
||||
else:
|
||||
processed_exploit['type'] = 'wmi_pth'
|
||||
return processed_exploit
|
||||
|
||||
@staticmethod
|
||||
def process_ssh_exploit(exploit):
|
||||
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
||||
processed_exploit['type'] = 'ssh'
|
||||
return processed_exploit
|
||||
|
||||
@staticmethod
|
||||
def process_rdp_exploit(exploit):
|
||||
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
||||
processed_exploit['type'] = 'rdp'
|
||||
return processed_exploit
|
||||
|
||||
@staticmethod
|
||||
def process_sambacry_exploit(exploit):
|
||||
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
||||
processed_exploit['type'] = 'sambacry'
|
||||
return processed_exploit
|
||||
|
||||
@staticmethod
|
||||
def process_elastic_exploit(exploit):
|
||||
processed_exploit = ReportService.process_general_exploit(exploit)
|
||||
processed_exploit['type'] = 'elastic'
|
||||
return processed_exploit
|
||||
|
||||
@staticmethod
|
||||
def process_conficker_exploit(exploit):
|
||||
processed_exploit = ReportService.process_general_exploit(exploit)
|
||||
processed_exploit['type'] = 'conficker'
|
||||
return processed_exploit
|
||||
|
||||
@staticmethod
|
||||
def process_shellshock_exploit(exploit):
|
||||
processed_exploit = ReportService.process_general_exploit(exploit)
|
||||
processed_exploit['type'] = 'shellshock'
|
||||
urls = exploit['data']['info']['vulnerable_urls']
|
||||
processed_exploit['port'] = urls[0].split(':')[2].split('/')[0]
|
||||
processed_exploit['paths'] = ['/' + url.split(':')[2].split('/')[1] for url in urls]
|
||||
return processed_exploit
|
||||
|
||||
@staticmethod
|
||||
def process_exploit(exploit):
|
||||
exploiter_type = exploit['data']['exploiter']
|
||||
EXPLOIT_PROCESS_FUNCTION_DICT = {
|
||||
'SmbExploiter': ReportService.process_smb_exploit,
|
||||
'WmiExploiter': ReportService.process_wmi_exploit,
|
||||
'SSHExploiter': ReportService.process_ssh_exploit,
|
||||
'RdpExploiter': ReportService.process_rdp_exploit,
|
||||
'SambaCryExploiter': ReportService.process_sambacry_exploit,
|
||||
'ElasticGroovyExploiter': ReportService.process_elastic_exploit,
|
||||
'Ms08_067_Exploiter': ReportService.process_conficker_exploit,
|
||||
'ShellShockExploiter': ReportService.process_shellshock_exploit,
|
||||
}
|
||||
|
||||
return EXPLOIT_PROCESS_FUNCTION_DICT[exploiter_type](exploit)
|
||||
|
||||
@staticmethod
|
||||
def get_exploits():
|
||||
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):
|
||||
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 network_info['data']['network_info']['networks']
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def get_cross_segment_issues():
|
||||
issues = []
|
||||
island_ips = local_ip_addresses()
|
||||
for monkey in mongo.db.monkey.find({'tunnel': {'$exists': False}}, {'tunnel': 1, 'guid': 1, 'hostname': 1}):
|
||||
found_good_ip = False
|
||||
monkey_subnets = ReportService.get_monkey_subnets(monkey['guid'])
|
||||
for subnet in monkey_subnets:
|
||||
for ip in island_ips:
|
||||
if ipaddress.ip_address(unicode(ip)) in subnet:
|
||||
found_good_ip = True
|
||||
break
|
||||
if found_good_ip:
|
||||
break
|
||||
if not found_good_ip:
|
||||
issues.append(
|
||||
{'type': 'cross_segment', 'machine': monkey['hostname'],
|
||||
'networks': [str(subnet) for subnet in monkey_subnets],
|
||||
'server_networks': [str(subnet) for subnet in get_subnets()]}
|
||||
)
|
||||
|
||||
return issues
|
||||
|
||||
@staticmethod
|
||||
def get_issues():
|
||||
issues = ReportService.get_exploits() + ReportService.get_tunnels() + ReportService.get_cross_segment_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_manual_monkeys():
|
||||
return [monkey['hostname'] for monkey in mongo.db.monkey.find({}, {'hostname': 1, 'parent': 1, 'guid': 1}) if
|
||||
NodeService.get_monkey_manual_run(monkey)]
|
||||
|
||||
@staticmethod
|
||||
def get_config_users():
|
||||
return ConfigService.get_config_value(['basic', 'credentials', 'exploit_user_list'], True)
|
||||
|
||||
@staticmethod
|
||||
def get_config_passwords():
|
||||
return ConfigService.get_config_value(['basic', 'credentials', 'exploit_password_list'], True)
|
||||
|
||||
@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, True)
|
||||
|
||||
if exploits == default_exploits:
|
||||
return ['default']
|
||||
|
||||
return [ReportService.EXPLOIT_DISPLAY_DICT[exploit] for exploit in
|
||||
exploits]
|
||||
|
||||
@staticmethod
|
||||
def get_config_ips():
|
||||
if ConfigService.get_config_value(['basic_network', 'network_range', 'range_class'], True) != 'FixedRange':
|
||||
return []
|
||||
return ConfigService.get_config_value(['basic_network', 'network_range', 'range_fixed'], True)
|
||||
|
||||
@staticmethod
|
||||
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.value] = True
|
||||
elif issue['type'] == 'sambacry':
|
||||
issues_byte_array[ReportService.ISSUES_DICT.SAMBACRY.value] = True
|
||||
elif issue['type'] == 'shellshock':
|
||||
issues_byte_array[ReportService.ISSUES_DICT.SHELLSHOCK.value] = True
|
||||
elif issue['type'] == 'conficker':
|
||||
issues_byte_array[ReportService.ISSUES_DICT.CONFICKER.value] = True
|
||||
elif issue['type'].endswith('_password') and issue['password'] in config_passwords and \
|
||||
issue['username'] in config_users:
|
||||
issues_byte_array[ReportService.ISSUES_DICT.WEAK_PASSWORD.value] = True
|
||||
elif issue['type'].endswith('_pth') or issue['type'].endswith('_password'):
|
||||
issues_byte_array[ReportService.ISSUES_DICT.STOLEN_CREDS.value] = 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.value] = True
|
||||
elif issue['type'] == 'tunnel':
|
||||
warnings_byte_array[ReportService.WARNINGS_DICT.TUNNEL.value] = True
|
||||
|
||||
return warnings_byte_array
|
||||
|
||||
@staticmethod
|
||||
def is_report_generated():
|
||||
generated_report = mongo.db.report.find_one({'name': 'generated_report'})
|
||||
if generated_report is None:
|
||||
return False
|
||||
return generated_report['value']
|
||||
|
||||
@staticmethod
|
||||
def set_report_generated():
|
||||
mongo.db.report.update(
|
||||
{'name': 'generated_report'},
|
||||
{'$set': {'value': True}},
|
||||
upsert=True)
|
||||
|
||||
@staticmethod
|
||||
def get_report():
|
||||
issues = ReportService.get_issues()
|
||||
config_users = ReportService.get_config_users()
|
||||
config_passwords = ReportService.get_config_passwords()
|
||||
|
||||
report = \
|
||||
{
|
||||
'overview':
|
||||
{
|
||||
'manual_monkeys': ReportService.get_manual_monkeys(),
|
||||
'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': ReportService.get_issues_overview(issues, config_users, config_passwords),
|
||||
'warnings': ReportService.get_warnings_overview(issues)
|
||||
},
|
||||
'glance':
|
||||
{
|
||||
'scanned': ReportService.get_scanned(),
|
||||
'exploited': ReportService.get_exploited(),
|
||||
'stolen_creds': ReportService.get_stolen_creds()
|
||||
},
|
||||
'recommendations':
|
||||
{
|
||||
'issues': issues
|
||||
}
|
||||
}
|
||||
|
||||
finished_run = NodeService.is_monkey_finished_running()
|
||||
if finished_run:
|
||||
ReportService.set_report_generated()
|
||||
|
||||
return report
|
||||
|
||||
@staticmethod
|
||||
def did_exploit_type_succeed(exploit_type):
|
||||
return mongo.db.edge.count(
|
||||
{'exploits': {'$elemMatch': {'exploiter': exploit_type, 'result': True}}},
|
||||
limit=1) > 0
|
|
@ -67,6 +67,7 @@
|
|||
"js-file-download": "^0.4.1",
|
||||
"normalize.css": "^4.0.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"rc-progress": "^2.2.5",
|
||||
"react": "^15.6.1",
|
||||
"react-bootstrap": "^0.31.2",
|
||||
"react-copy-to-clipboard": "^5.0.0",
|
||||
|
@ -80,6 +81,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"
|
||||
}
|
||||
|
|
|
@ -16,8 +16,10 @@ 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 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 {
|
||||
|
@ -27,7 +29,8 @@ class AppComponent extends React.Component {
|
|||
completedSteps: {
|
||||
run_server: true,
|
||||
run_monkey: false,
|
||||
infection_done: false
|
||||
infection_done: false,
|
||||
report_done: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -66,7 +69,8 @@ class AppComponent extends React.Component {
|
|||
<Row>
|
||||
<Col sm={3} md={2} className="sidebar">
|
||||
<div className="header">
|
||||
<img src={logoImage} alt="Infection Monkey"/>
|
||||
<img src={logoImage} style={{width: '5vw', margin: '15px'}}/>
|
||||
<img src={infectionMonkeyImage} style={{width: '15vw'}} alt="Infection Monkey"/>
|
||||
</div>
|
||||
|
||||
<ul className="navigation">
|
||||
|
@ -101,6 +105,9 @@ class AppComponent extends React.Component {
|
|||
<NavLink to="/report">
|
||||
<span className="number">4.</span>
|
||||
Security Report
|
||||
{ this.state.completedSteps.report_done ?
|
||||
<Icon name="check" className="pull-right checkmark text-success"/>
|
||||
: ''}
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
let groupNames = ['clean_unknown', 'clean_linux', 'clean_windows', 'exploited_linux', 'exploited_windows', 'island',
|
||||
'island_monkey_linux', 'island_monkey_linux_running', 'island_monkey_windows', 'island_monkey_windows_running',
|
||||
'manual_linux', 'manual_linux_running', 'manual_windows', 'manual_windows_running', 'monkey_linux',
|
||||
'monkey_linux_running', 'monkey_windows', 'monkey_windows_running'];
|
||||
|
||||
let getGroupsOptions = () => {
|
||||
let groupOptions = {};
|
||||
for (let groupName of groupNames) {
|
||||
groupOptions[groupName] =
|
||||
{
|
||||
shape: 'image',
|
||||
size: 50,
|
||||
image: require('../../images/nodes/' + groupName + '.png')
|
||||
};
|
||||
}
|
||||
return groupOptions;
|
||||
};
|
||||
|
||||
export const options = {
|
||||
autoResize: true,
|
||||
layout: {
|
||||
improvedLayout: false
|
||||
},
|
||||
edges: {
|
||||
width: 2,
|
||||
smooth: {
|
||||
type: 'curvedCW'
|
||||
}
|
||||
},
|
||||
physics: {
|
||||
barnesHut: {
|
||||
gravitationalConstant: -120000,
|
||||
avoidOverlap: 0.5
|
||||
},
|
||||
minVelocity: 0.75
|
||||
},
|
||||
groups: getGroupsOptions()
|
||||
};
|
||||
|
||||
export function edgeGroupToColor(group) {
|
||||
switch (group) {
|
||||
case 'exploited':
|
||||
return '#c00';
|
||||
case 'tunnel':
|
||||
return '#0058aa';
|
||||
case 'scan':
|
||||
return '#f90';
|
||||
case 'island':
|
||||
return '#aaa';
|
||||
}
|
||||
return 'black';
|
||||
}
|
|
@ -2,48 +2,10 @@ import React from 'react';
|
|||
import {Col} from 'react-bootstrap';
|
||||
import {Link} from 'react-router-dom';
|
||||
import {Icon} from 'react-fa';
|
||||
import PreviewPane from 'components/preview-pane/PreviewPane';
|
||||
import {ReactiveGraph} from '../reactive-graph/ReactiveGraph';
|
||||
import PreviewPane from 'components/map/preview-pane/PreviewPane';
|
||||
import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
|
||||
import {ModalContainer, ModalDialog} from 'react-modal-dialog';
|
||||
|
||||
let groupNames = ['clean_unknown', 'clean_linux', 'clean_windows', 'exploited_linux', 'exploited_windows', 'island',
|
||||
'island_monkey_linux', 'island_monkey_linux_running', 'island_monkey_windows', 'island_monkey_windows_running',
|
||||
'manual_linux', 'manual_linux_running', 'manual_windows', 'manual_windows_running', 'monkey_linux',
|
||||
'monkey_linux_running', 'monkey_windows', 'monkey_windows_running'];
|
||||
|
||||
let getGroupsOptions = () => {
|
||||
let groupOptions = {};
|
||||
for (let groupName of groupNames) {
|
||||
groupOptions[groupName] =
|
||||
{
|
||||
shape: 'image',
|
||||
size: 50,
|
||||
image: require('../../images/nodes/' + groupName + '.png')
|
||||
};
|
||||
}
|
||||
return groupOptions;
|
||||
};
|
||||
|
||||
let options = {
|
||||
autoResize: true,
|
||||
layout: {
|
||||
improvedLayout: false
|
||||
},
|
||||
edges: {
|
||||
width: 2,
|
||||
smooth: {
|
||||
type: 'curvedCW'
|
||||
}
|
||||
},
|
||||
physics: {
|
||||
barnesHut: {
|
||||
gravitationalConstant: -120000,
|
||||
avoidOverlap: 0.5
|
||||
},
|
||||
minVelocity: 0.75
|
||||
},
|
||||
groups: getGroupsOptions()
|
||||
};
|
||||
import {options, edgeGroupToColor} from 'components/map/MapOptions';
|
||||
|
||||
class MapPageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -53,7 +15,9 @@ class MapPageComponent extends React.Component {
|
|||
selected: null,
|
||||
selectedType: null,
|
||||
killPressed: false,
|
||||
showKillDialog: false
|
||||
showKillDialog: false,
|
||||
telemetry: [],
|
||||
telemetryLastTimestamp: null
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -61,41 +25,47 @@ class MapPageComponent extends React.Component {
|
|||
select: event => this.selectionChanged(event)
|
||||
};
|
||||
|
||||
static edgeGroupToColor(group) {
|
||||
switch (group) {
|
||||
case 'exploited':
|
||||
return '#c00';
|
||||
case 'tunnel':
|
||||
return '#0058aa';
|
||||
case 'scan':
|
||||
return '#f90';
|
||||
case 'island':
|
||||
return '#aaa';
|
||||
}
|
||||
return 'black';
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateMapFromServer();
|
||||
this.interval = setInterval(this.updateMapFromServer, 1000);
|
||||
this.interval = setInterval(this.timedEvents, 1000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
timedEvents = () => {
|
||||
this.updateMapFromServer();
|
||||
this.updateTelemetryFromServer();
|
||||
};
|
||||
|
||||
updateMapFromServer = () => {
|
||||
fetch('/api/netmap')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
res.edges.forEach(edge => {
|
||||
edge.color = MapPageComponent.edgeGroupToColor(edge.group);
|
||||
edge.color = edgeGroupToColor(edge.group);
|
||||
});
|
||||
this.setState({graph: res});
|
||||
this.props.onStatusChange();
|
||||
});
|
||||
};
|
||||
|
||||
updateTelemetryFromServer = () => {
|
||||
fetch('/api/telemetry-feed?timestamp='+this.state.telemetryLastTimestamp)
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
let newTelem = this.state.telemetry.concat(res['telemetries']);
|
||||
|
||||
this.setState(
|
||||
{
|
||||
telemetry: newTelem,
|
||||
telemetryLastTimestamp: res['timestamp']
|
||||
});
|
||||
this.props.onStatusChange();
|
||||
});
|
||||
};
|
||||
|
||||
selectionChanged(event) {
|
||||
if (event.nodes.length === 1) {
|
||||
fetch('/api/netmap/node?id=' + event.nodes[0])
|
||||
|
@ -156,6 +126,26 @@ class MapPageComponent extends React.Component {
|
|||
)
|
||||
};
|
||||
|
||||
renderTelemetryEntry(telemetry) {
|
||||
return (
|
||||
<div key={telemetry.id}>
|
||||
<span className="date">{telemetry.timestamp}</span>
|
||||
<span className="source"> {telemetry.hostname}:</span>
|
||||
<span className="event"> {telemetry.brief}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTelemetryConsole() {
|
||||
return (
|
||||
<div className="telemetry-console">
|
||||
{
|
||||
this.state.telemetry.map(this.renderTelemetryEntry)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
|
@ -174,17 +164,7 @@ class MapPageComponent extends React.Component {
|
|||
<b style={{color: '#aeaeae'}}> | </b>
|
||||
<span>Island Communication <i className="fa fa-lg fa-minus" style={{color: '#a9aaa9'}} /></span>
|
||||
</div>
|
||||
{
|
||||
/*
|
||||
<div className="telemetry-console">
|
||||
<div>
|
||||
<span className="date">2017-10-16 16:00:05</span>
|
||||
<span className="source"> monkey-elastic</span>
|
||||
<span className="event"> bla bla</span>
|
||||
</div>
|
||||
</div>
|
||||
*/
|
||||
}
|
||||
{ this.renderTelemetryConsole() }
|
||||
<div style={{height: '80vh'}}>
|
||||
<ReactiveGraph graph={this.state.graph} options={options} events={this.events}/>
|
||||
</div>
|
||||
|
|
|
@ -1,23 +1,692 @@
|
|||
import React from 'react';
|
||||
import {Col} from 'react-bootstrap';
|
||||
import {Button, 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 {edgeGroupToColor, options} from 'components/map/MapOptions';
|
||||
import StolenPasswords from 'components/report-components/StolenPasswords';
|
||||
import CollapsibleWellComponent from 'components/report-components/CollapsibleWell';
|
||||
import {Line} from 'rc-progress';
|
||||
|
||||
let guardicoreLogoImage = require('../../images/guardicore-logo.png');
|
||||
let monkeyLogoImage = require('../../images/monkey-icon.svg');
|
||||
|
||||
class ReportPageComponent extends React.Component {
|
||||
|
||||
Issue =
|
||||
{
|
||||
WEAK_PASSWORD: 0,
|
||||
STOLEN_CREDS: 1,
|
||||
ELASTIC: 2,
|
||||
SAMBACRY: 3,
|
||||
SHELLSHOCK: 4,
|
||||
CONFICKER: 5
|
||||
};
|
||||
|
||||
Warning =
|
||||
{
|
||||
CROSS_SEGMENT: 0,
|
||||
TUNNEL: 1
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
report: {},
|
||||
graph: {nodes: [], edges: []},
|
||||
allMonkeysAreDead: false,
|
||||
runStarted: true
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateMonkeysRunning().then(res => this.getReportFromServer(res));
|
||||
this.updateMapFromServer();
|
||||
this.interval = setInterval(this.updateMapFromServer, 1000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
render() {
|
||||
let content;
|
||||
if (Object.keys(this.state.report).length === 0) {
|
||||
if (this.state.runStarted) {
|
||||
content = (<h1>Generating Report...</h1>);
|
||||
} else {
|
||||
content =
|
||||
<p className="alert alert-warning">
|
||||
<i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/>
|
||||
You have to run a monkey before generating a report!
|
||||
</p>;
|
||||
}
|
||||
} else {
|
||||
content = this.generateReportContent();
|
||||
}
|
||||
|
||||
return (
|
||||
<Col xs={12} lg={8}>
|
||||
<h1 className="page-title">4. Security Report</h1>
|
||||
<h1 className="page-title no-print">4. Security Report</h1>
|
||||
<div style={{'fontSize': '1.2em'}}>
|
||||
<p>
|
||||
Under construction
|
||||
</p>
|
||||
{content}
|
||||
</div>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
||||
updateMonkeysRunning = () => {
|
||||
return fetch('/api')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
// This check is used to prevent unnecessary re-rendering
|
||||
this.setState({
|
||||
allMonkeysAreDead: (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']),
|
||||
runStarted: res['completed_steps']['run_monkey']
|
||||
});
|
||||
return res;
|
||||
});
|
||||
};
|
||||
|
||||
updateMapFromServer = () => {
|
||||
fetch('/api/netmap')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
res.edges.forEach(edge => {
|
||||
edge.color = edgeGroupToColor(edge.group);
|
||||
});
|
||||
this.setState({graph: res});
|
||||
this.props.onStatusChange();
|
||||
});
|
||||
};
|
||||
|
||||
getReportFromServer(res) {
|
||||
if (res['completed_steps']['run_monkey']) {
|
||||
fetch('/api/report')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
this.setState({
|
||||
report: res
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
generateReportContent() {
|
||||
return (
|
||||
<div>
|
||||
<div className="text-center no-print" style={{marginBottom: '20px'}}>
|
||||
<Button bsSize="large" onClick={() => {
|
||||
print();
|
||||
}}><i className="glyphicon glyphicon-print"/> Print Report</Button>
|
||||
</div>
|
||||
<div className="report-page">
|
||||
{this.generateReportHeader()}
|
||||
<hr/>
|
||||
{this.generateReportOverviewSection()}
|
||||
{this.generateReportFindingsSection()}
|
||||
{this.generateReportRecommendationsSection()}
|
||||
{this.generateReportGlanceSection()}
|
||||
{this.generateReportFooter()}
|
||||
</div>
|
||||
<div className="text-center no-print" style={{marginTop: '20px'}}>
|
||||
<Button bsSize="large" onClick={() => {
|
||||
print();
|
||||
}}><i className="glyphicon glyphicon-print"/> Print Report</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
generateReportHeader() {
|
||||
return (
|
||||
<div id="header" className="row justify-content-between">
|
||||
<Col xs={8}>
|
||||
<div>
|
||||
<h1 style={{marginTop: '0px', marginBottom: '5px', color: '#666666', fontFamily: 'Alegreya'}}>Security Report</h1>
|
||||
<h1 style={{marginTop: '0px', marginBottom: '0px', color: '#ffcc00', fontFamily: 'Alegreya'}}>Infection <b>Monkey</b></h1>
|
||||
</div>
|
||||
</Col>
|
||||
<Col xs={4}>
|
||||
<img src={monkeyLogoImage}
|
||||
style={{
|
||||
float: 'right',
|
||||
width: '80px'
|
||||
}}/>
|
||||
</Col>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
generateReportOverviewSection() {
|
||||
return (
|
||||
<div id="overview">
|
||||
<h2>
|
||||
Overview
|
||||
</h2>
|
||||
{
|
||||
this.state.report.glance.exploited.length > 0 ?
|
||||
(<p className="alert alert-danger">
|
||||
<i className="glyphicon glyphicon-exclamation-sign" style={{'marginRight': '5px'}}/>
|
||||
Critical security issues were detected!
|
||||
</p>) :
|
||||
(<p className="alert alert-success">
|
||||
<i className="glyphicon glyphicon-ok-sign" style={{'marginRight': '5px'}}/>
|
||||
No critical security issues were detected.
|
||||
</p>)
|
||||
}
|
||||
{
|
||||
this.state.allMonkeysAreDead ?
|
||||
''
|
||||
:
|
||||
(<p className="alert alert-warning">
|
||||
<i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/>
|
||||
Some monkeys are still running. To get the best report it's best to wait for all of them to finish
|
||||
running.
|
||||
</p>)
|
||||
}
|
||||
{
|
||||
this.state.report.glance.exploited.length > 0 ?
|
||||
''
|
||||
:
|
||||
<p className="alert alert-info">
|
||||
<i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/>
|
||||
To improve the monkey's detection rates, try adding users and passwords and enable the "Local
|
||||
network
|
||||
scan" config value under <b>Basic - Network</b>.
|
||||
</p>
|
||||
}
|
||||
<p>
|
||||
The first monkey run was started on <span
|
||||
className="label label-info">{this.state.report.overview.monkey_start_time}</span>. After <span
|
||||
className="label label-info">{this.state.report.overview.monkey_duration}</span>, all monkeys finished
|
||||
propagation attempts.
|
||||
</p>
|
||||
<p>
|
||||
The monkey started propagating from the following machines where it was manually installed:
|
||||
<ul>
|
||||
{this.state.report.overview.manual_monkeys.map(x => <li>{x}</li>)}
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
The monkeys were run with the following configuration:
|
||||
</p>
|
||||
{
|
||||
this.state.report.overview.config_users.length > 0 ?
|
||||
<p>
|
||||
Usernames used for brute-forcing:
|
||||
<ul>
|
||||
{this.state.report.overview.config_users.map(x => <li>{x}</li>)}
|
||||
</ul>
|
||||
Passwords used for brute-forcing:
|
||||
<ul>
|
||||
{this.state.report.overview.config_passwords.map(x => <li>{x.substr(0, 3) + '******'}</li>)}
|
||||
</ul>
|
||||
</p>
|
||||
:
|
||||
<p>
|
||||
Brute forcing uses stolen credentials only. No credentials were supplied during Monkey’s
|
||||
configuration.
|
||||
</p>
|
||||
}
|
||||
{
|
||||
this.state.report.overview.config_exploits.length > 0 ?
|
||||
(
|
||||
this.state.report.overview.config_exploits[0] === 'default' ?
|
||||
''
|
||||
:
|
||||
<p>
|
||||
The Monkey uses the following exploit methods:
|
||||
<ul>
|
||||
{this.state.report.overview.config_exploits.map(x => <li>{x}</li>)}
|
||||
</ul>
|
||||
</p>
|
||||
)
|
||||
:
|
||||
<p>
|
||||
No exploits are used by the Monkey.
|
||||
</p>
|
||||
}
|
||||
{
|
||||
this.state.report.overview.config_ips.length > 0 ?
|
||||
<p>
|
||||
The Monkey scans the following IPs:
|
||||
<ul>
|
||||
{this.state.report.overview.config_ips.map(x => <li>{x}</li>)}
|
||||
</ul>
|
||||
</p>
|
||||
:
|
||||
''
|
||||
}
|
||||
{
|
||||
this.state.report.overview.config_scan ?
|
||||
''
|
||||
:
|
||||
<p>
|
||||
Note: Monkeys were configured to avoid scanning of the local network.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
generateReportFindingsSection() {
|
||||
return (
|
||||
<div id="findings">
|
||||
<h3>
|
||||
Security Findings
|
||||
</h3>
|
||||
<div>
|
||||
<h3>
|
||||
Immediate Threats
|
||||
</h3>
|
||||
{
|
||||
this.state.report.overview.issues.filter(function (x) {
|
||||
return x === true;
|
||||
}).length > 0 ?
|
||||
<div>
|
||||
During this simulated attack the Monkey uncovered <span
|
||||
className="label label-warning">
|
||||
{this.state.report.overview.issues.filter(function (x) {
|
||||
return x === true;
|
||||
}).length} threats</span>:
|
||||
<ul>
|
||||
{this.state.report.overview.issues[this.Issue.STOLEN_CREDS] ?
|
||||
<li>Stolen credentials are used to exploit other machines.</li> : null}
|
||||
{this.state.report.overview.issues[this.Issue.ELASTIC] ?
|
||||
<li>Elasticsearch servers are vulnerable to <a
|
||||
href="https://www.cvedetails.com/cve/cve-2015-1427">CVE-2015-1427</a>.
|
||||
</li> : null}
|
||||
{this.state.report.overview.issues[this.Issue.SAMBACRY] ?
|
||||
<li>Samba servers are vulnerable to ‘SambaCry’ (<a
|
||||
href="https://www.samba.org/samba/security/CVE-2017-7494.html"
|
||||
>CVE-2017-7494</a>).</li> : null}
|
||||
{this.state.report.overview.issues[this.Issue.SHELLSHOCK] ?
|
||||
<li>Machines are vulnerable to ‘Shellshock’ (<a
|
||||
href="https://www.cvedetails.com/cve/CVE-2014-6271">CVE-2014-6271</a>).
|
||||
</li> : null}
|
||||
{this.state.report.overview.issues[this.Issue.CONFICKER] ?
|
||||
<li>Machines are vulnerable to ‘Conficker’ (<a
|
||||
href="https://docs.microsoft.com/en-us/security-updates/SecurityBulletins/2008/ms08-067"
|
||||
>MS08-067</a>).</li> : null}
|
||||
{this.state.report.overview.issues[this.Issue.WEAK_PASSWORD] ?
|
||||
<li>Machines are accessible using passwords supplied by the user during the Monkey’s
|
||||
configuration.</li> : null}
|
||||
</ul>
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
During this simulated attack the Monkey uncovered <span
|
||||
className="label label-success">0 threats</span>.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<h3>
|
||||
Potential Security Issues
|
||||
</h3>
|
||||
{
|
||||
this.state.report.overview.warnings.filter(function (x) {
|
||||
return x === true;
|
||||
}).length > 0 ?
|
||||
<div>
|
||||
The Monkey uncovered the following possible set of issues:
|
||||
<ul>
|
||||
{this.state.report.overview.warnings[this.Warning.CROSS_SEGMENT] ?
|
||||
<li>Weak segmentation - Machines from different segments are able to
|
||||
communicate.</li> : null}
|
||||
{this.state.report.overview.warnings[this.Warning.TUNNEL] ?
|
||||
<li>Weak segmentation - machines were able to communicate over unused ports.</li> : null}
|
||||
</ul>
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
The Monkey did not find any issues.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
generateReportRecommendationsSection() {
|
||||
return (
|
||||
<div id="recommendations">
|
||||
<h3>
|
||||
Recommendations
|
||||
</h3>
|
||||
<div>
|
||||
{this.generateIssues(this.state.report.recommendations.issues)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
generateReportGlanceSection() {
|
||||
let exploitPercentage =
|
||||
(100 * this.state.report.glance.exploited.length) / this.state.report.glance.scanned.length;
|
||||
return (
|
||||
<div id="glance">
|
||||
<h3>
|
||||
The Network from the Monkey's Eyes
|
||||
</h3>
|
||||
<div>
|
||||
<p>
|
||||
The Monkey discovered <span
|
||||
className="label label-warning">{this.state.report.glance.scanned.length}</span> machines and
|
||||
successfully breached <span
|
||||
className="label label-danger">{this.state.report.glance.exploited.length}</span> of them.
|
||||
</p>
|
||||
<div className="text-center" style={{margin: '10px'}}>
|
||||
<Line style={{width: '300px', marginRight: '5px'}} percent={exploitPercentage} strokeWidth="4"
|
||||
trailWidth="4"
|
||||
strokeColor="#d9534f" trailColor="#f0ad4e"/>
|
||||
<b>{Math.round(exploitPercentage)}% of scanned machines exploited</b>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
From the attacker's point of view, the network looks like this:
|
||||
</p>
|
||||
<div className="map-legend">
|
||||
<b>Legend: </b>
|
||||
<span>Exploit <i className="fa fa-lg fa-minus" style={{color: '#cc0200'}}/></span>
|
||||
<b style={{color: '#aeaeae'}}> | </b>
|
||||
<span>Scan <i className="fa fa-lg fa-minus" style={{color: '#ff9900'}}/></span>
|
||||
<b style={{color: '#aeaeae'}}> | </b>
|
||||
<span>Tunnel <i className="fa fa-lg fa-minus" style={{color: '#0158aa'}}/></span>
|
||||
<b style={{color: '#aeaeae'}}> | </b>
|
||||
<span>Island Communication <i className="fa fa-lg fa-minus" style={{color: '#a9aaa9'}}/></span>
|
||||
</div>
|
||||
<div style={{position: 'relative', height: '80vh'}}>
|
||||
<ReactiveGraph graph={this.state.graph} options={options}/>
|
||||
</div>
|
||||
<div style={{marginBottom: '20px'}}>
|
||||
<BreachedServers data={this.state.report.glance.exploited}/>
|
||||
</div>
|
||||
<div style={{marginBottom: '20px'}}>
|
||||
<ScannedServers data={this.state.report.glance.scanned}/>
|
||||
</div>
|
||||
<div>
|
||||
<StolenPasswords data={this.state.report.glance.stolen_creds}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
generateReportFooter() {
|
||||
return (
|
||||
<div id="footer" className="text-center" style={{marginTop: '20px'}}>
|
||||
For questions, suggestions or any other feedback
|
||||
contact: <a href="mailto://labs@guardicore.com" className="no-print">labs@guardicore.com</a>
|
||||
<div className="force-print" style={{display: 'none'}}>labs@guardicore.com</div>
|
||||
<img src={guardicoreLogoImage} alt="GuardiCore" className="center-block" style={{height: '50px'}}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
generateInfoBadges(data_array) {
|
||||
return data_array.map(badge_data => <span className="label label-info" style={{margin: '2px'}}>{badge_data}</span>);
|
||||
}
|
||||
|
||||
generateShellshockPathListBadges(paths) {
|
||||
return paths.map(path => <span className="label label-warning" style={{margin: '2px'}}>{path}</span>);
|
||||
}
|
||||
|
||||
generateSmbPasswordIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password
|
||||
that is not shared with other computers on the network.
|
||||
<CollapsibleWellComponent>
|
||||
The machine <span className="label label-primary">{issue.machine}</span> (<span
|
||||
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||
className="label label-danger">SMB</span> attack.
|
||||
<br/>
|
||||
The Monkey authenticated over the SMB protocol with user <span
|
||||
className="label label-success">{issue.username}</span> and its password.
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
generateSmbPthIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password
|
||||
that is not shared with other computers on the network.
|
||||
<CollapsibleWellComponent>
|
||||
The machine <span className="label label-primary">{issue.machine}</span> (<span
|
||||
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||
className="label label-danger">SMB</span> attack.
|
||||
<br/>
|
||||
The Monkey used a pass-the-hash attack over SMB protocol with user <span
|
||||
className="label label-success">{issue.username}</span>.
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
generateWmiPasswordIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password
|
||||
that is not shared with other computers on the network.
|
||||
<CollapsibleWellComponent>
|
||||
The machine <span className="label label-primary">{issue.machine}</span> (<span
|
||||
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||
className="label label-danger">WMI</span> attack.
|
||||
<br/>
|
||||
The Monkey authenticated over the WMI protocol with user <span
|
||||
className="label label-success">{issue.username}</span> and its password.
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
generateWmiPthIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password
|
||||
that is not shared with other computers on the network.
|
||||
<CollapsibleWellComponent>
|
||||
The machine <span className="label label-primary">{issue.machine}</span> (<span
|
||||
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||
className="label label-danger">WMI</span> attack.
|
||||
<br/>
|
||||
The Monkey used a pass-the-hash attack over WMI protocol with user <span
|
||||
className="label label-success">{issue.username}</span>.
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
generateSshIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password
|
||||
that is not shared with other computers on the network.
|
||||
<CollapsibleWellComponent>
|
||||
The machine <span className="label label-primary">{issue.machine}</span> (<span
|
||||
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||
className="label label-danger">SSH</span> attack.
|
||||
<br/>
|
||||
The Monkey authenticated over the SSH protocol with user <span
|
||||
className="label label-success">{issue.username}</span> and its password.
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
generateRdpIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password
|
||||
that is not shared with other computers on the network.
|
||||
<CollapsibleWellComponent>
|
||||
The machine <span className="label label-primary">{issue.machine}</span> (<span
|
||||
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||
className="label label-danger">RDP</span> attack.
|
||||
<br/>
|
||||
The Monkey authenticated over the RDP protocol with user <span
|
||||
className="label label-success">{issue.username}</span> and its password.
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
generateSambaCryIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password
|
||||
that is not shared with other computers on the network.
|
||||
<br/>
|
||||
Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up.
|
||||
<CollapsibleWellComponent>
|
||||
The machine <span className="label label-primary">{issue.machine}</span> (<span
|
||||
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||
className="label label-danger">SambaCry</span> attack.
|
||||
<br/>
|
||||
The Monkey authenticated over the SMB protocol with user <span
|
||||
className="label label-success">{issue.username}</span> and its password, and used the SambaCry
|
||||
vulnerability.
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
generateElasticIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
Update your Elastic Search server to version 1.4.3 and up.
|
||||
<CollapsibleWellComponent>
|
||||
The machine <span className="label label-primary">{issue.machine}</span> (<span
|
||||
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to an <span
|
||||
className="label label-danger">Elastic Groovy</span> attack.
|
||||
<br/>
|
||||
The attack was made possible because the Elastic Search server was not patched against CVE-2015-1427.
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
generateShellshockIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
Update your Bash to a ShellShock-patched version.
|
||||
<CollapsibleWellComponent>
|
||||
The machine <span className="label label-primary">{issue.machine}</span> (<span
|
||||
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||
className="label label-danger">ShellShock</span> attack.
|
||||
<br/>
|
||||
The attack was made possible because the HTTP server running on TCP port <span
|
||||
className="label label-info">{issue.port}</span> was vulnerable to a shell injection attack on the
|
||||
paths: {this.generateShellshockPathListBadges(issue.paths)}.
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
generateConfickerIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
Install the latest Windows updates or upgrade to a newer operating system.
|
||||
<CollapsibleWellComponent>
|
||||
The machine <span className="label label-primary">{issue.machine}</span> (<span
|
||||
className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||
className="label label-danger">Conficker</span> attack.
|
||||
<br/>
|
||||
The attack was made possible because the target machine used an outdated and unpatched operating system
|
||||
vulnerable to Conficker.
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
generateCrossSegmentIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
Segment your network and make sure there is no communication between machines from different segments.
|
||||
<CollapsibleWellComponent>
|
||||
The network can probably be segmented. A monkey instance on <span
|
||||
className="label label-primary">{issue.machine}</span> in the
|
||||
networks {this.generateInfoBadges(issue.networks)}
|
||||
could directly access the Monkey Island C&C server in the
|
||||
networks {this.generateInfoBadges(issue.server_networks)}.
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
generateTunnelIssue(issue) {
|
||||
return (
|
||||
<li>
|
||||
Use micro-segmentation policies to disable communication other than the required.
|
||||
<CollapsibleWellComponent>
|
||||
Machines are not locked down at port level. Network tunnel was set up from <span
|
||||
className="label label-primary">{issue.machine}</span> to <span
|
||||
className="label label-primary">{issue.dest}</span>.
|
||||
</CollapsibleWellComponent>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
generateIssue = (issue) => {
|
||||
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 data;
|
||||
};
|
||||
|
||||
generateIssues = (issues) => {
|
||||
let issuesDivArray = [];
|
||||
for (let machine of Object.keys(issues)) {
|
||||
issuesDivArray.push(
|
||||
<li>
|
||||
<h4><b>{machine}</b></h4>
|
||||
<ol>
|
||||
{issues[machine].map(this.generateIssue)}
|
||||
</ol>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
return <ul>{issuesDivArray}</ul>;
|
||||
};
|
||||
}
|
||||
|
||||
export default ReportPageComponent;
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import React from 'react';
|
||||
import ReactTable from 'react-table'
|
||||
|
||||
let renderArray = function(val) {
|
||||
if (val.length === 0) {
|
||||
return '';
|
||||
}
|
||||
return val.reduce((total, new_str) => total + ', ' + new_str);
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
Header: 'Breached Servers',
|
||||
columns: [
|
||||
{Header: 'Machine', accessor: 'label'},
|
||||
{Header: 'IP Addresses', id: 'ip_addresses', accessor: x => renderArray(x.ip_addresses)},
|
||||
{Header: 'Exploits', id: 'exploits', accessor: x => renderArray(x.exploits)}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const pageSize = 10;
|
||||
|
||||
class BreachedServersComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
let defaultPageSize = this.props.data.length > pageSize ? pageSize : this.props.data.length;
|
||||
let showPagination = this.props.data.length > pageSize;
|
||||
return (
|
||||
<div className="data-table-container">
|
||||
<ReactTable
|
||||
columns={columns}
|
||||
data={this.props.data}
|
||||
showPagination={showPagination}
|
||||
defaultPageSize={defaultPageSize}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default BreachedServersComponent;
|
|
@ -0,0 +1,40 @@
|
|||
import React from 'react';
|
||||
import {Button, Collapse, Well} from 'react-bootstrap';
|
||||
|
||||
class CollapsibleWellComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
open: false
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
let well =
|
||||
(
|
||||
<Well style={{margin: '10px'}}>
|
||||
{this.props.children}
|
||||
</Well>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="no-print">
|
||||
<Button onClick={() => this.setState({open: !this.state.open})} bsStyle="link">
|
||||
Read More...
|
||||
</Button>
|
||||
<Collapse in={this.state.open}>
|
||||
<div>
|
||||
{well}
|
||||
</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
<div className="force-print" style={{display: 'none'}}>
|
||||
{well}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default CollapsibleWellComponent;
|
|
@ -0,0 +1,46 @@
|
|||
import React from 'react';
|
||||
import ReactTable from 'react-table'
|
||||
|
||||
let renderArray = function(val) {
|
||||
if (val.length === 0) {
|
||||
return '';
|
||||
}
|
||||
return val.reduce((total, new_str) => total + ', ' + new_str);
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
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;
|
||||
|
||||
class ScannedServersComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
let defaultPageSize = this.props.data.length > pageSize ? pageSize : this.props.data.length;
|
||||
let showPagination = this.props.data.length > pageSize;
|
||||
return (
|
||||
<div className="data-table-container">
|
||||
<ReactTable
|
||||
columns={columns}
|
||||
data={this.props.data}
|
||||
showPagination={showPagination}
|
||||
defaultPageSize={defaultPageSize}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ScannedServersComponent;
|
|
@ -0,0 +1,38 @@
|
|||
import React from 'react';
|
||||
import ReactTable from 'react-table'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
Header: 'Stolen Credentials',
|
||||
columns: [
|
||||
{ Header: 'Username', accessor: 'username'},
|
||||
{ Header: 'Type', accessor: 'type'},
|
||||
{ Header: 'Stolen From', accessor: 'origin'}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const pageSize = 10;
|
||||
|
||||
class StolenPasswordsComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
let defaultPageSize = this.props.data.length > pageSize ? pageSize : this.props.data.length;
|
||||
let showPagination = this.props.data.length > pageSize;
|
||||
return (
|
||||
<div className="data-table-container">
|
||||
<ReactTable
|
||||
columns={columns}
|
||||
data={this.props.data}
|
||||
showPagination={showPagination}
|
||||
defaultPageSize={defaultPageSize}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default StolenPasswordsComponent;
|
|
@ -0,0 +1,32 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 97.577 11.46">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #fff;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<title>14cbedff-3eed-4f8f-abb7-fffe92867ded</title>
|
||||
<g>
|
||||
<path class="cls-1" d="M89.22,6.874l1.819-.159a3.153,3.153,0,1,1,.124-1.065,2.306,2.306,0,0,1-.005.492l-4.489.393a1.257,1.257,0,0,0,1.414.989,1.417,1.417,0,0,0,1.06-.56A.6.6,0,0,1,89.22,6.874Zm.087-1.767a1.311,1.311,0,0,0-1.443-.916,1.282,1.282,0,0,0-1.249,1.151Z"/>
|
||||
<path class="cls-1" d="M67.715,4.191a1.622,1.622,0,1,1-1.622,1.622,1.624,1.624,0,0,1,1.622-1.622m0-1.542a3.164,3.164,0,1,0,3.164,3.164A3.164,3.164,0,0,0,67.715,2.65Z"/>
|
||||
<path class="cls-1" d="M54.893,8.808,55.53.648A6.958,6.958,0,0,1,56.736.564a6.743,6.743,0,0,1,1.193.084L58.7,3.7q.324,1.332.456,2.148h.071q.132-.816.456-2.148L60.45.648a6.914,6.914,0,0,1,1.2-.084,6.7,6.7,0,0,1,1.188.084l.636,8.16a3.218,3.218,0,0,1-.863.1,5.125,5.125,0,0,1-.864-.06l-.192-3.48q-.1-1.656-.1-2.724h-.084L60.126,7.62a5.447,5.447,0,0,1-.93.06,5.9,5.9,0,0,1-.954-.06L57.006,2.64h-.084q-.023,1.464-.1,2.724l-.192,3.48a5.125,5.125,0,0,1-.864.06A3.366,3.366,0,0,1,54.893,8.808Z"/>
|
||||
<path class="cls-1" d="M77.094,4.86v2.5a2.089,2.089,0,0,0,.288,1.188,1.611,1.611,0,0,1-1.05.384.983.983,0,0,1-.8-.276,1.321,1.321,0,0,1-.223-.84V5.2a1.33,1.33,0,0,0-.12-.648.446.446,0,0,0-.42-.2,1.386,1.386,0,0,0-.983.468v4.02A4.716,4.716,0,0,1,72.9,8.9a5.113,5.113,0,0,1-.906-.072V2.856l.084-.084h.672a.9.9,0,0,1,.948.72,2.669,2.669,0,0,1,1.7-.744,1.477,1.477,0,0,1,1.26.576A2.5,2.5,0,0,1,77.094,4.86Z"/>
|
||||
<path class="cls-1" d="M81.3,6.408l-.4.012V8.832a4.606,4.606,0,0,1-.864.072,5,5,0,0,1-.888-.072V.084L79.241,0h.7a.973.973,0,0,1,.75.246,1.243,1.243,0,0,1,.222.834V4.944h.216a.369.369,0,0,0,.336-.216l.685-1.26a1.062,1.062,0,0,1,1.008-.66q.347,0,1,.024l.071.1-.972,1.824a1.885,1.885,0,0,1-.6.672,1.7,1.7,0,0,1,1.02,1.044l.3.888a4.932,4.932,0,0,0,.307.708.941.941,0,0,0,.27.3,1.512,1.512,0,0,1-1.2.564.839.839,0,0,1-.582-.18,1.534,1.534,0,0,1-.354-.636l-.36-1.056a1.225,1.225,0,0,0-.306-.516A.675.675,0,0,0,81.3,6.408Z"/>
|
||||
<path class="cls-1" d="M97.577,3.036l-1.5,5.628a5.042,5.042,0,0,1-1,2.1,2.437,2.437,0,0,1-1.9.7,4.322,4.322,0,0,1-1.416-.24V11a2.268,2.268,0,0,1,.09-.51,1.213,1.213,0,0,1,.27-.54,3.946,3.946,0,0,0,1.057.18,1.231,1.231,0,0,0,1.212-1.044l.048-.168q-1.008-.024-1.2-.744L91.805,2.988a2.4,2.4,0,0,1,.989-.24,1.049,1.049,0,0,1,.666.168,1.108,1.108,0,0,1,.313.6l.6,2.376q.1.348.288,1.6a.094.094,0,0,0,.108.084l1.08-4.716a3.485,3.485,0,0,1,.786-.072,2.786,2.786,0,0,1,.882.132Z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="cls-1" d="M0,8.937V.741A1.882,1.882,0,0,1,.414.705,2.229,2.229,0,0,1,.852.741v8.2a2.231,2.231,0,0,1-.432.036A2.056,2.056,0,0,1,0,8.937Z"/>
|
||||
<path class="cls-1" d="M8.232,4.809V7.833a2.871,2.871,0,0,0,.12,1,.7.7,0,0,1-.517.18q-.42,0-.42-.54V5.085a2.2,2.2,0,0,0-.191-1.074.747.747,0,0,0-.7-.318,2.332,2.332,0,0,0-1.044.27,2.7,2.7,0,0,0-.888.69V8.937a1.994,1.994,0,0,1-.408.036,1.987,1.987,0,0,1-.408-.036V3.093l.072-.072h.312a.384.384,0,0,1,.348.132.831.831,0,0,1,.085.432v.24a2.983,2.983,0,0,1,.966-.636A2.908,2.908,0,0,1,6.7,2.949a1.335,1.335,0,0,1,1.17.516A2.272,2.272,0,0,1,8.232,4.809Z"/>
|
||||
<path class="cls-1" d="M11.147,8.937V3.669l-1.031.012a1.473,1.473,0,0,1-.036-.312,1.639,1.639,0,0,1,.036-.324l1,.024q-.085-1.115-.084-1.272A1.769,1.769,0,0,1,11.472.555,1.538,1.538,0,0,1,12.666.069a3.728,3.728,0,0,1,1.386.252.818.818,0,0,1-.2.564A3.881,3.881,0,0,0,12.756.693a.883.883,0,0,0-.708.294,1.226,1.226,0,0,0-.252.822,9.977,9.977,0,0,0,.1,1.26l1.776-.024a1.716,1.716,0,0,1,.036.324,1.54,1.54,0,0,1-.036.312l-1.716-.012V8.937a2.258,2.258,0,0,1-.8,0Z"/>
|
||||
<path class="cls-1" d="M32.352,3.045a1.716,1.716,0,0,1,.036.324,1.54,1.54,0,0,1-.036.312l-1.524-.012v4.14q0,.492.468.492h.925a1,1,0,0,1,.071.372,1.405,1.405,0,0,1-.012.24,8.879,8.879,0,0,1-1.122.072,1.268,1.268,0,0,1-.846-.246.87.87,0,0,1-.288-.7V3.669l-.828.012a1.336,1.336,0,0,1-.036-.306,1.664,1.664,0,0,1,.036-.33l.828.012v-1.3a.794.794,0,0,1,.09-.432.4.4,0,0,1,.354-.132h.288l.072.072v1.8Z"/>
|
||||
<path class="cls-1" d="M35.472,3.837v5.1a2.324,2.324,0,0,1-.816,0V4.257a.7.7,0,0,0-.108-.456.5.5,0,0,0-.384-.12h-.107a1.288,1.288,0,0,1-.036-.294,1.616,1.616,0,0,1,.036-.318,4.4,4.4,0,0,1,.575-.048h.1a.7.7,0,0,1,.546.216A.856.856,0,0,1,35.472,3.837ZM34.379,1.881a.986.986,0,0,1-.084-.438.837.837,0,0,1,.084-.414.853.853,0,0,1,.511-.12.8.8,0,0,1,.5.12.837.837,0,0,1,.084.414.986.986,0,0,1-.084.438A.707.707,0,0,1,34.9,2,.874.874,0,0,1,34.379,1.881Z"/>
|
||||
<path class="cls-1" d="M49.139,4.809V7.833a2.871,2.871,0,0,0,.12,1,.7.7,0,0,1-.517.18q-.42,0-.42-.54V5.085a2.2,2.2,0,0,0-.191-1.074.747.747,0,0,0-.7-.318,2.332,2.332,0,0,0-1.044.27,2.7,2.7,0,0,0-.888.69V8.937a2.324,2.324,0,0,1-.816,0V3.093l.072-.072h.312a.384.384,0,0,1,.348.132.831.831,0,0,1,.085.432v.24a2.983,2.983,0,0,1,.966-.636,2.908,2.908,0,0,1,1.134-.24,1.335,1.335,0,0,1,1.17.516A2.272,2.272,0,0,1,49.139,4.809Z"/>
|
||||
<path class="cls-1" d="M40.075,3.555a2.364,2.364,0,1,1-2.364,2.364,2.367,2.367,0,0,1,2.364-2.364m0-.8a3.164,3.164,0,1,0,3.164,3.164,3.164,3.164,0,0,0-3.164-3.164Z"/>
|
||||
<path class="cls-1" d="M25.315,8.285a2.349,2.349,0,0,1-2.04-1.18,2.335,2.335,0,0,1,0-2.36,2.344,2.344,0,0,1,4.08,0l.7-.4a3.16,3.16,0,1,0,0,3.16l-.7-.4A2.35,2.35,0,0,1,25.315,8.285Z"/>
|
||||
<g>
|
||||
<path class="cls-1" d="M17.87,8.285a2.365,2.365,0,1,1,2.36-2.37h.8a3.162,3.162,0,0,0-5.9-1.58,3.018,3.018,0,0,0-.43,1.58,3.165,3.165,0,0,0,5.91,1.58l-.69-.39A2.378,2.378,0,0,1,17.87,8.285Z"/>
|
||||
<polygon class="cls-1" points="15.286 5.718 20.514 5.261 21.03 5.915 15.347 6.412 15.286 5.718"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 38 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 55 KiB |
|
@ -2,6 +2,7 @@
|
|||
@import url('https://fonts.googleapis.com/css?family=Alegreya');
|
||||
|
||||
/* Base Application Styles */
|
||||
|
||||
body {
|
||||
color: #fff;
|
||||
background: #222;
|
||||
|
@ -23,7 +24,7 @@ body {
|
|||
left: 0;
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
padding: 1em;
|
||||
padding: 0px !important;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
||||
background-color: #f5f5f5;
|
||||
|
@ -34,6 +35,8 @@ body {
|
|||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
background-color: #ffcc00;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.header img {
|
||||
|
@ -42,11 +45,12 @@ body {
|
|||
|
||||
.navigation {
|
||||
margin-top: 1em;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
li {
|
||||
|
@ -54,30 +58,29 @@ body {
|
|||
}
|
||||
|
||||
li .number {
|
||||
color: #666;
|
||||
display: inline-block;
|
||||
width: 1.1em;
|
||||
}
|
||||
|
||||
li a {
|
||||
/*color: #d30d09;*/
|
||||
color: #666;
|
||||
color: #333333;
|
||||
display: block;
|
||||
padding: 0.5em 1em;
|
||||
margin: 0.1em 0;
|
||||
}
|
||||
|
||||
li a:hover {
|
||||
color: #000;
|
||||
background: #e9e9e9;
|
||||
text-decoration: none;
|
||||
}
|
||||
li a.active {
|
||||
background: #fff;
|
||||
background: #333333;
|
||||
text-decoration: none;
|
||||
color: #d30d09;
|
||||
color: #ffcc00;
|
||||
}
|
||||
li a.active:hover {
|
||||
color: #d30d09;
|
||||
color: #ffcc00;
|
||||
}
|
||||
li a.disabled {
|
||||
color: #666;
|
||||
|
@ -92,6 +95,7 @@ body {
|
|||
li .checkmark {
|
||||
font-size: 1.3em;
|
||||
margin-right: -10px;
|
||||
color: #ffcc00;
|
||||
}
|
||||
|
||||
hr {
|
||||
|
@ -122,9 +126,9 @@ body {
|
|||
margin-top: 0;
|
||||
margin-bottom: 1em;
|
||||
padding-bottom: 0.5em;
|
||||
border-bottom: 2px dotted #d30d09;
|
||||
border-bottom: 2px dotted #ffcc00;
|
||||
font-size: 2.5em;
|
||||
color: #d30d09;
|
||||
color: #ffcc00;
|
||||
font-family: 'Alegreya', serif;
|
||||
}
|
||||
|
||||
|
@ -276,13 +280,14 @@ body {
|
|||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 70px;
|
||||
height: 130px;
|
||||
background: rgba(0,0,0,0.7);
|
||||
border-radius: 5px;
|
||||
border: 3px solid #aaa;
|
||||
padding: 0.5em;
|
||||
color: white;
|
||||
font-family: Consolas, "Courier New", monospace;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.telemetry-console .date {
|
||||
|
@ -363,3 +368,123 @@ body {
|
|||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Report page */
|
||||
|
||||
.report-page {
|
||||
font-size: 1.2em;
|
||||
border: 1px solid #fff;
|
||||
padding: 2em;
|
||||
-webkit-box-shadow: 1px 1px 7px -1px #ccc;
|
||||
box-shadow: 1px 1px 7px -1px #ccc;
|
||||
}
|
||||
|
||||
.report-page h1 {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.report-page h3 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.report-page h4 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.report-page ul {
|
||||
list-style: disc;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
.report-page li {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.report-page li a {
|
||||
display: inline;
|
||||
padding: 0em;
|
||||
}
|
||||
|
||||
/* Print report styling */
|
||||
|
||||
@media print {
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no-print {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.force-print {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,9 @@ import sys
|
|||
import array
|
||||
|
||||
import struct
|
||||
import ipaddress
|
||||
from netifaces import interfaces, ifaddresses, AF_INET
|
||||
|
||||
from cc.database import mongo
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
||||
|
||||
|
@ -56,3 +55,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
|
||||
|
|
|
@ -2,16 +2,17 @@
|
|||
|
||||
MONKEY_FOLDER=/var/monkey_island
|
||||
INSTALLATION_FOLDER=/var/monkey_island/installation
|
||||
PYTHON_FOLDER=/var/monkey_island/bin/python
|
||||
|
||||
cp -f ${MONKEY_FOLDER}/monkey.sh /usr/bin/monkey
|
||||
chmod 755 /usr/bin/monkey
|
||||
|
||||
# Fix dependency bug
|
||||
pip uninstall -y bson
|
||||
# Prepare python virtualenv
|
||||
pip2 install virtualenv --no-index --find-links file://$INSTALLATION_FOLDER
|
||||
virtualenv -p python2.7 ${PYTHON_FOLDER}
|
||||
|
||||
# install pip requirements
|
||||
pip install -r $MONKEY_FOLDER/pip_requirements.txt --no-index --find-links file://$INSTALLATION_FOLDER
|
||||
|
||||
${PYTHON_FOLDER}/bin/python -m pip install -r $MONKEY_FOLDER/pip_requirements.txt --no-index --find-links file://$INSTALLATION_FOLDER
|
||||
|
||||
# remove installation folder and unnecessary files
|
||||
rm -rf ${INSTALLATION_FOLDER}
|
||||
|
|
|
@ -8,4 +8,6 @@ rm -f /etc/init/monkey-mongo.conf
|
|||
[ -f "/lib/systemd/system/monkey-island.service" ] && rm -f /lib/systemd/system/monkey-island.service
|
||||
[ -f "/lib/systemd/system/monkey-mongo.service" ] && rm -f /lib/systemd/system/monkey-mongo.service
|
||||
|
||||
rm -r -f /var/monkey_island
|
||||
|
||||
exit 0
|
|
@ -9,4 +9,7 @@ flask
|
|||
Flask-Pymongo
|
||||
Flask-Restful
|
||||
jsonschema
|
||||
netifaces
|
||||
netifaces
|
||||
ipaddress
|
||||
enum34
|
||||
virtualenv
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/bash
|
||||
|
||||
cd /var/monkey_island/cc
|
||||
python main.py
|
||||
/var/monkey_island/bin/python/bin/python main.py
|
|
@ -9,4 +9,6 @@ flask
|
|||
Flask-Pymongo
|
||||
Flask-Restful
|
||||
jsonschema
|
||||
netifaces
|
||||
netifaces
|
||||
ipaddress
|
||||
enum34
|
|
@ -0,0 +1 @@
|
|||
xcopy %1 %2
|
|
@ -1,3 +1,18 @@
|
|||
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
|
||||
@echo off
|
||||
|
||||
IF [%1] == [] (
|
||||
set mydir=%cd%\
|
||||
) ELSE (
|
||||
set mydir=%~1%
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
"%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"
|
|
@ -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)
|
|
@ -0,0 +1 @@
|
|||
del %1
|
|
@ -1,3 +1,4 @@
|
|||
if not exist db mkdir db
|
||||
start windows\run_mongodb.bat
|
||||
start windows\run_cc.bat
|
||||
start windows\run_cc.bat
|
||||
start https://localhost:5000
|
Loading…
Reference in New Issue