forked from p15670423/monkey
Merge branch 'develop' into SSH_key_stealing
This commit is contained in:
commit
ecdd2e8762
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us fix things!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Configure the Monkey with X settings
|
||||||
|
2. Run the monkey on specific machine
|
||||||
|
3. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Machine version(please complete the following information):**
|
||||||
|
- OS: Windows or Linux
|
|
@ -0,0 +1,27 @@
|
||||||
|
group: travis_latest
|
||||||
|
language: python
|
||||||
|
cache: pip
|
||||||
|
python:
|
||||||
|
- 2.7
|
||||||
|
- 3.6
|
||||||
|
#- nightly
|
||||||
|
#- pypy
|
||||||
|
#- pypy3
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- python: nightly
|
||||||
|
- python: pypy
|
||||||
|
- python: pypy3
|
||||||
|
install:
|
||||||
|
#- pip install -r requirements.txt
|
||||||
|
- pip install flake8 # pytest # add another testing frameworks later
|
||||||
|
before_script:
|
||||||
|
# stop the build if there are Python syntax errors or undefined names
|
||||||
|
- flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
|
||||||
|
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||||
|
- flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||||
|
script:
|
||||||
|
- true # pytest --capture=sys # add other tests here
|
||||||
|
notifications:
|
||||||
|
on_success: change
|
||||||
|
on_failure: change # `always` will be the setting once code changes slow down
|
|
@ -20,7 +20,7 @@ The following is a *short* list of recommendations. PRs that don't match these c
|
||||||
* **Don't** leave your pull request description blank.
|
* **Don't** leave your pull request description blank.
|
||||||
* **Do** license your code as GPLv3.
|
* **Do** license your code as GPLv3.
|
||||||
|
|
||||||
|
Also, please submit PRs to the develop branch.
|
||||||
|
|
||||||
## Issues
|
## Issues
|
||||||
* **Do** write a detailed description of your bug and use a descriptive title.
|
* **Do** write a detailed description of your bug and use a descriptive title.
|
||||||
|
|
|
@ -4,6 +4,7 @@ import struct
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
import ipaddress
|
import ipaddress
|
||||||
|
from six import text_type
|
||||||
|
|
||||||
__author__ = 'itamar'
|
__author__ = 'itamar'
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ class CidrRange(NetworkRange):
|
||||||
def __init__(self, cidr_range, shuffle=True):
|
def __init__(self, cidr_range, shuffle=True):
|
||||||
super(CidrRange, self).__init__(shuffle=shuffle)
|
super(CidrRange, self).__init__(shuffle=shuffle)
|
||||||
self._cidr_range = cidr_range.strip()
|
self._cidr_range = cidr_range.strip()
|
||||||
self._ip_network = ipaddress.ip_network(unicode(self._cidr_range), strict=False)
|
self._ip_network = ipaddress.ip_network(text_type(self._cidr_range), strict=False)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<CidrRange %s>" % (self._cidr_range,)
|
return "<CidrRange %s>" % (self._cidr_range,)
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
FROM debian:jessie-slim
|
||||||
|
|
||||||
|
LABEL MAINTAINER="theonlydoo <theonlydoo@gmail.com>"
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ADD https://github.com/guardicore/monkey/releases/download/1.5.2/infection_monkey_1.5.2_deb.tgz .
|
||||||
|
|
||||||
|
RUN tar xvf infection_monkey_1.5.2_deb.tgz \
|
||||||
|
&& apt-get -yqq update \
|
||||||
|
&& apt-get -yqq upgrade \
|
||||||
|
&& apt-get -yqq install python-pip \
|
||||||
|
libssl-dev \
|
||||||
|
supervisor \
|
||||||
|
&& dpkg -i *.deb
|
||||||
|
|
||||||
|
COPY stack.conf /etc/supervisor/conf.d/stack.conf
|
||||||
|
|
||||||
|
ENTRYPOINT [ "supervisord", "-n", "-c", "/etc/supervisor/supervisord.conf" ]
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Improvements needed
|
||||||
|
|
||||||
|
* Remove embedded mongodb from .deb, it forbids installation on a `debian:stretch` distro.
|
||||||
|
* Package monkey for system's python usage.
|
||||||
|
* Fix package number: (I installed the 1.5.2)
|
||||||
|
```
|
||||||
|
ii gc-monkey-island 1.0 amd64 Guardicore Infection Monkey Island installation package
|
||||||
|
```
|
||||||
|
* Use .deb dependencies for mongodb setup?
|
||||||
|
* Use docker-compose for stack construction.
|
||||||
|
* Remove the .sh script from the systemd unit file (`/var/monkey_island/ubuntu/systemd/start_server.sh`) which only does a `cd && localpython run`
|
|
@ -0,0 +1,4 @@
|
||||||
|
[program:mongod]
|
||||||
|
command=/var/monkey_island/bin/mongodb/bin/mongod --quiet --dbpath /var/monkey_island/db
|
||||||
|
[program:monkey]
|
||||||
|
command=/var/monkey_island/ubuntu/systemd/start_server.sh
|
|
@ -40,7 +40,7 @@ def _cast_by_example(value, example):
|
||||||
return int(value)
|
return int(value)
|
||||||
elif example_type is float:
|
elif example_type is float:
|
||||||
return float(value)
|
return float(value)
|
||||||
elif example_type is types.ClassType or example_type is ABCMeta:
|
elif example_type in (type, ABCMeta):
|
||||||
return globals()[value]
|
return globals()[value]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
@ -84,10 +84,10 @@ class Configuration(object):
|
||||||
if val_type is types.FunctionType or val_type is types.MethodType:
|
if val_type is types.FunctionType or val_type is types.MethodType:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if val_type is types.ClassType or val_type is ABCMeta:
|
if val_type in (type, ABCMeta):
|
||||||
value = value.__name__
|
value = value.__name__
|
||||||
elif val_type is tuple or val_type is list:
|
elif val_type is tuple or val_type is list:
|
||||||
if len(value) != 0 and (type(value[0]) is types.ClassType or type(value[0]) is ABCMeta):
|
if len(value) != 0 and type(value[0]) in (type, ABCMeta):
|
||||||
value = val_type([x.__name__ for x in value])
|
value = val_type([x.__name__ for x in value])
|
||||||
|
|
||||||
result[key] = value
|
result[key] = value
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
"41.50.73.31:5000"
|
"41.50.73.31:5000"
|
||||||
],
|
],
|
||||||
"internet_services": [
|
"internet_services": [
|
||||||
"monkey.guardicore.com",
|
"monkey.guardicore.com",
|
||||||
"www.google.com"
|
"www.google.com"
|
||||||
],
|
],
|
||||||
"keep_tunnel_open_time": 60,
|
"keep_tunnel_open_time": 60,
|
||||||
"subnet_scan_list": [
|
"subnet_scan_list": [
|
||||||
""
|
""
|
||||||
|
@ -36,15 +36,15 @@
|
||||||
"WmiExploiter",
|
"WmiExploiter",
|
||||||
"ShellShockExploiter",
|
"ShellShockExploiter",
|
||||||
"ElasticGroovyExploiter",
|
"ElasticGroovyExploiter",
|
||||||
"SambaCryExploiter",
|
"SambaCryExploiter"
|
||||||
],
|
],
|
||||||
"finger_classes": [
|
"finger_classes": [
|
||||||
"SSHFinger",
|
"SSHFinger",
|
||||||
"PingScanner",
|
"PingScanner",
|
||||||
"HTTPFinger",
|
"HTTPFinger",
|
||||||
"SMBFinger",
|
"SMBFinger",
|
||||||
"MySQLFinger"
|
"MySQLFinger",
|
||||||
"ElasticFinger",
|
"ElasticFinger"
|
||||||
],
|
],
|
||||||
"max_iterations": 3,
|
"max_iterations": 3,
|
||||||
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||||
|
@ -91,4 +91,4 @@
|
||||||
"use_file_logging": true,
|
"use_file_logging": true,
|
||||||
"victims_max_exploit": 7,
|
"victims_max_exploit": 7,
|
||||||
"victims_max_find": 30
|
"victims_max_find": 30
|
||||||
}
|
}
|
|
@ -27,7 +27,7 @@ LOG = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def twisted_log_func(*message, **kw):
|
def twisted_log_func(*message, **kw):
|
||||||
if kw.has_key('isError') and kw['isError']:
|
if kw.get('isError'):
|
||||||
error_msg = 'Unknown'
|
error_msg = 'Unknown'
|
||||||
if 'failure' in kw:
|
if 'failure' in kw:
|
||||||
error_msg = kw['failure'].getErrorMessage()
|
error_msg = kw['failure'].getErrorMessage()
|
||||||
|
|
|
@ -8,7 +8,7 @@ import requests
|
||||||
|
|
||||||
from exploit import HostExploiter
|
from exploit import HostExploiter
|
||||||
from exploit.tools import get_target_monkey, HTTPTools, get_monkey_depth
|
from exploit.tools import get_target_monkey, HTTPTools, get_monkey_depth
|
||||||
from model import MONKEY_ARG
|
from model import DROPPER_ARG
|
||||||
from shellshock_resources import CGI_FILES
|
from shellshock_resources import CGI_FILES
|
||||||
from tools import build_monkey_commandline
|
from tools import build_monkey_commandline
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ class ShellShockExploiter(HostExploiter):
|
||||||
self.attack_page(url, header, run_path)
|
self.attack_page(url, header, run_path)
|
||||||
|
|
||||||
# run the monkey
|
# run the monkey
|
||||||
cmdline = "%s %s" % (dropper_target_path_linux, MONKEY_ARG)
|
cmdline = "%s %s" % (dropper_target_path_linux, DROPPER_ARG)
|
||||||
cmdline += build_monkey_commandline(self.host, get_monkey_depth() - 1) + ' & '
|
cmdline += build_monkey_commandline(self.host, get_monkey_depth() - 1) + ' & '
|
||||||
run_path = exploit + cmdline
|
run_path = exploit + cmdline
|
||||||
self.attack_page(url, header, run_path)
|
self.attack_page(url, header, run_path)
|
||||||
|
|
|
@ -405,7 +405,7 @@ def get_interface_to_target(dst):
|
||||||
for d, m, gw, i, a in routes:
|
for d, m, gw, i, a in routes:
|
||||||
aa = atol(a)
|
aa = atol(a)
|
||||||
if aa == dst:
|
if aa == dst:
|
||||||
pathes.append((0xffffffffL, ("lo", a, "0.0.0.0")))
|
pathes.append((0xffffffff, ("lo", a, "0.0.0.0")))
|
||||||
if (dst & m) == (d & m):
|
if (dst & m) == (d & m):
|
||||||
pathes.append((m, (i, a, gw)))
|
pathes.append((m, (i, a, gw)))
|
||||||
if not pathes:
|
if not pathes:
|
||||||
|
|
|
@ -12,6 +12,7 @@ from control import ControlClient
|
||||||
from model import DELAY_DELETE_CMD
|
from model import DELAY_DELETE_CMD
|
||||||
from network.firewall import app as firewall
|
from network.firewall import app as firewall
|
||||||
from network.network_scanner import NetworkScanner
|
from network.network_scanner import NetworkScanner
|
||||||
|
from six.moves import xrange
|
||||||
from system_info import SystemInfoCollector
|
from system_info import SystemInfoCollector
|
||||||
from system_singleton import SystemSingleton
|
from system_singleton import SystemSingleton
|
||||||
from windows_upgrader import WindowsUpgrader
|
from windows_upgrader import WindowsUpgrader
|
||||||
|
|
|
@ -10,6 +10,11 @@ from subprocess import check_output
|
||||||
from random import randint
|
from random import randint
|
||||||
from common.network.network_range import CidrRange
|
from common.network.network_range import CidrRange
|
||||||
|
|
||||||
|
try:
|
||||||
|
long # Python 2
|
||||||
|
except NameError:
|
||||||
|
long = int # Python 3
|
||||||
|
|
||||||
|
|
||||||
def get_host_subnets():
|
def get_host_subnets():
|
||||||
"""
|
"""
|
||||||
|
@ -93,8 +98,8 @@ else:
|
||||||
ifaddr = socket.inet_ntoa(ifreq[20:24])
|
ifaddr = socket.inet_ntoa(ifreq[20:24])
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
routes.append((socket.htonl(long(dst, 16)) & 0xffffffffL,
|
routes.append((socket.htonl(long(dst, 16)) & 0xffffffff,
|
||||||
socket.htonl(long(msk, 16)) & 0xffffffffL,
|
socket.htonl(long(msk, 16)) & 0xffffffff,
|
||||||
socket.inet_ntoa(struct.pack("I", long(gw, 16))),
|
socket.inet_ntoa(struct.pack("I", long(gw, 16))),
|
||||||
iff, ifaddr))
|
iff, ifaddr))
|
||||||
|
|
||||||
|
|
|
@ -144,13 +144,13 @@ class SMBFinger(HostFinger):
|
||||||
host.os['type'] = 'linux'
|
host.os['type'] = 'linux'
|
||||||
|
|
||||||
host.services[SMB_SERVICE]['name'] = service_client
|
host.services[SMB_SERVICE]['name'] = service_client
|
||||||
if not host.os.has_key('version'):
|
if 'version' not in host.os:
|
||||||
host.os['version'] = os_version
|
host.os['version'] = os_version
|
||||||
else:
|
else:
|
||||||
host.services[SMB_SERVICE]['os-version'] = os_version
|
host.services[SMB_SERVICE]['os-version'] = os_version
|
||||||
|
|
||||||
return True
|
return True
|
||||||
except Exception, exc:
|
except Exception as exc:
|
||||||
LOG.debug("Error getting smb fingerprint: %s", exc)
|
LOG.debug("Error getting smb fingerprint: %s", exc)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -10,8 +10,7 @@ odict
|
||||||
paramiko
|
paramiko
|
||||||
psutil==3.4.2
|
psutil==3.4.2
|
||||||
PyInstaller
|
PyInstaller
|
||||||
|
six
|
||||||
ecdsa
|
ecdsa
|
||||||
netifaces
|
netifaces
|
||||||
mock
|
ipaddress
|
||||||
nose
|
|
||||||
ipaddress
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ class MimikatzCollector(object):
|
||||||
self._collect = collect_proto(("collect", self._dll))
|
self._collect = collect_proto(("collect", self._dll))
|
||||||
self._get = get_proto(("get", self._dll))
|
self._get = get_proto(("get", self._dll))
|
||||||
self._isInit = True
|
self._isInit = True
|
||||||
except StandardError:
|
except Exception:
|
||||||
LOG.exception("Error initializing mimikatz collector")
|
LOG.exception("Error initializing mimikatz collector")
|
||||||
|
|
||||||
def get_logon_info(self):
|
def get_logon_info(self):
|
||||||
|
@ -71,7 +71,7 @@ class MimikatzCollector(object):
|
||||||
logon_data_dictionary[username]["ntlm_hash"] = ntlm_hash
|
logon_data_dictionary[username]["ntlm_hash"] = ntlm_hash
|
||||||
|
|
||||||
return logon_data_dictionary
|
return logon_data_dictionary
|
||||||
except StandardError:
|
except Exception:
|
||||||
LOG.exception("Error getting logon info")
|
LOG.exception("Error getting logon info")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
# -*- coding: UTF-8 -*-
|
|
||||||
# NOTE: Launch all tests with `nosetests` command from infection_monkey dir.
|
|
||||||
|
|
||||||
import json
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from mock import Mock, patch
|
|
||||||
|
|
||||||
import control
|
|
||||||
|
|
||||||
from config import GUID
|
|
||||||
|
|
||||||
|
|
||||||
class ReportConfigErrorTestCase(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
When unknown config variable received form the island server, skip it and report config
|
|
||||||
error back to the server.
|
|
||||||
"""
|
|
||||||
|
|
||||||
config_response = Mock(json=Mock(return_value={'config': {'blah': 'blah'}}))
|
|
||||||
|
|
||||||
def teardown(self):
|
|
||||||
patch.stopall()
|
|
||||||
|
|
||||||
def test_config(self):
|
|
||||||
patch('control.requests.patch', Mock()).start()
|
|
||||||
patch('control.WormConfiguration', Mock(current_server='127.0.0.1:123')).start()
|
|
||||||
|
|
||||||
# GIVEN the server with uknown config variable
|
|
||||||
patch('control.requests.get', Mock(return_value=self.config_response)).start()
|
|
||||||
|
|
||||||
# WHEN monkey tries to load config from server
|
|
||||||
control.ControlClient.load_control_config()
|
|
||||||
|
|
||||||
# THEN she reports config error back to the server
|
|
||||||
control.requests.patch.assert_called_once_with(
|
|
||||||
"https://127.0.0.1:123/api/monkey/%s" % GUID,
|
|
||||||
data=json.dumps({'config_error': True}),
|
|
||||||
headers={'content-type': 'application/json'},
|
|
||||||
verify=False,
|
|
||||||
proxies=control.ControlClient.proxies)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
|
@ -32,12 +32,12 @@ class FTPServer(threading.Thread):
|
||||||
try:
|
try:
|
||||||
func=getattr(self,cmd[:4].strip().upper())
|
func=getattr(self,cmd[:4].strip().upper())
|
||||||
func(cmd)
|
func(cmd)
|
||||||
except Exception,e:
|
except Exception as e:
|
||||||
self.conn.send('500 Sorry.\r\n')
|
self.conn.send('500 Sorry.\r\n')
|
||||||
break
|
break
|
||||||
|
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
self.sock.close()
|
self.sock.close()
|
||||||
|
|
||||||
def SYST(self,cmd):
|
def SYST(self,cmd):
|
||||||
self.conn.send('215 UNIX Type: L8\r\n')
|
self.conn.send('215 UNIX Type: L8\r\n')
|
||||||
|
@ -83,7 +83,7 @@ class FTPServer(threading.Thread):
|
||||||
def PASV(self,cmd):
|
def PASV(self,cmd):
|
||||||
self.pasv_mode = True
|
self.pasv_mode = True
|
||||||
self.servsock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
self.servsock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||||
self.servsock.bind((local_ip,0))
|
self.servsock.bind((self.local_ip,0))
|
||||||
self.servsock.listen(1)
|
self.servsock.listen(1)
|
||||||
ip, port = self.servsock.getsockname()
|
ip, port = self.servsock.getsockname()
|
||||||
self.conn.send('227 Entering Passive Mode (%s,%u,%u).\r\n' %
|
self.conn.send('227 Entering Passive Mode (%s,%u,%u).\r\n' %
|
||||||
|
|
|
@ -122,7 +122,7 @@ class HTTPConnectProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||||
address = (u.hostname, u.port or 443)
|
address = (u.hostname, u.port or 443)
|
||||||
try:
|
try:
|
||||||
conn = socket.create_connection(address)
|
conn = socket.create_connection(address)
|
||||||
except socket.error, e:
|
except socket.error as e:
|
||||||
LOG.debug("HTTPConnectProxyHandler: Got exception while trying to connect to %s: %s" % (repr(address), e))
|
LOG.debug("HTTPConnectProxyHandler: Got exception while trying to connect to %s: %s" % (repr(address), e))
|
||||||
self.send_error(504) # 504 Gateway Timeout
|
self.send_error(504) # 504 Gateway Timeout
|
||||||
return
|
return
|
||||||
|
|
|
@ -63,7 +63,7 @@ class TcpProxy(TransportProxyBase):
|
||||||
try:
|
try:
|
||||||
dest = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
dest = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
dest.connect((self.dest_host, self.dest_port))
|
dest.connect((self.dest_host, self.dest_port))
|
||||||
except socket.error, ex:
|
except socket.error as ex:
|
||||||
source.close()
|
source.close()
|
||||||
dest.close()
|
dest.close()
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -14,6 +14,7 @@ from cc.resources.client_run import ClientRun
|
||||||
from cc.resources.edge import Edge
|
from cc.resources.edge import Edge
|
||||||
from cc.resources.local_run import LocalRun
|
from cc.resources.local_run import LocalRun
|
||||||
from cc.resources.log import Log
|
from cc.resources.log import Log
|
||||||
|
from cc.resources.island_logs import IslandLog
|
||||||
from cc.resources.monkey import Monkey
|
from cc.resources.monkey import Monkey
|
||||||
from cc.resources.monkey_configuration import MonkeyConfiguration
|
from cc.resources.monkey_configuration import MonkeyConfiguration
|
||||||
from cc.resources.monkey_download import MonkeyDownload
|
from cc.resources.monkey_download import MonkeyDownload
|
||||||
|
@ -104,5 +105,6 @@ def init_app(mongo_url):
|
||||||
api.add_resource(Report, '/api/report', '/api/report/')
|
api.add_resource(Report, '/api/report', '/api/report/')
|
||||||
api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/')
|
api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/')
|
||||||
api.add_resource(Log, '/api/log', '/api/log/')
|
api.add_resource(Log, '/api/log', '/api/log/')
|
||||||
|
api.add_resource(IslandLog, '/api/log/island/download', '/api/log/island/download/')
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import standard
|
import standard
|
||||||
import aws
|
import aws
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
ENV_DICT = {
|
ENV_DICT = {
|
||||||
'standard': standard.StandardEnvironment,
|
'standard': standard.StandardEnvironment,
|
||||||
'aws': aws.AwsEnvironment
|
'aws': aws.AwsEnvironment
|
||||||
|
@ -18,6 +22,7 @@ def load_env_from_file():
|
||||||
try:
|
try:
|
||||||
__env_type = load_env_from_file()
|
__env_type = load_env_from_file()
|
||||||
env = ENV_DICT[__env_type]()
|
env = ENV_DICT[__env_type]()
|
||||||
|
logger.info('Monkey\'s env is: {0}'.format(env.__class__.__name__))
|
||||||
except Exception:
|
except Exception:
|
||||||
print('Failed initializing environment: %s' % __env_type)
|
logger.error('Failed initializing environment', exc_info=True)
|
||||||
raise
|
raise
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import logging.config
|
||||||
|
|
||||||
|
|
||||||
|
__author__ = 'Maor.Rayzin'
|
||||||
|
|
||||||
|
|
||||||
|
def json_setup_logging(default_path='logging.json', default_level=logging.INFO, env_key='LOG_CFG'):
|
||||||
|
"""
|
||||||
|
Setup the logging configuration
|
||||||
|
:param default_path: the default log configuration file path
|
||||||
|
:param default_level: Default level to log from
|
||||||
|
:param env_key: SYS ENV key to use for external configuration file path
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
path = default_path
|
||||||
|
value = os.getenv(env_key, None)
|
||||||
|
if value:
|
||||||
|
path = value
|
||||||
|
if os.path.exists(path):
|
||||||
|
with open(path, 'rt') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
logging.config.dictConfig(config)
|
||||||
|
else:
|
||||||
|
logging.basicConfig(level=default_level)
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": false,
|
||||||
|
"formatters": {
|
||||||
|
"simple": {
|
||||||
|
"format": "%(asctime)s - %(filename)s:%(lineno)s - %(funcName)10s() - %(levelname)s - %(message)s"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"handlers": {
|
||||||
|
"console": {
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"level": "DEBUG",
|
||||||
|
"formatter": "simple",
|
||||||
|
"stream": "ext://sys.stdout"
|
||||||
|
},
|
||||||
|
|
||||||
|
"info_file_handler": {
|
||||||
|
"class": "logging.handlers.RotatingFileHandler",
|
||||||
|
"level": "INFO",
|
||||||
|
"formatter": "simple",
|
||||||
|
"filename": "info.log",
|
||||||
|
"maxBytes": 10485760,
|
||||||
|
"backupCount": 20,
|
||||||
|
"encoding": "utf8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"root": {
|
||||||
|
"level": "INFO",
|
||||||
|
"handlers": ["console", "info_file_handler"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,19 +2,25 @@ from __future__ import print_function # In python 2.7
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
import logging
|
||||||
|
|
||||||
BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
if BASE_PATH not in sys.path:
|
if BASE_PATH not in sys.path:
|
||||||
sys.path.insert(0, BASE_PATH)
|
sys.path.insert(0, BASE_PATH)
|
||||||
|
|
||||||
|
from cc.island_logger import json_setup_logging
|
||||||
|
# This is here in order to catch EVERYTHING, some functions are being called on imports the log init needs to be on top.
|
||||||
|
json_setup_logging(default_path='island_logger_default_config.json', default_level=logging.DEBUG)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
from cc.app import init_app
|
from cc.app import init_app
|
||||||
from cc.utils import local_ip_addresses
|
from cc.utils import local_ip_addresses
|
||||||
from cc.environment.environment import env
|
from cc.environment.environment import env
|
||||||
from cc.database import is_db_server_up
|
from cc.database import is_db_server_up
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
def main():
|
||||||
from tornado.wsgi import WSGIContainer
|
from tornado.wsgi import WSGIContainer
|
||||||
from tornado.httpserver import HTTPServer
|
from tornado.httpserver import HTTPServer
|
||||||
from tornado.ioloop import IOLoop
|
from tornado.ioloop import IOLoop
|
||||||
|
@ -22,7 +28,7 @@ if __name__ == '__main__':
|
||||||
mongo_url = os.environ.get('MONGO_URL', env.get_mongo_url())
|
mongo_url = os.environ.get('MONGO_URL', env.get_mongo_url())
|
||||||
|
|
||||||
while not is_db_server_up(mongo_url):
|
while not is_db_server_up(mongo_url):
|
||||||
print('Waiting for MongoDB server')
|
logger.info('Waiting for MongoDB server')
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
app = init_app(mongo_url)
|
app = init_app(mongo_url)
|
||||||
|
@ -33,6 +39,10 @@ if __name__ == '__main__':
|
||||||
ssl_options={'certfile': os.environ.get('SERVER_CRT', 'server.crt'),
|
ssl_options={'certfile': os.environ.get('SERVER_CRT', 'server.crt'),
|
||||||
'keyfile': os.environ.get('SERVER_KEY', 'server.key')})
|
'keyfile': os.environ.get('SERVER_KEY', 'server.key')})
|
||||||
http_server.listen(env.get_island_port())
|
http_server.listen(env.get_island_port())
|
||||||
print('Monkey Island Server is running on https://{}:{}'.format(local_ip_addresses()[0], env.get_island_port()))
|
logger.info(
|
||||||
|
'Monkey Island Server is running on https://{}:{}'.format(local_ip_addresses()[0], env.get_island_port()))
|
||||||
IOLoop.instance().start()
|
IOLoop.instance().start()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import logging
|
||||||
from flask import request, jsonify
|
from flask import request, jsonify
|
||||||
import flask_restful
|
import flask_restful
|
||||||
|
|
||||||
|
@ -5,6 +6,8 @@ from cc.services.node import NodeService
|
||||||
|
|
||||||
__author__ = 'itay.mizeretz'
|
__author__ = 'itay.mizeretz'
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ClientRun(flask_restful.Resource):
|
class ClientRun(flask_restful.Resource):
|
||||||
def get(self):
|
def get(self):
|
||||||
|
@ -17,6 +20,7 @@ class ClientRun(flask_restful.Resource):
|
||||||
if monkey is not None:
|
if monkey is not None:
|
||||||
is_monkey_running = not monkey["dead"]
|
is_monkey_running = not monkey["dead"]
|
||||||
else:
|
else:
|
||||||
|
logger.info("Monkey is not running")
|
||||||
is_monkey_running = False
|
is_monkey_running = False
|
||||||
|
|
||||||
return jsonify(is_running=is_monkey_running)
|
return jsonify(is_running=is_monkey_running)
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import flask_restful
|
||||||
|
|
||||||
|
from cc.auth import jwt_required
|
||||||
|
from cc.services.island_logs import IslandLogService
|
||||||
|
|
||||||
|
__author__ = "Maor.Rayzin"
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class IslandLog(flask_restful.Resource):
|
||||||
|
@jwt_required()
|
||||||
|
def get(self):
|
||||||
|
try:
|
||||||
|
return IslandLogService.get_log_file()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error('Monkey Island logs failed to download', exc_info=True)
|
|
@ -13,6 +13,8 @@ from cc.utils import local_ip_addresses
|
||||||
|
|
||||||
__author__ = 'Barak'
|
__author__ = 'Barak'
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def run_local_monkey():
|
def run_local_monkey():
|
||||||
import platform
|
import platform
|
||||||
|
@ -32,6 +34,7 @@ def run_local_monkey():
|
||||||
copyfile(monkey_path, target_path)
|
copyfile(monkey_path, target_path)
|
||||||
os.chmod(target_path, stat.S_IRWXU | stat.S_IRWXG)
|
os.chmod(target_path, stat.S_IRWXU | stat.S_IRWXG)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
logger.error('Copy file failed', exc_info=True)
|
||||||
return False, "Copy file failed: %s" % exc
|
return False, "Copy file failed: %s" % exc
|
||||||
|
|
||||||
# run the monkey
|
# run the monkey
|
||||||
|
@ -41,6 +44,7 @@ def run_local_monkey():
|
||||||
args = "".join(args)
|
args = "".join(args)
|
||||||
pid = subprocess.Popen(args, shell=True).pid
|
pid = subprocess.Popen(args, shell=True).pid
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
logger.error('popen failed', exc_info=True)
|
||||||
return False, "popen failed: %s" % exc
|
return False, "popen failed: %s" % exc
|
||||||
|
|
||||||
return True, "pis: %s" % pid
|
return True, "pis: %s" % pid
|
||||||
|
|
|
@ -17,7 +17,7 @@ class MonkeyConfiguration(flask_restful.Resource):
|
||||||
@jwt_required()
|
@jwt_required()
|
||||||
def post(self):
|
def post(self):
|
||||||
config_json = json.loads(request.data)
|
config_json = json.loads(request.data)
|
||||||
if config_json.has_key('reset'):
|
if 'reset' in config_json:
|
||||||
ConfigService.reset_config()
|
ConfigService.reset_config()
|
||||||
else:
|
else:
|
||||||
ConfigService.update_config(config_json, should_encrypt=True)
|
ConfigService.update_config(config_json, should_encrypt=True)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import logging
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -6,6 +7,8 @@ import flask_restful
|
||||||
|
|
||||||
__author__ = 'Barak'
|
__author__ = 'Barak'
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
MONKEY_DOWNLOADS = [
|
MONKEY_DOWNLOADS = [
|
||||||
{
|
{
|
||||||
|
@ -42,7 +45,10 @@ MONKEY_DOWNLOADS = [
|
||||||
def get_monkey_executable(host_os, machine):
|
def get_monkey_executable(host_os, machine):
|
||||||
for download in MONKEY_DOWNLOADS:
|
for download in MONKEY_DOWNLOADS:
|
||||||
if host_os == download.get('type') and machine == download.get('machine'):
|
if host_os == download.get('type') and machine == download.get('machine'):
|
||||||
|
logger.info('Monkey exec found for os: {0} and machine: {1}'.format(host_os, machine))
|
||||||
return download
|
return download
|
||||||
|
logger.warning('No monkey executables could be found for the host os or machine or both: host_os: {0}, machine: {1}'
|
||||||
|
.format(host_os, machine))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
|
|
||||||
import flask_restful
|
import flask_restful
|
||||||
from flask import request, make_response, jsonify
|
from flask import request, make_response, jsonify
|
||||||
|
@ -12,6 +13,8 @@ from cc.utils import local_ip_addresses
|
||||||
|
|
||||||
__author__ = 'Barak'
|
__author__ = 'Barak'
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Root(flask_restful.Resource):
|
class Root(flask_restful.Resource):
|
||||||
|
|
||||||
|
@ -42,6 +45,7 @@ class Root(flask_restful.Resource):
|
||||||
# We can't drop system collections.
|
# We can't drop system collections.
|
||||||
[mongo.db[x].drop() for x in mongo.db.collection_names() if not x.startswith('system.')]
|
[mongo.db[x].drop() for x in mongo.db.collection_names() if not x.startswith('system.')]
|
||||||
ConfigService.init_config()
|
ConfigService.init_config()
|
||||||
|
logger.info('DB was reset')
|
||||||
return jsonify(status='OK')
|
return jsonify(status='OK')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -50,6 +54,7 @@ class Root(flask_restful.Resource):
|
||||||
mongo.db.monkey.update({'dead': False}, {'$set': {'config.alive': False, 'modifytime': datetime.now()}},
|
mongo.db.monkey.update({'dead': False}, {'$set': {'config.alive': False, 'modifytime': datetime.now()}},
|
||||||
upsert=False,
|
upsert=False,
|
||||||
multi=True)
|
multi=True)
|
||||||
|
logger.info('Kill all monkeys was called')
|
||||||
return jsonify(status='OK')
|
return jsonify(status='OK')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -59,6 +64,7 @@ class Root(flask_restful.Resource):
|
||||||
infection_done = NodeService.is_monkey_finished_running()
|
infection_done = NodeService.is_monkey_finished_running()
|
||||||
if not infection_done:
|
if not infection_done:
|
||||||
report_done = False
|
report_done = False
|
||||||
|
logger.info('Report generation cannot be completed, infection is not done.')
|
||||||
else:
|
else:
|
||||||
report_done = ReportService.is_report_generated()
|
report_done = ReportService.is_report_generated()
|
||||||
return dict(run_server=True, run_monkey=is_any_exists, infection_done=infection_done, report_done=report_done)
|
return dict(run_server=True, run_monkey=is_any_exists, infection_done=infection_done, report_done=report_done)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
import copy
|
import copy
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
@ -17,6 +18,9 @@ from cc.encryptor import encryptor
|
||||||
__author__ = 'Barak'
|
__author__ = 'Barak'
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Telemetry(flask_restful.Resource):
|
class Telemetry(flask_restful.Resource):
|
||||||
@jwt_required()
|
@jwt_required()
|
||||||
def get(self, **kw):
|
def get(self, **kw):
|
||||||
|
@ -52,10 +56,9 @@ class Telemetry(flask_restful.Resource):
|
||||||
if telem_type in TELEM_PROCESS_DICT:
|
if telem_type in TELEM_PROCESS_DICT:
|
||||||
TELEM_PROCESS_DICT[telem_type](telemetry_json)
|
TELEM_PROCESS_DICT[telem_type](telemetry_json)
|
||||||
else:
|
else:
|
||||||
print('Got unknown type of telemetry: %s' % telem_type)
|
logger.info('Got unknown type of telemetry: %s' % telem_type)
|
||||||
except StandardError as ex:
|
except Exception as ex:
|
||||||
print("Exception caught while processing telemetry: %s" % str(ex))
|
logger.error("Exception caught while processing telemetry", exc_info=True)
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
telem_id = mongo.db.telemetry.insert(telemetry_json)
|
telem_id = mongo.db.telemetry.insert(telemetry_json)
|
||||||
return mongo.db.telemetry.find_one_or_404({"_id": telem_id})
|
return mongo.db.telemetry.find_one_or_404({"_id": telem_id})
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import copy
|
import copy
|
||||||
import collections
|
import collections
|
||||||
import functools
|
import functools
|
||||||
|
import logging
|
||||||
from jsonschema import Draft4Validator, validators
|
from jsonschema import Draft4Validator, validators
|
||||||
|
from six import string_types
|
||||||
|
|
||||||
from cc.database import mongo
|
from cc.database import mongo
|
||||||
from cc.encryptor import encryptor
|
from cc.encryptor import encryptor
|
||||||
|
@ -10,6 +12,8 @@ from cc.utils import local_ip_addresses
|
||||||
|
|
||||||
__author__ = "itay.mizeretz"
|
__author__ = "itay.mizeretz"
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
WARNING_SIGN = u" \u26A0"
|
WARNING_SIGN = u" \u26A0"
|
||||||
|
|
||||||
SCHEMA = {
|
SCHEMA = {
|
||||||
|
@ -916,6 +920,7 @@ class ConfigService:
|
||||||
if should_encrypt:
|
if should_encrypt:
|
||||||
ConfigService.encrypt_config(config_json)
|
ConfigService.encrypt_config(config_json)
|
||||||
mongo.db.config.update({'name': 'newconfig'}, {"$set": config_json}, upsert=True)
|
mongo.db.config.update({'name': 'newconfig'}, {"$set": config_json}, upsert=True)
|
||||||
|
logger.info('monkey config was updated')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def init_default_config():
|
def init_default_config():
|
||||||
|
@ -931,6 +936,7 @@ class ConfigService:
|
||||||
config = copy.deepcopy(ConfigService.default_config)
|
config = copy.deepcopy(ConfigService.default_config)
|
||||||
if should_encrypt:
|
if should_encrypt:
|
||||||
ConfigService.encrypt_config(config)
|
ConfigService.encrypt_config(config)
|
||||||
|
logger.info("Default config was called")
|
||||||
return config
|
return config
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -944,6 +950,7 @@ class ConfigService:
|
||||||
config = ConfigService.get_default_config(True)
|
config = ConfigService.get_default_config(True)
|
||||||
ConfigService.set_server_ips_in_config(config)
|
ConfigService.set_server_ips_in_config(config)
|
||||||
ConfigService.update_config(config, should_encrypt=False)
|
ConfigService.update_config(config, should_encrypt=False)
|
||||||
|
logger.info('Monkey config reset was called')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_server_ips_in_config(config):
|
def set_server_ips_in_config(config):
|
||||||
|
@ -960,6 +967,7 @@ class ConfigService:
|
||||||
initial_config['name'] = 'initial'
|
initial_config['name'] = 'initial'
|
||||||
initial_config.pop('_id')
|
initial_config.pop('_id')
|
||||||
mongo.db.config.insert(initial_config)
|
mongo.db.config.insert(initial_config)
|
||||||
|
logger.info('Monkey config was inserted to mongo and saved')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _extend_config_with_default(validator_class):
|
def _extend_config_with_default(validator_class):
|
||||||
|
@ -1001,7 +1009,7 @@ class ConfigService:
|
||||||
"""
|
"""
|
||||||
keys = [config_arr_as_array[2] for config_arr_as_array in ENCRYPTED_CONFIG_ARRAYS]
|
keys = [config_arr_as_array[2] for config_arr_as_array in ENCRYPTED_CONFIG_ARRAYS]
|
||||||
for key in keys:
|
for key in keys:
|
||||||
if isinstance(flat_config[key], collections.Sequence) and not isinstance(flat_config[key], basestring):
|
if isinstance(flat_config[key], collections.Sequence) and not isinstance(flat_config[key], string_types):
|
||||||
# Check if we are decrypting ssh key pair
|
# Check if we are decrypting ssh key pair
|
||||||
if flat_config[key] and isinstance(flat_config[key][0], dict) and 'public_key' in flat_config[key][0]:
|
if flat_config[key] and isinstance(flat_config[key][0], dict) and 'public_key' in flat_config[key][0]:
|
||||||
flat_config[key] = [ConfigService.decrypt_ssh_key_pair(item) for item in flat_config[key]]
|
flat_config[key] = [ConfigService.decrypt_ssh_key_pair(item) for item in flat_config[key]]
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import logging
|
||||||
|
__author__ = "Maor.Rayzin"
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class IslandLogService:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_log_file():
|
||||||
|
"""
|
||||||
|
This static function is a helper function for the monkey island log download function.
|
||||||
|
It finds the logger handlers and checks if one of them is a fileHandler of any kind by checking if the handler
|
||||||
|
has the property handler.baseFilename.
|
||||||
|
:return:
|
||||||
|
a dict with the log file content.
|
||||||
|
"""
|
||||||
|
logger_handlers = logger.parent.handlers
|
||||||
|
for handler in logger_handlers:
|
||||||
|
if hasattr(handler, 'baseFilename'):
|
||||||
|
logger.info('Log file found: {0}'.format(handler.baseFilename))
|
||||||
|
log_file_path = handler.baseFilename
|
||||||
|
with open(log_file_path, 'rt') as f:
|
||||||
|
log_file = f.read()
|
||||||
|
return {
|
||||||
|
'log_file': log_file
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.warning('No log file could be found, check logger config.')
|
||||||
|
return None
|
|
@ -1,6 +1,9 @@
|
||||||
import ipaddress
|
import ipaddress
|
||||||
|
import logging
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
from six import text_type
|
||||||
|
|
||||||
from cc.database import mongo
|
from cc.database import mongo
|
||||||
from cc.services.config import ConfigService
|
from cc.services.config import ConfigService
|
||||||
from cc.services.edge import EdgeService
|
from cc.services.edge import EdgeService
|
||||||
|
@ -10,6 +13,9 @@ from cc.utils import local_ip_addresses, get_subnets
|
||||||
__author__ = "itay.mizeretz"
|
__author__ = "itay.mizeretz"
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ReportService:
|
class ReportService:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
@ -78,6 +84,8 @@ class ReportService:
|
||||||
creds = ReportService.get_azure_creds()
|
creds = ReportService.get_azure_creds()
|
||||||
machines = set([instance['origin'] for instance in creds])
|
machines = set([instance['origin'] for instance in creds])
|
||||||
|
|
||||||
|
logger.info('Azure issues generated for reporting')
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'type': 'azure_password',
|
'type': 'azure_password',
|
||||||
|
@ -104,6 +112,8 @@ class ReportService:
|
||||||
}
|
}
|
||||||
for node in nodes]
|
for node in nodes]
|
||||||
|
|
||||||
|
logger.info('Scanned nodes generated for reporting')
|
||||||
|
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -125,6 +135,8 @@ class ReportService:
|
||||||
}
|
}
|
||||||
for monkey in exploited]
|
for monkey in exploited]
|
||||||
|
|
||||||
|
logger.info('Exploited nodes generated for reporting')
|
||||||
|
|
||||||
return exploited
|
return exploited
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -148,6 +160,7 @@ class ReportService:
|
||||||
'origin': origin
|
'origin': origin
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
logger.info('Stolen creds generated for reporting')
|
||||||
return creds
|
return creds
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -189,6 +202,8 @@ class ReportService:
|
||||||
azure_leaked_users = [{'username': user.replace(',', '.'), 'type': 'Clear Password',
|
azure_leaked_users = [{'username': user.replace(',', '.'), 'type': 'Clear Password',
|
||||||
'origin': origin} for user in azure_users]
|
'origin': origin} for user in azure_users]
|
||||||
creds.extend(azure_leaked_users)
|
creds.extend(azure_leaked_users)
|
||||||
|
|
||||||
|
logger.info('Azure machines creds generated for reporting')
|
||||||
return creds
|
return creds
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -311,7 +326,7 @@ class ReportService:
|
||||||
|
|
||||||
return \
|
return \
|
||||||
[
|
[
|
||||||
ipaddress.ip_interface(unicode(network['addr'] + '/' + network['netmask'])).network
|
ipaddress.ip_interface(text_type(network['addr'] + '/' + network['netmask'])).network
|
||||||
for network in network_info['data']['network_info']['networks']
|
for network in network_info['data']['network_info']['networks']
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -324,7 +339,7 @@ class ReportService:
|
||||||
monkey_subnets = ReportService.get_monkey_subnets(monkey['guid'])
|
monkey_subnets = ReportService.get_monkey_subnets(monkey['guid'])
|
||||||
for subnet in monkey_subnets:
|
for subnet in monkey_subnets:
|
||||||
for ip in island_ips:
|
for ip in island_ips:
|
||||||
if ipaddress.ip_address(unicode(ip)) in subnet:
|
if ipaddress.ip_address(text_type(ip)) in subnet:
|
||||||
found_good_ip = True
|
found_good_ip = True
|
||||||
break
|
break
|
||||||
if found_good_ip:
|
if found_good_ip:
|
||||||
|
@ -348,6 +363,7 @@ class ReportService:
|
||||||
if machine not in issues_dict:
|
if machine not in issues_dict:
|
||||||
issues_dict[machine] = []
|
issues_dict[machine] = []
|
||||||
issues_dict[machine].append(issue)
|
issues_dict[machine].append(issue)
|
||||||
|
logger.info('Issues generated for reporting')
|
||||||
return issues_dict
|
return issues_dict
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -437,6 +453,7 @@ class ReportService:
|
||||||
{'name': 'generated_report'},
|
{'name': 'generated_report'},
|
||||||
{'$set': {'value': True}},
|
{'$set': {'value': True}},
|
||||||
upsert=True)
|
upsert=True)
|
||||||
|
logger.info("Report marked as generated.")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_report():
|
def get_report():
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -346,7 +346,7 @@ class ReportPageComponent extends AuthComponent {
|
||||||
<li>Weak segmentation - Machines from different segments are able to
|
<li>Weak segmentation - Machines from different segments are able to
|
||||||
communicate.</li> : null}
|
communicate.</li> : null}
|
||||||
{this.state.report.overview.warnings[this.Warning.TUNNEL] ?
|
{this.state.report.overview.warnings[this.Warning.TUNNEL] ?
|
||||||
<li>Weak segmentation - machines were able to communicate over unused ports.</li> : null}
|
<li>Weak segmentation - Machines were able to communicate over unused ports.</li> : null}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Col} from 'react-bootstrap';
|
import {Button, Col} from 'react-bootstrap';
|
||||||
import JSONTree from 'react-json-tree'
|
import JSONTree from 'react-json-tree'
|
||||||
import {DataTable} from 'react-data-components';
|
import {DataTable} from 'react-data-components';
|
||||||
import AuthComponent from '../AuthComponent';
|
import AuthComponent from '../AuthComponent';
|
||||||
|
import download from 'downloadjs'
|
||||||
|
|
||||||
const renderJson = (val) => <JSONTree data={val} level={1} theme="eighties" invertTheme={true} />;
|
const renderJson = (val) => <JSONTree data={val} level={1} theme="eighties" invertTheme={true} />;
|
||||||
const renderTime = (val) => val.split('.')[0];
|
const renderTime = (val) => val.split('.')[0];
|
||||||
|
@ -28,21 +29,47 @@ class TelemetryPageComponent extends AuthComponent {
|
||||||
.then(res => this.setState({data: res.objects}));
|
.then(res => this.setState({data: res.objects}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
downloadIslandLog = () => {
|
||||||
|
this.authFetch('/api/log/island/download')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(res => {
|
||||||
|
let filename = 'Island_log'
|
||||||
|
let logContent = (res['log_file']);
|
||||||
|
download(logContent, filename, 'text/plain');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Col xs={12} lg={8}>
|
<div>
|
||||||
<h1 className="page-title">Log</h1>
|
<div>
|
||||||
<div className="data-table-container">
|
<Col xs={12} lg={8}>
|
||||||
<DataTable
|
<h1 className="page-title">Log</h1>
|
||||||
keys="name"
|
<div className="data-table-container">
|
||||||
columns={columns}
|
<DataTable
|
||||||
initialData={this.state.data}
|
keys="name"
|
||||||
initialPageLength={20}
|
columns={columns}
|
||||||
initialSortBy={{ prop: 'timestamp', order: 'descending' }}
|
initialData={this.state.data}
|
||||||
pageLengthOptions={[ 20, 50, 100 ]}
|
initialPageLength={20}
|
||||||
/>
|
initialSortBy={{ prop: 'timestamp', order: 'descending' }}
|
||||||
</div>
|
pageLengthOptions={[ 20, 50, 100 ]}
|
||||||
</Col>
|
/>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Col xs={12} lg={8}>
|
||||||
|
<h1 className="page-title"> Monkey Island Logs </h1>
|
||||||
|
<div className="text-center" style={{marginBottom: '20px'}}>
|
||||||
|
<p style={{'marginBottom': '2em', 'fontSize': '1.2em'}}> Download Monkey Island internal log file </p>
|
||||||
|
<Button bsSize="large" onClick={()=> {
|
||||||
|
this.downloadIslandLog();
|
||||||
|
}}>
|
||||||
|
<i className="glyphicon glyphicon-download"/> Download </Button>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,4 +5,4 @@ Homepage: http://www.guardicore.com
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Version: 1.0
|
Version: 1.0
|
||||||
Description: Guardicore Infection Monkey Island installation package
|
Description: Guardicore Infection Monkey Island installation package
|
||||||
Depends: openssl, python-pip
|
Depends: openssl, python-pip, python-dev
|
||||||
|
|
Loading…
Reference in New Issue