forked from p15670423/monkey
Merge branch 'develop' into feature/report-backend
# Conflicts: # monkey_island/cc/main.py
This commit is contained in:
commit
a0dc706a1e
171
README.md
171
README.md
|
@ -6,137 +6,52 @@ Infection Monkey
|
|||
|
||||
Welcome to the Infection Monkey!
|
||||
|
||||
The Infection Monkey is an open source security tool for testing a data center's resiliency to perimeter breaches and internal server infection. The Monkey uses various methods to self propagate across a data center and reports success to a centralized C&C server. To read more about the Monkey, visit https://www.guardicore.com/infectionmonkey/
|
||||
The Infection Monkey is an open source security tool for testing a data center's resiliency to perimeter breaches and internal server infection. The Monkey uses various methods to self propagate across a data center and reports success to a centralized Command and Control(C&C) server.
|
||||
|
||||
### http://www.guardicore.com/the-infected-chaos-monkey/
|
||||
The Infection Monkey is comprised of two parts:
|
||||
* Chaos Monkey - A tool which infects other machines and propagates to them
|
||||
* Monkey Island - A C&C server with a dedicated UI to visualize the Chaos Monkey's progress inside the data center
|
||||
|
||||
Features include:
|
||||
To read more about the Monkey, visit http://infectionmonkey.com
|
||||
|
||||
Main Features
|
||||
---------------
|
||||
|
||||
The Infection Monkey uses the following techniques and exploits to propagate to other machines.
|
||||
|
||||
* Multiple propagation techniques:
|
||||
* Predefined passwords
|
||||
* Common exploits
|
||||
* Common logical exploits
|
||||
* Password stealing using mimikatz
|
||||
* Multiple exploit methods:
|
||||
* SSH
|
||||
* SMB
|
||||
* RDP
|
||||
* WMI
|
||||
* Shellshock
|
||||
* A C&C server with a dedicated UI to visualize the Monkey's progress inside the data center
|
||||
* Conficker
|
||||
* SambaCry
|
||||
* Elastic Search (CVE-2015-1427)
|
||||
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
The Infection Monkey is comprised of two parts: the Monkey and the C&C server.
|
||||
The monkey is the tool which infects other machines and propagates to them, while the C&C server collects all Monkey reports and displays them to the user.
|
||||
|
||||
### Requirements
|
||||
|
||||
The C&C Server has been tested on Ubuntu 14.04,15.04 and 16.04.
|
||||
The Monkey itself has been tested on Windows XP, 7, 8.1 and 10. The Linux build has been tested on Ubuntu server 14.04 and 15.10.
|
||||
The Monkey itself has been tested on Windows XP, 7, 8.1 and 10. The Linux build has been tested on Ubuntu server (multiple versions).
|
||||
|
||||
### Installation
|
||||
|
||||
For off-the-shelf use, download a Debian package from our website and follow the guide [written in our blog](https://www.guardicore.com/2016/07/infection-monkey-loose-2/).
|
||||
Warning! The Debian package will uninstall the python library 'bson' because of an issue with pymongo. You can reinstall it later, but monkey island will probably not work.
|
||||
|
||||
For off-the-shelf use, download our Debian package from our website and follow the guide [written in our blog](https://www.guardicore.com/2016/07/infection-monkey-loose-2/).
|
||||
To manually set up and the C&C server follow the instructions on [Monkey Island readme](monkey_island/readme.txt). If you wish to compile the binaries yourself, follow the instructions under Building the Monkey from Source.
|
||||
|
||||
### Initial configuration.
|
||||
Whether you're downloading or building the Monkey from source, the Infection Monkey is comprised of 4 executable files for different platforms plus a default configuration file.
|
||||
To manually set up and the C&C server follow the instructions on [Monkey Island readme](monkey_island/readme.txt). If you wish to compile the binaries yourself, follow the instructions under Building the Monkey from Source.
|
||||
|
||||
Monkey configuration is stored in two places:
|
||||
1. By default, the Monkey uses a local configuration file (usually, config.bin). This configuration file must include the address of the Monkey's C&C server.
|
||||
2. After successfully connecting to the C&C server, the monkey downloads a new configuration from the server and discards the local configuration. It is possible to change the default configuration from the C&C server's UI.
|
||||
### Start Infecting
|
||||
|
||||
In both cases the command server hostname should be modified to point to your local instance of the Monkey Island (note that this doesn't require connectivity right off the bat). In addition, to improve the Monkey's chances of spreading, you can pre-seed it with credentials and usernames commonly used.
|
||||
|
||||
Both configuration options use a JSON format for specifying options; see "Options" below for details.
|
||||
|
||||
### Running the C&C Server
|
||||
|
||||
To run the C&C Server, install our infected Monkey debian package on a specific server. The initial infected machine doesn't require a direct link to this server.
|
||||
|
||||
### Unleashing the Monkey
|
||||
|
||||
Once configured, run the monkey using ```./monkey-linux-64 m0nk3y -c config.bin -s 41.50.73.31:5000``` (Windows is identical). This can be done at multiple points in the network simultaneously.
|
||||
|
||||
Command line options include:
|
||||
* `-c`, `--config`: set configuration file. JSON file with configuration values, will override compiled configuration.
|
||||
* `-p`, `--parent`: set monkey’s parent uuid, allows better recognition of exploited monkeys in c&c
|
||||
* `-t`, `--tunnel`: ip:port, set default tunnel for Monkey when connecting to c&c.
|
||||
* `-d`, `--depth` : sets the Monkey's current operation depth.
|
||||
|
||||
|
||||
How the Monkey works
|
||||
---------------------
|
||||
|
||||
1. Wakeup connection to c&c, sends basic info of the current machine and the configuration the monkey uses to the c&c.
|
||||
1. First try direct connection to c&c.
|
||||
2. If direct connection fails, try connection through a tunnel, a tunnel is found according to specified parameter (the default tunnel) or by sending a multicast query and waiting for another monkey to answer.
|
||||
3. If no connection can be made to c&c, continue without it.
|
||||
2. If a firewall app is running on the machine (supports Windows Firewall for Win XP and Windows Advanced Firewall for Win 7+), try to add a rule to allow all our traffic.
|
||||
3. Startup of tunnel for other Monkeys (if connection to c&c works).
|
||||
1. Firewall is checked to allow listening sockets (if we failed to add a rule to Windows firewall for example, the tunnel will not be created)
|
||||
2. Will answer multicast requests from other Monkeys in search of a tunnel.
|
||||
4. Running exploitation sessions, will run x sessions according to configuration:
|
||||
1. Connect to c&c and get the latest configuration
|
||||
2. Scan ip ranges according to configuration.
|
||||
3. Try fingerprinting each host that answers, using the classes defined in the configuration (SMBFinger, SSHFinger, etc)
|
||||
4. Try exploitation on each host found, for each exploit class in configuration:
|
||||
1. check exploit class supports target host (can be disabled by configuration)
|
||||
2. each exploitation class will use the data acquired in fingerprinting, or during the exploit, to find the suitable Monkey executable for the host from the c&c.
|
||||
1. If c&c connection fails, and the source monkey’s executable is suitable, we use it.
|
||||
2. If a suitable executable isn’t found, exploitation will fail.
|
||||
3. Executables are cached in memory.
|
||||
5. will skip hosts that are already exploited in next run
|
||||
6. will skip hosts that failed during exploitation in next run (can be disabled by configuration)
|
||||
5. Close tunnel before exiting
|
||||
Wait for monkeys using the tunnel to unregister for it
|
||||
Cleanup
|
||||
Remove firewall rules if added
|
||||
|
||||
Configuration Options
|
||||
---------------------
|
||||
|
||||
Key | Type | Description | Possible Values
|
||||
--- | ---- | ----------- | ---------------
|
||||
alive | bool | sets whether or not the monkey is alive. if false will stop scanning and exploiting
|
||||
command_servers | array | addresses of c&c servers to try to connect | example: ["russian-mail-brides.com:5000"]
|
||||
singleton_mutex_name | string | string of the mutex name for single instance | example: {2384ec59-0df8-4ab9-918c-843740924a28}
|
||||
self_delete_in_cleanup | bool | sets whether or not to self delete the monkey executable when stopped
|
||||
use_file_logging | bool | sets whether or not to use a log file
|
||||
monkey_log_path_[windows/linux] | string | file path for monkey logger.
|
||||
kill_file_path_[windows/linux] | string | file path that the Monkey checks to prevent running
|
||||
timeout_between_iterations | int | how long to wait between scan iterations
|
||||
max_iterations | int | how many scan iterations to perform on each run
|
||||
internet_services | array | addresses of internet servers to ping and check if the monkey has internet acccess
|
||||
victims_max_find | int | how many victims to look for in a single scan iteration
|
||||
victims_max_exploit | int | how many victims to exploit before stopping
|
||||
retry_failed_explotation | bool | sets whether or not to retry failed hosts on next scan
|
||||
local_network_scan | bool | sets whether to auto detect and scan local subnets
|
||||
range_class | class name | sets which ip ranges class is used to construct the list of ips to scan | `FixedRange` - scan list is a static ips list, `RelativeRange` - scan list will be constructed according to ip address of the machine and size of the scan, `ClassCRange` - will scan the entire class c the machine is in.
|
||||
range_fixed | tuple of strings | list of ips to scan
|
||||
RelativeRange range_size | int | number of hosts to scan in relative range
|
||||
scanner_class | class name | sets which scan class to use when scanning for hosts to exploit | `TCPScanner` - searches for hosts according to open tcp ports, `PingScanner` - searches for hosts according to ping scan
|
||||
finger_classes | tuple of class names | sets which fingerprinting classes to use | in the list: `SMBFinger` - get host os info by checking smb info, `SSHFinger` - get host os info by checking ssh banner, `PingScanner` - get host os type by checking ping ttl. For example: `(SMBFinger, SSHFinger, PingScanner)`
|
||||
exploiter_classes | tuple of class names | | `SmbExploiter` - exploit using smb connection, `WmiExploiter` - exploit using wmi connection, `RdpExploiter` - exploit using rdp connection, `Ms08_067_Exploiter` - exploit using ms08_067 smb exploit, `SSHExploiter` - exploit using ssh connection
|
||||
tcp_target_ports | list of int | which ports to scan using TCPScanner
|
||||
tcp_scan_timeout | int | timeout for tcp connection in tcp scan (in milliseconds)
|
||||
tcp_scan_interval | int | time to wait between ports in the tcp scan (in milliseconds)
|
||||
tcp_scan_get_banner | bool | sets whether or not to read a banner from the tcp ports when scanning
|
||||
ping_scan_timeout | int | timeout for the ping command (in milliseconds) utilised by PingScanner
|
||||
skip_exploit_if_file_exist | bool | sets whether or not to abort exploit if the monkey already exists in target, used by SmbExploiter
|
||||
psexec_user | string | user to use for connection, utilised by SmbExploiter/WmiExploiter/RdpExploiter
|
||||
psexec_passwords | list of strings | list of passwords to use when trying to exploit
|
||||
rdp_use_vbs_download | bool | sets whether to use vbs payload for rdp exploitation in RdpExploiter. If false, bits payload is used (will fail if bitsadmin.exe doesn’t exist)
|
||||
ms08_067_exploit_attempt | int | number of times to try and exploit using ms08_067 exploit
|
||||
ms08_067_remote_user_add | string | user to add to target when using ms08_067 exploit
|
||||
ms08_067_remote_user_pass | string | password of the user the exploit will add
|
||||
ssh_user | string | user to use for ssh connection, used by SSHExploiter
|
||||
ssh_passwords | list of strings | list of passwords to use when trying to exploit using SSHExploiter
|
||||
dropper_set_date | bool | whether or not to change the monkey file date to match other files
|
||||
dropper_target_path_[windows/linux] | string | path for the dropper
|
||||
serialize_config | bool | sets whether or not to locally save the running configuration after finishing
|
||||
After installing the Infection Monkey on a server of your choice, just browse https://your-server-ip:5000 and follow the instructions to start infecting.
|
||||
|
||||
|
||||
Building the Monkey from source
|
||||
|
@ -146,46 +61,6 @@ If you want to build the monkey from source instead of using our provided packag
|
|||
|
||||
License
|
||||
=======
|
||||
Copyright (c) 2016 Guardicore Ltd
|
||||
Copyright (c) 2017 Guardicore Ltd
|
||||
|
||||
See the [LICENSE](LICENSE) file for license rights and limitations (GPLv3).
|
||||
|
||||
Dependent packages
|
||||
---------------------
|
||||
|
||||
Dependency | License | Notes
|
||||
----------------------------|----------------------------|----------------------------
|
||||
libffi-dev | https://github.com/atgreen/libffi/blob/master/LICENSE
|
||||
PyCrypto | Public domain
|
||||
upx | Custom license, http://upx.sourceforge.net/upx-license.html
|
||||
bson | BSD
|
||||
enum34 | BSD
|
||||
pyasn1 | BSD
|
||||
psutil | BSD
|
||||
flask | BSD
|
||||
flask-Pymongo | BSD
|
||||
Flask-Restful | BSD
|
||||
python-dateutil | Simplified BSD
|
||||
zope | ZPL 2.1
|
||||
Bootstrap | MIT
|
||||
Bootstrap Switch | Apache 2.0
|
||||
Bootstrap Dialog | MIT
|
||||
JSON Editor | MIT
|
||||
Datatables | MIT
|
||||
jQuery | MIT
|
||||
cffi | MIT
|
||||
twisted | MIT
|
||||
typeahead.js | MIT
|
||||
Font Awesome | MIT
|
||||
vis.js | MIT/Apache 2.0
|
||||
impacket | Apache Modified
|
||||
Start Bootstrap (UI Theme) | Apache 2.0
|
||||
requests | Apache 2.0
|
||||
grequests | BSD
|
||||
odict | Python Software Foundation License
|
||||
paramiko | LGPL
|
||||
rdpy | GPL-3
|
||||
winbind | GPL-3
|
||||
pyinstaller | GPL
|
||||
Celery | BSD
|
||||
mimikatz | CC BY 4.0 | We use an altered version of mimikatz made by gentilkiwi: https://github.com/guardicore/mimikatz
|
||||
See the [LICENSE](LICENSE) file for license rights and limitations (GPLv3).
|
|
@ -183,7 +183,6 @@ class Configuration(object):
|
|||
local_network_scan = True
|
||||
|
||||
range_class = FixedRange
|
||||
range_size = 1
|
||||
range_fixed = ['', ]
|
||||
|
||||
blocked_ips = ['', ]
|
||||
|
|
|
@ -97,11 +97,11 @@ class ControlClient(object):
|
|||
return {}
|
||||
|
||||
@staticmethod
|
||||
def send_telemetry(tele_type='general', data=''):
|
||||
def send_telemetry(telem_type, data):
|
||||
if not WormConfiguration.current_server:
|
||||
return
|
||||
try:
|
||||
telemetry = {'monkey_guid': GUID, 'telem_type': tele_type, 'data': data}
|
||||
telemetry = {'monkey_guid': GUID, 'telem_type': telem_type, 'data': data}
|
||||
reply = requests.post("https://%s/api/telemetry" % (WormConfiguration.current_server,),
|
||||
data=json.dumps(telemetry),
|
||||
headers={'content-type': 'application/json'},
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
"ms08_067_remote_user_add": "Monkey_IUSER_SUPPORT",
|
||||
"ms08_067_remote_user_pass": "Password1!",
|
||||
"ping_scan_timeout": 10000,
|
||||
"range_size": 30,
|
||||
"rdp_use_vbs_download": true,
|
||||
"smb_download_timeout": 300,
|
||||
"smb_service_name": "InfectionMonkey",
|
||||
|
|
|
@ -6,14 +6,16 @@ __author__ = 'itamar'
|
|||
class HostExploiter(object):
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
_TARGET_OS_TYPE = []
|
||||
|
||||
def __init__(self, host):
|
||||
self._target_os_type = []
|
||||
|
||||
self._exploit_info = {}
|
||||
self._exploit_attempts = []
|
||||
self.host = host
|
||||
|
||||
def is_os_supported(self):
|
||||
return self.host.os.get('type') in self._target_os_type
|
||||
return self.host.os.get('type') in self._TARGET_OS_TYPE
|
||||
|
||||
def send_exploit_telemetry(self, result):
|
||||
from control import ControlClient
|
||||
|
|
|
@ -34,9 +34,10 @@ class ElasticGroovyExploiter(HostExploiter):
|
|||
|
||||
DOWNLOAD_TIMEOUT = 300 # copied from rdpgrinder
|
||||
|
||||
_TARGET_OS_TYPE = ['linux', 'windows']
|
||||
|
||||
def __init__(self, host):
|
||||
super(ElasticGroovyExploiter, self).__init__(host)
|
||||
self._target_os_type = ['linux', 'windows']
|
||||
self._config = __import__('config').WormConfiguration
|
||||
self.skip_exist = self._config.skip_exploit_if_file_exist
|
||||
|
||||
|
@ -46,7 +47,7 @@ class ElasticGroovyExploiter(HostExploiter):
|
|||
Either using version string or by trying to attack
|
||||
:return:
|
||||
"""
|
||||
if self.host.os.get('type') not in self._target_os_type:
|
||||
if not super(ElasticGroovyExploiter, self).is_os_supported():
|
||||
return False
|
||||
|
||||
if ES_SERVICE not in self.host.services:
|
||||
|
|
|
@ -233,14 +233,15 @@ class CMDClientFactory(rdp.ClientFactory):
|
|||
|
||||
class RdpExploiter(HostExploiter):
|
||||
|
||||
_TARGET_OS_TYPE = ['windows']
|
||||
|
||||
def __init__(self, host):
|
||||
super(RdpExploiter, self).__init__(host)
|
||||
self._target_os_type = ['windows']
|
||||
self._config = __import__('config').WormConfiguration
|
||||
self._guid = __import__('config').GUID
|
||||
|
||||
def is_os_supported(self):
|
||||
if self.host.os.get('type') in self._target_os_type:
|
||||
if super(RdpExploiter, self).is_os_supported():
|
||||
return True
|
||||
|
||||
if not self.host.os.get('type'):
|
||||
|
|
|
@ -32,6 +32,7 @@ class SambaCryExploiter(HostExploiter):
|
|||
https://github.com/CoreSecurity/impacket/blob/master/examples/sambaPipe.py
|
||||
"""
|
||||
|
||||
_TARGET_OS_TYPE = ['linux']
|
||||
# Name of file which contains the monkey's commandline
|
||||
SAMBACRY_COMMANDLINE_FILENAME = "monkey_commandline.txt"
|
||||
# Name of file which contains the runner's result
|
||||
|
@ -51,7 +52,6 @@ class SambaCryExploiter(HostExploiter):
|
|||
|
||||
def __init__(self, host):
|
||||
super(SambaCryExploiter, self).__init__(host)
|
||||
self._target_os_type = ['linux']
|
||||
self._config = __import__('config').WormConfiguration
|
||||
|
||||
def exploit_host(self):
|
||||
|
|
|
@ -25,9 +25,10 @@ class ShellShockExploiter(HostExploiter):
|
|||
"Content-type": "() { :;}; echo; "
|
||||
}
|
||||
|
||||
_TARGET_OS_TYPE = ['linux']
|
||||
|
||||
def __init__(self, host):
|
||||
super(ShellShockExploiter, self).__init__(host)
|
||||
self._target_os_type = ['linux']
|
||||
self._config = __import__('config').WormConfiguration
|
||||
self.HTTP = [str(port) for port in self._config.HTTP_PORTS]
|
||||
self.success_flag = ''.join(
|
||||
|
@ -98,7 +99,7 @@ class ShellShockExploiter(HostExploiter):
|
|||
LOG.info("Host %s was already infected under the current configuration, done" % self.host)
|
||||
return True # return already infected
|
||||
|
||||
src_path = src_path or get_target_monkey(self.host)
|
||||
src_path = get_target_monkey(self.host)
|
||||
if not src_path:
|
||||
LOG.info("Can't find suitable monkey executable for host %r", self.host)
|
||||
return False
|
||||
|
@ -144,6 +145,8 @@ class ShellShockExploiter(HostExploiter):
|
|||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def check_remote_file_exists(cls, url, header, exploit, file_path):
|
||||
"""
|
||||
|
|
|
@ -14,6 +14,7 @@ LOG = getLogger(__name__)
|
|||
|
||||
|
||||
class SmbExploiter(HostExploiter):
|
||||
_TARGET_OS_TYPE = ['windows']
|
||||
KNOWN_PROTOCOLS = {
|
||||
'139/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 139),
|
||||
'445/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 445),
|
||||
|
@ -22,12 +23,11 @@ class SmbExploiter(HostExploiter):
|
|||
|
||||
def __init__(self, host):
|
||||
super(SmbExploiter, self).__init__(host)
|
||||
self._target_os_type = ['windows']
|
||||
self._config = __import__('config').WormConfiguration
|
||||
self._guid = __import__('config').GUID
|
||||
|
||||
def is_os_supported(self):
|
||||
if self.host.os.get('type') in self._target_os_type:
|
||||
if super(SmbExploiter, self).is_os_supported():
|
||||
return True
|
||||
|
||||
if not self.host.os.get('type'):
|
||||
|
@ -39,7 +39,7 @@ class SmbExploiter(HostExploiter):
|
|||
is_nb_open, _ = check_port_tcp(self.host.ip_addr, 139)
|
||||
if is_nb_open:
|
||||
self.host.os['type'] = 'windows'
|
||||
return self.host.os.get('type') in self._target_os_type
|
||||
return self.host.os.get('type') in self._TARGET_OS_TYPE
|
||||
return False
|
||||
|
||||
def exploit_host(self):
|
||||
|
@ -86,11 +86,11 @@ class SmbExploiter(HostExploiter):
|
|||
|
||||
# execute the remote dropper in case the path isn't final
|
||||
if remote_full_path.lower() != self._config.dropper_target_path.lower():
|
||||
cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {'dropper_path': remote_full_path}
|
||||
cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {'dropper_path': remote_full_path} + \
|
||||
build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.dropper_target_path)
|
||||
else:
|
||||
cmdline = MONKEY_CMDLINE_DETACHED_WINDOWS % {'monkey_path': remote_full_path}
|
||||
|
||||
cmdline += build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
||||
cmdline = MONKEY_CMDLINE_DETACHED_WINDOWS % {'monkey_path': remote_full_path} + \
|
||||
build_monkey_commandline(self.host, get_monkey_depth() - 1)
|
||||
|
||||
for str_bind_format, port in SmbExploiter.KNOWN_PROTOCOLS.values():
|
||||
rpctransport = transport.DCERPCTransportFactory(str_bind_format % (self.host.ip_addr,))
|
||||
|
|
|
@ -18,10 +18,10 @@ TRANSFER_UPDATE_RATE = 15
|
|||
|
||||
|
||||
class SSHExploiter(HostExploiter):
|
||||
_TARGET_OS_TYPE = ['linux', None]
|
||||
|
||||
def __init__(self, host):
|
||||
super(SSHExploiter, self).__init__(host)
|
||||
self._target_os_type = ['linux', None]
|
||||
self._config = __import__('config').WormConfiguration
|
||||
self._update_timestamp = 0
|
||||
self.skip_exist = self._config.skip_exploit_if_file_exist
|
||||
|
|
|
@ -389,7 +389,9 @@ class HTTPTools(object):
|
|||
|
||||
def get_interface_to_target(dst):
|
||||
if sys.platform == "win32":
|
||||
return get_close_matches(dst, local_ips())[0]
|
||||
ips = local_ips()
|
||||
matches = get_close_matches(dst, ips)
|
||||
return matches[0] if (len(matches) > 0) else ips[0]
|
||||
else:
|
||||
# based on scapy implementation
|
||||
|
||||
|
|
|
@ -152,27 +152,27 @@ class SRVSVC_Exploit(object):
|
|||
|
||||
|
||||
class Ms08_067_Exploiter(HostExploiter):
|
||||
_TARGET_OS_TYPE = ['windows']
|
||||
_windows_versions = {'Windows Server 2003 3790 Service Pack 2': WindowsVersion.Windows2003_SP2,
|
||||
'Windows Server 2003 R2 3790 Service Pack 2': WindowsVersion.Windows2003_SP2}
|
||||
|
||||
def __init__(self, host):
|
||||
super(Ms08_067_Exploiter, self).__init__(host)
|
||||
self._target_os_type = ['windows']
|
||||
self._config = __import__('config').WormConfiguration
|
||||
self._guid = __import__('config').GUID
|
||||
|
||||
def is_os_supported(self):
|
||||
if self.host.os.get('type') in self._target_os_type and \
|
||||
if self.host.os.get('type') in self._TARGET_OS_TYPE and \
|
||||
self.host.os.get('version') in self._windows_versions.keys():
|
||||
return True
|
||||
|
||||
if not self.host.os.get('type') or (
|
||||
self.host.os.get('type') in self._target_os_type and not self.host.os.get('version')):
|
||||
self.host.os.get('type') in self._TARGET_OS_TYPE and not self.host.os.get('version')):
|
||||
is_smb_open, _ = check_port_tcp(self.host.ip_addr, 445)
|
||||
if is_smb_open:
|
||||
smb_finger = SMBFinger()
|
||||
if smb_finger.get_host_fingerprint(self.host):
|
||||
return self.host.os.get('type') in self._target_os_type and \
|
||||
return self.host.os.get('type') in self._TARGET_OS_TYPE and \
|
||||
self.host.os.get('version') in self._windows_versions.keys()
|
||||
return False
|
||||
|
||||
|
|
|
@ -14,9 +14,10 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class WmiExploiter(HostExploiter):
|
||||
_TARGET_OS_TYPE = ['windows']
|
||||
|
||||
def __init__(self, host):
|
||||
super(WmiExploiter, self).__init__(host)
|
||||
self._target_os_type = ['windows']
|
||||
self._config = __import__('config').WormConfiguration
|
||||
self._guid = __import__('config').GUID
|
||||
|
||||
|
|
|
@ -34,12 +34,13 @@ class VictimHost(object):
|
|||
def __str__(self):
|
||||
victim = "Victim Host %s: " % self.ip_addr
|
||||
victim += "OS - ["
|
||||
for k, v in self.os.iteritems():
|
||||
for k, v in self.os.items():
|
||||
victim += "%s-%s " % (k, v)
|
||||
victim += "] Services - ["
|
||||
for k, v in self.services.iteritems():
|
||||
for k, v in self.services.items():
|
||||
victim += "%s-%s " % (k, v)
|
||||
victim += ']'
|
||||
victim += "target monkey: %s" % self.monkey_exe
|
||||
return victim
|
||||
|
||||
def set_default_server(self, default_server):
|
||||
|
|
|
@ -160,20 +160,20 @@ class ChaosMonkey(object):
|
|||
|
||||
LOG.info("Trying to exploit %r with exploiter %s...", machine, exploiter.__class__.__name__)
|
||||
|
||||
result = False
|
||||
try:
|
||||
if exploiter.exploit_host():
|
||||
result = exploiter.exploit_host()
|
||||
if result:
|
||||
successful_exploiter = exploiter
|
||||
exploiter.send_exploit_telemetry(True)
|
||||
break
|
||||
else:
|
||||
LOG.info("Failed exploiting %r with exploiter %s", machine, exploiter.__class__.__name__)
|
||||
exploiter.send_exploit_telemetry(False)
|
||||
|
||||
except Exception as exc:
|
||||
LOG.exception("Exception while attacking %s using %s: %s",
|
||||
machine, exploiter.__class__.__name__, exc)
|
||||
exploiter.send_exploit_telemetry(False)
|
||||
continue
|
||||
finally:
|
||||
exploiter.send_exploit_telemetry(result)
|
||||
|
||||
if successful_exploiter:
|
||||
self._exploited_machines.add(machine)
|
||||
|
|
|
@ -138,12 +138,14 @@ def get_ips_from_interfaces():
|
|||
res = []
|
||||
ifs = get_host_subnets()
|
||||
for net_interface in ifs:
|
||||
host_addr = ipaddress.ip_address(net_interface['addr'])
|
||||
ip_interface = ipaddress.ip_interface(u"%s/%s" % (net_interface['addr'], net_interface['netmask']))
|
||||
address_str = unicode(net_interface['addr'])
|
||||
netmask_str = unicode(net_interface['netmask'])
|
||||
host_address = ipaddress.ip_address(address_str)
|
||||
ip_interface = ipaddress.ip_interface(u"%s/%s" % (address_str, netmask_str))
|
||||
# limit subnet scans to class C only
|
||||
if ip_interface.network.num_addresses > 255:
|
||||
ip_interface = ipaddress.ip_interface(u"%s/24" % net_interface['addr'])
|
||||
addrs = [str(addr) for addr in ip_interface.network.hosts() if addr != host_addr]
|
||||
ip_interface = ipaddress.ip_interface(u"%s/24" % address_str)
|
||||
addrs = [str(addr) for addr in ip_interface.network.hosts() if addr != host_address]
|
||||
res.extend(addrs)
|
||||
return res
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class RelativeRange(NetworkRange):
|
|||
def __init__(self, base_address, shuffle=True):
|
||||
base_address = struct.unpack(">L", socket.inet_aton(base_address))[0]
|
||||
super(RelativeRange, self).__init__(base_address, shuffle=shuffle)
|
||||
self._size = self._config.range_size
|
||||
self._size = 1
|
||||
|
||||
def __repr__(self):
|
||||
return "<RelativeRange %s-%s>" % (socket.inet_ntoa(struct.pack(">L", self._base_address - self._size)),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
How to build a monkey binary from scratch.
|
||||
|
||||
The monkey is composed of three seperate parts.
|
||||
The monkey is composed of three separate parts.
|
||||
* The Infection Monkey itself - PyInstaller compressed python archives
|
||||
* Sambacry binaries - Two linux binaries, 32/64 bit.
|
||||
* Mimikatz binaries - Two windows binaries, 32/64 bit.
|
||||
|
@ -9,50 +9,65 @@ The monkey is composed of three seperate parts.
|
|||
|
||||
1. Install python 2.7. Preferably you should use ActiveState Python which includes pywin32 built in.
|
||||
You must use an up to date version, at least version 2.7.10
|
||||
http://www.activestate.com/activepython/downloads
|
||||
https://www.python.org/downloads/release/python-2712/
|
||||
If not using ActiveState, install pywin32, minimum build 219
|
||||
http://sourceforge.net/projects/pywin32/files/pywin32
|
||||
3. a. install VCForPython27.msi
|
||||
https://aka.ms/vcpython27
|
||||
b. if not installed, install Microsoft Visual C++ 2010 SP1 Redistributable Package
|
||||
32bit: http://www.microsoft.com/en-us/download/details.aspx?id=8328
|
||||
64bit: http://www.microsoft.com/en-us/download/details.aspx?id=13523
|
||||
4. Download the dependent python packages using
|
||||
pip install -r requirements.txt
|
||||
5. Download and extract UPX binary to [source-path]\monkey\chaos_monkey\bin\upx.exe:
|
||||
https://github.com/upx/upx/releases/download/v3.94/upx394w.zip
|
||||
6. To build the final exe:
|
||||
1 cd [code location]/chaos_monkey
|
||||
build_windows.bat
|
||||
output is in dist\monkey.exe
|
||||
https://www.python.org/download/releases/2.7/
|
||||
2. Install pywin32 (if you didn't install ActiveState Python)
|
||||
Install pywin32, minimum build 219
|
||||
http://sourceforge.net/projects/pywin32/files/pywin32
|
||||
3. Add python directories to PATH environment variable (if you didn't install ActiveState Python)
|
||||
a. Run the following command on a cmd console (Replace C:\Python27 with your python directory if it's different)
|
||||
setx /M PATH "%PATH%;C:\Python27;C:\Pytohn27\Scripts
|
||||
b. Close the console, make sure you execute all commands in a new cmd console from now on.
|
||||
4. Install pip
|
||||
a. Download and run the pip installer
|
||||
https://bootstrap.pypa.io/get-pip.py
|
||||
5. Install further dependencies
|
||||
a. install VCForPython27.msi
|
||||
https://aka.ms/vcpython27
|
||||
b. if not installed, install Microsoft Visual C++ 2010 SP1 Redistributable Package
|
||||
32bit: http://www.microsoft.com/en-us/download/details.aspx?id=8328
|
||||
64bit: http://www.microsoft.com/en-us/download/details.aspx?id=13523
|
||||
6. Download the dependent python packages using
|
||||
pip install -r requirements.txt
|
||||
7. Download and extract UPX binary to [source-path]\monkey\chaos_monkey\bin\upx.exe:
|
||||
https://github.com/upx/upx/releases/download/v3.94/upx394w.zip
|
||||
8. Build/Download Sambacry and Mimikatz binaries
|
||||
a. Build/Download according to sections at the end of this readme.
|
||||
b. Place the binaries under [code location]\chaos_monkey\bin
|
||||
9. To build the final exe:
|
||||
cd [code location]/chaos_monkey
|
||||
build_windows.bat
|
||||
output is placed under dist\monkey.exe
|
||||
|
||||
--- Linux ---
|
||||
|
||||
Tested on Ubuntu 16.04 and 17.04.
|
||||
|
||||
1. Run:
|
||||
sudo apt-get update
|
||||
sudo apt-get install python-pip python-dev libffi-dev upx libssl-dev libc++1
|
||||
Install the python packages listed in requirements.txt.
|
||||
Using pip install -r requirements.txt
|
||||
sudo apt-get install winbind dnet-common
|
||||
2. Put source code in Code/monkey/chaos_monkey
|
||||
3. To build, run in terminal:
|
||||
cd [code location]/chaos_monkey
|
||||
chmod +x build_linux.sh
|
||||
./build_linux.sh
|
||||
output is in dist/monkey
|
||||
1. Install dependencies by running:
|
||||
sudo apt-get update
|
||||
sudo apt-get install python-pip python-dev libffi-dev upx libssl-dev libc++1
|
||||
Install the python packages listed in requirements.txt using pip
|
||||
cd [code location]/chaos_monkey
|
||||
pip install -r requirements.txt
|
||||
2. Build Sambacry binaries
|
||||
a. Build/Download according to sections at the end of this readme.
|
||||
b. Place the binaries under [code location]\chaos_monkey\bin
|
||||
3. To build, run in terminal:
|
||||
cd [code location]/chaos_monkey
|
||||
chmod +x build_linux.sh
|
||||
./build_linux.sh
|
||||
output is placed under dist/monkey
|
||||
|
||||
-- Sambacry --
|
||||
|
||||
Sambacry requires two standalone binaries to execute remotely.
|
||||
Compiling them requires gcc
|
||||
cd [code location]/chaos_monkey/monkey_utils/sambacry_monkey_runner
|
||||
./build.sh
|
||||
1. Install gcc-multilib if it's not installed
|
||||
sudo apt-get install gcc-multilib
|
||||
2. Build the binaries
|
||||
cd [code location]/chaos_monkey/monkey_utils/sambacry_monkey_runner
|
||||
./build.sh
|
||||
|
||||
-- Mimikatz --
|
||||
|
||||
Mimikatz is required for the Monkey to be able to steal credentials on Windows. It's possible to either compile from sources (requires Visual Studio 2013 and up) or download the binaries from
|
||||
https://github.com/guardicore/mimikatz/releases/tag/1.0.0
|
||||
Download both 32 and 64 bit DLLs and place them under [code location]\chaos_monkey\bin
|
||||
Download both 32 and 64 bit DLLs and place them under [code location]\chaos_monkey\bin
|
|
@ -1,5 +1,16 @@
|
|||
from flask_pymongo import PyMongo
|
||||
from flask_pymongo import MongoClient
|
||||
from pymongo.errors import ServerSelectionTimeoutError
|
||||
|
||||
__author__ = 'Barak'
|
||||
|
||||
mongo = PyMongo()
|
||||
|
||||
|
||||
def is_db_server_up(mongo_url):
|
||||
client = MongoClient(mongo_url, serverSelectionTimeoutMS=100)
|
||||
try:
|
||||
client.server_info()
|
||||
return True
|
||||
except ServerSelectionTimeoutError:
|
||||
return False
|
||||
|
|
|
@ -3,6 +3,8 @@ from __future__ import print_function # In python 2.7
|
|||
import os
|
||||
import sys
|
||||
|
||||
import time
|
||||
|
||||
BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if BASE_PATH not in sys.path:
|
||||
sys.path.insert(0, BASE_PATH)
|
||||
|
@ -10,14 +12,20 @@ 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, DEBUG_SERVER
|
||||
from cc.database import is_db_server_up
|
||||
|
||||
if __name__ == '__main__':
|
||||
from tornado.wsgi import WSGIContainer
|
||||
from tornado.httpserver import HTTPServer
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
app = init_app(os.environ.get('MONGO_URL', DEFAULT_MONGO_URL))
|
||||
mongo_url = os.environ.get('MONGO_URL', DEFAULT_MONGO_URL)
|
||||
|
||||
while not is_db_server_up(mongo_url):
|
||||
print('Waiting for MongoDB server')
|
||||
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:
|
||||
|
|
|
@ -29,7 +29,7 @@ class Root(flask_restful.Resource):
|
|||
ConfigService.init_config()
|
||||
return jsonify(status='OK')
|
||||
elif action == "killall":
|
||||
mongo.db.monkey.update({}, {'$set': {'config.alive': False, 'modifytime': datetime.now()}}, upsert=False,
|
||||
mongo.db.monkey.update({'dead': False}, {'$set': {'config.alive': False, 'modifytime': datetime.now()}}, upsert=False,
|
||||
multi=True)
|
||||
return jsonify(status='OK')
|
||||
else:
|
||||
|
|
|
@ -43,24 +43,20 @@ class Telemetry(flask_restful.Resource):
|
|||
monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])
|
||||
|
||||
try:
|
||||
if telemetry_json.get('telem_type') == 'tunnel':
|
||||
self.process_tunnel_telemetry(telemetry_json)
|
||||
elif telemetry_json.get('telem_type') == 'state':
|
||||
self.process_state_telemetry(telemetry_json)
|
||||
elif telemetry_json.get('telem_type') == 'exploit':
|
||||
self.process_exploit_telemetry(telemetry_json)
|
||||
elif telemetry_json.get('telem_type') == 'scan':
|
||||
self.process_scan_telemetry(telemetry_json)
|
||||
elif telemetry_json.get('telem_type') == 'system_info_collection':
|
||||
self.process_system_info_telemetry(telemetry_json)
|
||||
NodeService.update_monkey_modify_time(monkey["_id"])
|
||||
telem_type = telemetry_json.get('telem_type')
|
||||
if telem_type in TELEM_PROCESS_DICT:
|
||||
TELEM_PROCESS_DICT[telem_type](telemetry_json)
|
||||
else:
|
||||
print('Got unknown type of telemetry: %s' % telem_type)
|
||||
except StandardError as ex:
|
||||
print("Exception caught while processing telemetry: %s" % str(ex))
|
||||
traceback.print_exc()
|
||||
|
||||
return mongo.db.telemetry.find_one_or_404({"_id": telem_id})
|
||||
|
||||
def telemetry_to_displayed_telemetry(self, telemetry):
|
||||
@staticmethod
|
||||
def telemetry_to_displayed_telemetry(telemetry):
|
||||
monkey_guid_dict = {}
|
||||
monkeys = mongo.db.monkey.find({})
|
||||
for monkey in monkeys:
|
||||
|
@ -77,7 +73,8 @@ class Telemetry(flask_restful.Resource):
|
|||
|
||||
return objects
|
||||
|
||||
def get_edge_by_scan_or_exploit_telemetry(self, telemetry_json):
|
||||
@staticmethod
|
||||
def get_edge_by_scan_or_exploit_telemetry(telemetry_json):
|
||||
dst_ip = telemetry_json['data']['machine']['ip_addr']
|
||||
src_monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])
|
||||
dst_node = NodeService.get_monkey_by_ip(dst_ip)
|
||||
|
@ -86,7 +83,8 @@ class Telemetry(flask_restful.Resource):
|
|||
|
||||
return EdgeService.get_or_create_edge(src_monkey["_id"], dst_node["_id"])
|
||||
|
||||
def process_tunnel_telemetry(self, telemetry_json):
|
||||
@staticmethod
|
||||
def process_tunnel_telemetry(telemetry_json):
|
||||
monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])["_id"]
|
||||
if telemetry_json['data']['proxy'] is not None:
|
||||
tunnel_host_ip = telemetry_json['data']['proxy'].split(":")[-2].replace("//", "")
|
||||
|
@ -94,15 +92,17 @@ class Telemetry(flask_restful.Resource):
|
|||
else:
|
||||
NodeService.unset_all_monkey_tunnels(monkey_id)
|
||||
|
||||
def process_state_telemetry(self, telemetry_json):
|
||||
@staticmethod
|
||||
def process_state_telemetry(telemetry_json):
|
||||
monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])
|
||||
if telemetry_json['data']['done']:
|
||||
NodeService.set_monkey_dead(monkey, True)
|
||||
else:
|
||||
NodeService.set_monkey_dead(monkey, False)
|
||||
|
||||
def process_exploit_telemetry(self, telemetry_json):
|
||||
edge = self.get_edge_by_scan_or_exploit_telemetry(telemetry_json)
|
||||
@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.pop('machine')
|
||||
|
@ -115,8 +115,9 @@ class Telemetry(flask_restful.Resource):
|
|||
if new_exploit['result']:
|
||||
EdgeService.set_edge_exploited(edge)
|
||||
|
||||
def process_scan_telemetry(self, telemetry_json):
|
||||
edge = self.get_edge_by_scan_or_exploit_telemetry(telemetry_json)
|
||||
@staticmethod
|
||||
def process_scan_telemetry(telemetry_json):
|
||||
edge = Telemetry.get_edge_by_scan_or_exploit_telemetry(telemetry_json)
|
||||
data = telemetry_json['data']['machine']
|
||||
ip_address = data.pop("ip_addr")
|
||||
new_scan = \
|
||||
|
@ -144,7 +145,8 @@ class Telemetry(flask_restful.Resource):
|
|||
{"$set": {"os.version": scan_os["version"]}},
|
||||
upsert=False)
|
||||
|
||||
def process_system_info_telemetry(self, telemetry_json):
|
||||
@staticmethod
|
||||
def process_system_info_telemetry(telemetry_json):
|
||||
if 'credentials' in telemetry_json['data']:
|
||||
creds = telemetry_json['data']['credentials']
|
||||
for user in creds:
|
||||
|
@ -155,3 +157,18 @@ class Telemetry(flask_restful.Resource):
|
|||
ConfigService.creds_add_lm_hash(creds[user]['lm_hash'])
|
||||
if 'ntlm_hash' in creds[user]:
|
||||
ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash'])
|
||||
|
||||
@staticmethod
|
||||
def process_trace_telemetry(telemetry_json):
|
||||
# Nothing to do
|
||||
return
|
||||
|
||||
TELEM_PROCESS_DICT = \
|
||||
{
|
||||
'tunnel': Telemetry.process_tunnel_telemetry,
|
||||
'state': Telemetry.process_state_telemetry,
|
||||
'exploit': Telemetry.process_exploit_telemetry,
|
||||
'scan': Telemetry.process_scan_telemetry,
|
||||
'system_info_collection': Telemetry.process_system_info_telemetry,
|
||||
'trace': Telemetry.process_trace_telemetry
|
||||
}
|
|
@ -191,10 +191,13 @@ SCHEMA = {
|
|||
"description": "Determines whether the monkey should scan its subnets additionally"
|
||||
},
|
||||
"depth": {
|
||||
"title": "Depth" + WARNING_SIGN,
|
||||
"title": "Distance from island",
|
||||
"type": "integer",
|
||||
"default": 2,
|
||||
"description": "Amount of hops allowed for the monkey to spread"
|
||||
"description":
|
||||
"Amount of hops allowed for the monkey to spread from the island. "
|
||||
+ WARNING_SIGN
|
||||
+ " Note that setting this value too high may result in the monkey propagating too far"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -208,28 +211,17 @@ SCHEMA = {
|
|||
"default": "FixedRange",
|
||||
"enum": [
|
||||
"FixedRange",
|
||||
"RelativeRange",
|
||||
"ClassCRange"
|
||||
],
|
||||
"enumNames": [
|
||||
"Fixed Range",
|
||||
"Relative Range",
|
||||
"Class C Range"
|
||||
],
|
||||
"description":
|
||||
"Determines which class to use to determine scan range."
|
||||
" Fixed Range will scan only specific IPs listed under Fixed range IP list."
|
||||
" Relative Range will scan the <Relative range size> closest ips to the machine's IP."
|
||||
" Class C Range will scan machines in the Class C network the monkey's on."
|
||||
},
|
||||
"range_size": {
|
||||
"title": "Relative range size",
|
||||
"type": "integer",
|
||||
"default": 1,
|
||||
"description":
|
||||
"Determines the size of the RelativeRange - amount of IPs to scan"
|
||||
" (Only relevant for Relative Range)"
|
||||
},
|
||||
"range_fixed": {
|
||||
"title": "Fixed range IP list",
|
||||
"type": "array",
|
||||
|
@ -304,12 +296,14 @@ SCHEMA = {
|
|||
"description": "Determines the maximum number of machines the monkey is allowed to scan"
|
||||
},
|
||||
"victims_max_exploit": {
|
||||
"title": "Max victims to exploit" + WARNING_SIGN,
|
||||
"title": "Max victims to exploit",
|
||||
"type": "integer",
|
||||
"default": 7,
|
||||
"description":
|
||||
"Determines the maximum number of machines the monkey"
|
||||
" is allowed to successfully exploit"
|
||||
" is allowed to successfully exploit. " + WARNING_SIGN
|
||||
+ " Note that setting this value too high may result in the monkey propagating to "
|
||||
"a high number of machines"
|
||||
},
|
||||
"timeout_between_iterations": {
|
||||
"title": "Wait time between iterations",
|
||||
|
@ -587,7 +581,7 @@ SCHEMA = {
|
|||
"type": "object",
|
||||
"properties": {
|
||||
"exploiter_classes": {
|
||||
"title": "Exploits" + WARNING_SIGN,
|
||||
"title": "Exploits",
|
||||
"type": "array",
|
||||
"uniqueItems": True,
|
||||
"items": {
|
||||
|
@ -601,7 +595,9 @@ SCHEMA = {
|
|||
"SambaCryExploiter",
|
||||
"ElasticGroovyExploiter"
|
||||
],
|
||||
"description": "Determines which exploits to use"
|
||||
"description":
|
||||
"Determines which exploits to use. " + WARNING_SIGN
|
||||
+ " Note that using unsafe exploits may cause crashes of the exploited machine/service"
|
||||
},
|
||||
"skip_exploit_if_file_exist": {
|
||||
"title": "Skip exploit if file exists",
|
||||
|
|
|
@ -31,7 +31,7 @@ class NodeService:
|
|||
# node is infected
|
||||
new_node = NodeService.monkey_to_net_node(monkey)
|
||||
for key in monkey:
|
||||
if key not in ["_id", "modifytime", "parent", "dead"]:
|
||||
if key not in ['_id', 'modifytime', 'parent', 'dead', 'description']:
|
||||
new_node[key] = monkey[key]
|
||||
|
||||
else:
|
||||
|
@ -209,8 +209,14 @@ class NodeService:
|
|||
|
||||
@staticmethod
|
||||
def set_monkey_dead(monkey, is_dead):
|
||||
props_to_set = {'dead': is_dead}
|
||||
|
||||
# Cancel the force kill once monkey died
|
||||
if is_dead:
|
||||
props_to_set['config.alive'] = True
|
||||
|
||||
mongo.db.monkey.update({"guid": monkey['guid']},
|
||||
{'$set': {'dead': is_dead}},
|
||||
{'$set': props_to_set},
|
||||
upsert=False)
|
||||
|
||||
@staticmethod
|
||||
|
@ -255,9 +261,10 @@ class NodeService:
|
|||
{'dead': {'$ne': True}, 'keepalive': {'$gte': datetime.now() - timedelta(minutes=10)}}):
|
||||
return
|
||||
|
||||
# config.alive is changed to true to cancel the force kill of dead monkeys
|
||||
mongo.db.monkey.update(
|
||||
{'keepalive': {'$lte': datetime.now() - timedelta(minutes=10)}, 'dead': {'$ne': True}},
|
||||
{'$set': {'dead': True, 'modifytime': datetime.now()}}, upsert=False, multi=True)
|
||||
{'$set': {'dead': True, 'config.alive': True, 'modifytime': datetime.now()}}, upsert=False, multi=True)
|
||||
|
||||
@staticmethod
|
||||
def is_any_monkey_alive():
|
||||
|
|
|
@ -64,11 +64,9 @@
|
|||
"bootstrap": "^3.3.7",
|
||||
"core-js": "^2.5.1",
|
||||
"fetch": "^1.1.0",
|
||||
"github-markdown-css": "^2.8.0",
|
||||
"marked": "^0.3.6",
|
||||
"js-file-download": "^0.4.1",
|
||||
"normalize.css": "^4.0.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"raw-loader": "^0.5.1",
|
||||
"react": "^15.6.1",
|
||||
"react-bootstrap": "^0.31.2",
|
||||
"react-copy-to-clipboard": "^5.0.0",
|
||||
|
|
|
@ -1,191 +0,0 @@
|
|||
Infection Monkey
|
||||
====================
|
||||
|
||||
### Data center Security Testing Tool
|
||||
------------------------
|
||||
|
||||
Welcome to the Infection Monkey!
|
||||
|
||||
The Infection Monkey is an open source security tool for testing a data center's resiliency to perimeter breaches and internal server infection. The Monkey uses various methods to self propagate across a data center and reports success to a centralized C&C server. To read more about the Monkey, visit https://www.guardicore.com/infectionmonkey/
|
||||
|
||||
### http://www.guardicore.com/the-infected-chaos-monkey/
|
||||
|
||||
Features include:
|
||||
|
||||
* Multiple propagation techniques:
|
||||
* Predefined passwords
|
||||
* Common exploits
|
||||
* Multiple exploit methods:
|
||||
* SSH
|
||||
* SMB
|
||||
* RDP
|
||||
* WMI
|
||||
* Shellshock
|
||||
* A C&C server with a dedicated UI to visualize the Monkey's progress inside the data center
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
The Infection Monkey is comprised of two parts: the Monkey and the C&C server.
|
||||
The monkey is the tool which infects other machines and propagates to them, while the C&C server collects all Monkey reports and displays them to the user.
|
||||
|
||||
### Requirements
|
||||
|
||||
The C&C Server has been tested on Ubuntu 14.04,15.04 and 16.04.
|
||||
The Monkey itself has been tested on Windows XP, 7, 8.1 and 10. The Linux build has been tested on Ubuntu server 14.04 and 15.10.
|
||||
|
||||
### Installation
|
||||
|
||||
Warning! The Debian package will uninstall the python library 'bson' because of an issue with pymongo. You can reinstall it later, but monkey island will probably not work.
|
||||
|
||||
For off-the-shelf use, download our Debian package from our website and follow the guide [written in our blog](https://www.guardicore.com/2016/07/infection-monkey-loose-2/).
|
||||
To manually set up and the C&C server follow the instructions on [Monkey Island readme](monkey_island/readme.txt). If you wish to compile the binaries yourself, follow the instructions under Building the Monkey from Source.
|
||||
|
||||
### Initial configuration.
|
||||
Whether you're downloading or building the Monkey from source, the Infection Monkey is comprised of 4 executable files for different platforms plus a default configuration file.
|
||||
|
||||
Monkey configuration is stored in two places:
|
||||
1. By default, the Monkey uses a local configuration file (usually, config.bin). This configuration file must include the address of the Monkey's C&C server.
|
||||
2. After successfully connecting to the C&C server, the monkey downloads a new configuration from the server and discards the local configuration. It is possible to change the default configuration from the C&C server's UI.
|
||||
|
||||
In both cases the command server hostname should be modified to point to your local instance of the Monkey Island (note that this doesn't require connectivity right off the bat). In addition, to improve the Monkey's chances of spreading, you can pre-seed it with credentials and usernames commonly used.
|
||||
|
||||
Both configuration options use a JSON format for specifying options; see "Options" below for details.
|
||||
|
||||
### Running the C&C Server
|
||||
|
||||
To run the C&C Server, install our infected Monkey debian package on a specific server. The initial infected machine doesn't require a direct link to this server.
|
||||
|
||||
### Unleashing the Monkey
|
||||
|
||||
Once configured, run the monkey using ```./monkey-linux-64 m0nk3y -c config.bin -s 41.50.73.31:5000``` (Windows is identical). This can be done at multiple points in the network simultaneously.
|
||||
|
||||
Command line options include:
|
||||
* `-c`, `--config`: set configuration file. JSON file with configuration values, will override compiled configuration.
|
||||
* `-p`, `--parent`: set monkey’s parent uuid, allows better recognition of exploited monkeys in c&c
|
||||
* `-t`, `--tunnel`: ip:port, set default tunnel for Monkey when connecting to c&c.
|
||||
* `-d`, `--depth` : sets the Monkey's current operation depth.
|
||||
|
||||
|
||||
How the Monkey works
|
||||
---------------------
|
||||
|
||||
1. Wakeup connection to c&c, sends basic info of the current machine and the configuration the monkey uses to the c&c.
|
||||
1. First try direct connection to c&c.
|
||||
2. If direct connection fails, try connection through a tunnel, a tunnel is found according to specified parameter (the default tunnel) or by sending a multicast query and waiting for another monkey to answer.
|
||||
3. If no connection can be made to c&c, continue without it.
|
||||
2. If a firewall app is running on the machine (supports Windows Firewall for Win XP and Windows Advanced Firewall for Win 7+), try to add a rule to allow all our traffic.
|
||||
3. Startup of tunnel for other Monkeys (if connection to c&c works).
|
||||
1. Firewall is checked to allow listening sockets (if we failed to add a rule to Windows firewall for example, the tunnel will not be created)
|
||||
2. Will answer multicast requests from other Monkeys in search of a tunnel.
|
||||
4. Running exploitation sessions, will run x sessions according to configuration:
|
||||
1. Connect to c&c and get the latest configuration
|
||||
2. Scan ip ranges according to configuration.
|
||||
3. Try fingerprinting each host that answers, using the classes defined in the configuration (SMBFinger, SSHFinger, etc)
|
||||
4. Try exploitation on each host found, for each exploit class in configuration:
|
||||
1. check exploit class supports target host (can be disabled by configuration)
|
||||
2. each exploitation class will use the data acquired in fingerprinting, or during the exploit, to find the suitable Monkey executable for the host from the c&c.
|
||||
1. If c&c connection fails, and the source monkey’s executable is suitable, we use it.
|
||||
2. If a suitable executable isn’t found, exploitation will fail.
|
||||
3. Executables are cached in memory.
|
||||
5. will skip hosts that are already exploited in next run
|
||||
6. will skip hosts that failed during exploitation in next run (can be disabled by configuration)
|
||||
5. Close tunnel before exiting
|
||||
Wait for monkeys using the tunnel to unregister for it
|
||||
Cleanup
|
||||
Remove firewall rules if added
|
||||
|
||||
Configuration Options
|
||||
---------------------
|
||||
|
||||
Key | Type | Description | Possible Values
|
||||
--- | ---- | ----------- | ---------------
|
||||
alive | bool | sets whether or not the monkey is alive. if false will stop scanning and exploiting
|
||||
command_servers | array | addresses of c&c servers to try to connect | example: ["russian-mail-brides.com:5000"]
|
||||
singleton_mutex_name | string | string of the mutex name for single instance | example: {2384ec59-0df8-4ab9-918c-843740924a28}
|
||||
self_delete_in_cleanup | bool | sets whether or not to self delete the monkey executable when stopped
|
||||
use_file_logging | bool | sets whether or not to use a log file
|
||||
monkey_log_path_[windows/linux] | string | file path for monkey logger.
|
||||
kill_file_path_[windows/linux] | string | file path that the Monkey checks to prevent running
|
||||
timeout_between_iterations | int | how long to wait between scan iterations
|
||||
max_iterations | int | how many scan iterations to perform on each run
|
||||
internet_services | array | addresses of internet servers to ping and check if the monkey has internet acccess
|
||||
victims_max_find | int | how many victims to look for in a single scan iteration
|
||||
victims_max_exploit | int | how many victims to exploit before stopping
|
||||
retry_failed_explotation | bool | sets whether or not to retry failed hosts on next scan
|
||||
local_network_scan | bool | sets whether to auto detect and scan local subnets
|
||||
range_class | class name | sets which ip ranges class is used to construct the list of ips to scan | `FixedRange` - scan list is a static ips list, `RelativeRange` - scan list will be constructed according to ip address of the machine and size of the scan, `ClassCRange` - will scan the entire class c the machine is in.
|
||||
range_fixed | tuple of strings | list of ips to scan
|
||||
RelativeRange range_size | int | number of hosts to scan in relative range
|
||||
scanner_class | class name | sets which scan class to use when scanning for hosts to exploit | `TCPScanner` - searches for hosts according to open tcp ports, `PingScanner` - searches for hosts according to ping scan
|
||||
finger_classes | tuple of class names | sets which fingerprinting classes to use | in the list: `SMBFinger` - get host os info by checking smb info, `SSHFinger` - get host os info by checking ssh banner, `PingScanner` - get host os type by checking ping ttl. For example: `(SMBFinger, SSHFinger, PingScanner)`
|
||||
exploiter_classes | tuple of class names | | `SmbExploiter` - exploit using smb connection, `WmiExploiter` - exploit using wmi connection, `RdpExploiter` - exploit using rdp connection, `Ms08_067_Exploiter` - exploit using ms08_067 smb exploit, `SSHExploiter` - exploit using ssh connection
|
||||
tcp_target_ports | list of int | which ports to scan using TCPScanner
|
||||
tcp_scan_timeout | int | timeout for tcp connection in tcp scan (in milliseconds)
|
||||
tcp_scan_interval | int | time to wait between ports in the tcp scan (in milliseconds)
|
||||
tcp_scan_get_banner | bool | sets whether or not to read a banner from the tcp ports when scanning
|
||||
ping_scan_timeout | int | timeout for the ping command (in milliseconds) utilised by PingScanner
|
||||
skip_exploit_if_file_exist | bool | sets whether or not to abort exploit if the monkey already exists in target, used by SmbExploiter
|
||||
psexec_user | string | user to use for connection, utilised by SmbExploiter/WmiExploiter/RdpExploiter
|
||||
psexec_passwords | list of strings | list of passwords to use when trying to exploit
|
||||
rdp_use_vbs_download | bool | sets whether to use vbs payload for rdp exploitation in RdpExploiter. If false, bits payload is used (will fail if bitsadmin.exe doesn’t exist)
|
||||
ms08_067_exploit_attempt | int | number of times to try and exploit using ms08_067 exploit
|
||||
ms08_067_remote_user_add | string | user to add to target when using ms08_067 exploit
|
||||
ms08_067_remote_user_pass | string | password of the user the exploit will add
|
||||
ssh_user | string | user to use for ssh connection, used by SSHExploiter
|
||||
ssh_passwords | list of strings | list of passwords to use when trying to exploit using SSHExploiter
|
||||
dropper_set_date | bool | whether or not to change the monkey file date to match other files
|
||||
dropper_target_path_[windows/linux] | string | path for the dropper
|
||||
serialize_config | bool | sets whether or not to locally save the running configuration after finishing
|
||||
|
||||
|
||||
Building the Monkey from source
|
||||
-------------------------------
|
||||
If you want to build the monkey from source instead of using our provided packages, follow the instructions at the readme files under [chaos_monkey](chaos_monkey) and [monkey_island](monkey_island).
|
||||
|
||||
|
||||
License
|
||||
=======
|
||||
Copyright (c) 2016 Guardicore Ltd
|
||||
|
||||
See the [LICENSE](LICENSE) file for license rights and limitations (GPLv3).
|
||||
|
||||
Dependent packages
|
||||
---------------------
|
||||
|
||||
Dependency | License | Notes
|
||||
----------------------------|----------------------------|----------------------------
|
||||
libffi-dev | https://github.com/atgreen/libffi/blob/master/LICENSE
|
||||
PyCrypto | Public domain
|
||||
upx | Custom license, http://upx.sourceforge.net/upx-license.html
|
||||
bson | BSD
|
||||
enum34 | BSD
|
||||
pyasn1 | BSD
|
||||
psutil | BSD
|
||||
flask | BSD
|
||||
flask-Pymongo | BSD
|
||||
Flask-Restful | BSD
|
||||
python-dateutil | Simplified BSD
|
||||
zope | ZPL 2.1
|
||||
Bootstrap | MIT
|
||||
Bootstrap Switch | Apache 2.0
|
||||
Bootstrap Dialog | MIT
|
||||
JSON Editor | MIT
|
||||
Datatables | MIT
|
||||
jQuery | MIT
|
||||
cffi | MIT
|
||||
twisted | MIT
|
||||
typeahead.js | MIT
|
||||
Font Awesome | MIT
|
||||
vis.js | MIT/Apache 2.0
|
||||
impacket | Apache Modified
|
||||
Start Bootstrap (UI Theme) | Apache 2.0
|
||||
requests | Apache 2.0
|
||||
grequests | BSD
|
||||
odict | Python Software Foundation License
|
||||
paramiko | LGPL
|
||||
rdpy | GPL-3
|
||||
winbind | GPL-3
|
||||
pyinstaller | GPL
|
||||
Celery | BSD
|
||||
mimikatz | CC BY 4.0 | We use an altered version of mimikatz made by gentilkiwi: https://github.com/guardicore/mimikatz
|
|
@ -10,13 +10,12 @@ import MapPage from 'components/pages/MapPage';
|
|||
import TelemetryPage from 'components/pages/TelemetryPage';
|
||||
import StartOverPage from 'components/pages/StartOverPage';
|
||||
import ReportPage from 'components/pages/ReportPage';
|
||||
import ReadMePage from 'components/pages/ReadMePage';
|
||||
import LicensePage from 'components/pages/LicensePage';
|
||||
|
||||
require('normalize.css/normalize.css');
|
||||
require('react-data-components/css/table-twbs.css');
|
||||
require('styles/App.css')
|
||||
require('styles/App.css');
|
||||
require('react-toggle/style.css');
|
||||
require('github-markdown-css/github-markdown.css');
|
||||
|
||||
let logoImage = require('../images/monkey-logo.png');
|
||||
let guardicoreLogoImage = require('../images/guardicore-logo.png');
|
||||
|
@ -101,12 +100,12 @@ class AppComponent extends React.Component {
|
|||
<li>
|
||||
<NavLink to="/report">
|
||||
<span className="number">4.</span>
|
||||
Pen. Test Report
|
||||
Security Report
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink to="/start-over">
|
||||
<span className="number">5.</span>
|
||||
<span className="number"><i className="fa fa-undo" style={{'marginLeft': '-1px'}}/></span>
|
||||
Start Over
|
||||
</NavLink>
|
||||
</li>
|
||||
|
@ -115,18 +114,19 @@ class AppComponent extends React.Component {
|
|||
<hr/>
|
||||
<ul>
|
||||
<li><NavLink to="/configure">Configuration</NavLink></li>
|
||||
<li><NavLink to="/infection/telemetry">Monkey Telemetry</NavLink></li>
|
||||
<li><NavLink to="/readme">Read Me</NavLink></li>
|
||||
<li><NavLink to="/infection/telemetry">Log</NavLink></li>
|
||||
</ul>
|
||||
|
||||
<hr/>
|
||||
<div className="guardicore-link text-center">
|
||||
<div className="guardicore-link text-center" style={{'marginBottom': '0.5em'}}>
|
||||
<span>Powered by</span>
|
||||
<a href="http://www.guardicore.com" target="_blank">
|
||||
<img src={guardicoreLogoImage} alt="GuardiCore"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="license-link text-center">
|
||||
<NavLink to="/license">License</NavLink>
|
||||
</div>
|
||||
</Col>
|
||||
<Col sm={9} md={10} smOffset={3} mdOffset={2} className="main">
|
||||
<Route exact path="/" render={(props) => ( <RunServerPage onStatusChange={this.updateStatus} /> )} />
|
||||
|
@ -136,7 +136,7 @@ class AppComponent extends React.Component {
|
|||
<Route path="/infection/telemetry" render={(props) => ( <TelemetryPage onStatusChange={this.updateStatus} /> )} />
|
||||
<Route path="/start-over" render={(props) => ( <StartOverPage onStatusChange={this.updateStatus} /> )} />
|
||||
<Route path="/report" render={(props) => ( <ReportPage onStatusChange={this.updateStatus} /> )} />
|
||||
<Route path="/readme" render={(props) => ( <ReadMePage onStatusChange={this.updateStatus} /> )} />
|
||||
<Route path="/license" render={(props) => ( <LicensePage onStatusChange={this.updateStatus} /> )} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Grid>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import Form from 'react-jsonschema-form';
|
||||
import {Col, Nav, NavItem} from 'react-bootstrap';
|
||||
import fileDownload from 'js-file-download';
|
||||
|
||||
class ConfigurePageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -14,10 +15,10 @@ class ConfigurePageComponent extends React.Component {
|
|||
this.state = {
|
||||
schema: {},
|
||||
configuration: {},
|
||||
saved: false,
|
||||
reset: false,
|
||||
lastAction: 'none',
|
||||
sections: [],
|
||||
selectedSection: 'basic'
|
||||
selectedSection: 'basic',
|
||||
allMonkeysAreDead: true
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -36,6 +37,7 @@ class ConfigurePageComponent extends React.Component {
|
|||
selectedSection: 'basic'
|
||||
})
|
||||
});
|
||||
this.updateMonkeysRunning();
|
||||
}
|
||||
|
||||
onSubmit = ({formData}) => {
|
||||
|
@ -50,8 +52,7 @@ class ConfigurePageComponent extends React.Component {
|
|||
.then(res => res.json())
|
||||
.then(res => {
|
||||
this.setState({
|
||||
saved: true,
|
||||
reset: false,
|
||||
lastAction: 'saved',
|
||||
schema: res.schema,
|
||||
configuration: res.configuration
|
||||
});
|
||||
|
@ -90,8 +91,7 @@ class ConfigurePageComponent extends React.Component {
|
|||
.then(res => res.json())
|
||||
.then(res => {
|
||||
this.setState({
|
||||
reset: true,
|
||||
saved: false,
|
||||
lastAction: 'reset',
|
||||
schema: res.schema,
|
||||
configuration: res.configuration
|
||||
});
|
||||
|
@ -99,6 +99,43 @@ class ConfigurePageComponent extends React.Component {
|
|||
});
|
||||
};
|
||||
|
||||
onReadFile = (event) => {
|
||||
try {
|
||||
this.setState({
|
||||
configuration: JSON.parse(event.target.result),
|
||||
selectedSection: 'basic',
|
||||
lastAction: 'import_success'
|
||||
});
|
||||
this.currentSection = 'basic';
|
||||
this.currentFormData = {};
|
||||
} catch(SyntaxError) {
|
||||
this.setState({lastAction: 'import_failure'});
|
||||
}
|
||||
};
|
||||
|
||||
exportConfig = () => {
|
||||
this.updateConfigSection();
|
||||
fileDownload(JSON.stringify(this.state.configuration, null, 2), 'monkey.conf');
|
||||
};
|
||||
|
||||
importConfig = (event) => {
|
||||
let reader = new FileReader();
|
||||
reader.onload = this.onReadFile;
|
||||
reader.readAsText(event.target.files[0]);
|
||||
event.target.value = null;
|
||||
};
|
||||
|
||||
updateMonkeysRunning = () => {
|
||||
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'])
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
let displayedSchema = {};
|
||||
if (this.state.schema.hasOwnProperty('properties')) {
|
||||
|
@ -107,7 +144,7 @@ class ConfigurePageComponent extends React.Component {
|
|||
}
|
||||
|
||||
return (
|
||||
<Col xs={8}>
|
||||
<Col xs={12} lg={8}>
|
||||
<h1 className="page-title">Monkey Configuration</h1>
|
||||
<Nav bsStyle="tabs" justified
|
||||
activeKey={this.state.selectedSection} onSelect={this.setSelectedSection}
|
||||
|
@ -120,7 +157,7 @@ class ConfigurePageComponent extends React.Component {
|
|||
this.state.selectedSection === 'basic_network' ?
|
||||
<div className="alert alert-info">
|
||||
<i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/>
|
||||
The Monkey scans its subnet if "Local network scan" is ticked. Additionally the monkey will scan machines
|
||||
The Monkey scans its subnet if "Local network scan" is ticked. Additionally the monkey scans machines
|
||||
according to its range class.
|
||||
</div>
|
||||
: <div />
|
||||
|
@ -131,14 +168,14 @@ class ConfigurePageComponent extends React.Component {
|
|||
onSubmit={this.onSubmit}
|
||||
onChange={this.onChange}>
|
||||
<div>
|
||||
<div className="alert alert-info">
|
||||
<i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/>
|
||||
Changing the configuration will only apply to new infections.
|
||||
</div>
|
||||
<div className="alert alert-warning">
|
||||
<i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/>
|
||||
Changing config values with the ⚠ mark may result in the monkey propagating too far or using dangerous exploits.
|
||||
</div>
|
||||
{ this.state.allMonkeysAreDead ?
|
||||
'' :
|
||||
<div className="alert alert-warning">
|
||||
<i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/>
|
||||
Some monkeys are currently running. Note that changing the configuration will only apply to new
|
||||
infections.
|
||||
</div>
|
||||
}
|
||||
<div className="text-center">
|
||||
<button type="submit" className="btn btn-success btn-lg" style={{margin: '5px'}}>
|
||||
Submit
|
||||
|
@ -147,22 +184,45 @@ class ConfigurePageComponent extends React.Component {
|
|||
Reset to defaults
|
||||
</button>
|
||||
</div>
|
||||
{ this.state.reset ?
|
||||
<div className="alert alert-success">
|
||||
<i className="glyphicon glyphicon-ok-sign" style={{'marginRight': '5px'}}/>
|
||||
Configuration reset successfully.
|
||||
</div>
|
||||
: ''}
|
||||
{ this.state.saved ?
|
||||
<div className="alert alert-success">
|
||||
<i className="glyphicon glyphicon-ok-sign" style={{'marginRight': '5px'}}/>
|
||||
Configuration saved successfully.
|
||||
</div>
|
||||
: ''}
|
||||
</div>
|
||||
</Form>
|
||||
: ''}
|
||||
|
||||
<div className="text-center">
|
||||
<button onClick={() => document.getElementById('uploadInputInternal').click()}
|
||||
className="btn btn-info btn-lg" style={{margin: '5px'}}>
|
||||
Import Config
|
||||
</button>
|
||||
<input id="uploadInputInternal" type="file" accept=".conf" onChange={this.importConfig} style={{display: 'none'}} />
|
||||
<button type="button" onClick={this.exportConfig} className="btn btn-info btn-lg" style={{margin: '5px'}}>
|
||||
Export config
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
{ this.state.lastAction === 'reset' ?
|
||||
<div className="alert alert-success">
|
||||
<i className="glyphicon glyphicon-ok-sign" style={{'marginRight': '5px'}}/>
|
||||
Configuration reset successfully.
|
||||
</div>
|
||||
: ''}
|
||||
{ this.state.lastAction === 'saved' ?
|
||||
<div className="alert alert-success">
|
||||
<i className="glyphicon glyphicon-ok-sign" style={{'marginRight': '5px'}}/>
|
||||
Configuration saved successfully.
|
||||
</div>
|
||||
: ''}
|
||||
{ this.state.lastAction === 'import_failure' ?
|
||||
<div className="alert alert-danger">
|
||||
<i className="glyphicon glyphicon-exclamation-sign" style={{'marginRight': '5px'}}/>
|
||||
Failed importing configuration. Invalid config file.
|
||||
</div>
|
||||
: ''}
|
||||
{ this.state.lastAction === 'import_success' ?
|
||||
<div className="alert alert-success">
|
||||
<i className="glyphicon glyphicon-ok-sign" style={{'marginRight': '5px'}}/>
|
||||
Configuration imported successfully.
|
||||
</div>
|
||||
: ''}
|
||||
</div>
|
||||
|
||||
</Col>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import React from 'react';
|
||||
import {Col} from 'react-bootstrap';
|
||||
|
||||
class LicensePageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
setSelectedSection = (key) => {
|
||||
this.setState({
|
||||
selectedSection: key
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Col xs={12} lg={8}>
|
||||
<h1 className="page-title">License</h1>
|
||||
<div style={{'fontSize': '1.2em'}}>
|
||||
<p>
|
||||
Copyright <i className="glyphicon glyphicon-copyright-mark" /> 2017 Guardicore Ltd.
|
||||
<br />
|
||||
Licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html" target="_blank">GPLv3</a>.
|
||||
</p>
|
||||
<p>
|
||||
The source code is available on <a href="https://github.com/guardicore/monkey" target="_blank">GitHub</a>
|
||||
</p>
|
||||
</div>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default LicensePageComponent;
|
|
@ -11,8 +11,6 @@ let groupNames = ['clean_unknown', 'clean_linux', 'clean_windows', 'exploited_li
|
|||
'manual_linux', 'manual_linux_running', 'manual_windows', 'manual_windows_running', 'monkey_linux',
|
||||
'monkey_linux_running', 'monkey_windows', 'monkey_windows_running'];
|
||||
|
||||
let legend = require('../../images/map-legend.png');
|
||||
|
||||
let getGroupsOptions = () => {
|
||||
let groupOptions = {};
|
||||
for (let groupName of groupNames) {
|
||||
|
@ -136,21 +134,23 @@ class MapPageComponent extends React.Component {
|
|||
return (
|
||||
<ModalContainer onClose={() => this.setState({showKillDialog: false})}>
|
||||
<ModalDialog onClose={() => this.setState({showKillDialog: false})}>
|
||||
<h1>Kill all monkeys</h1>
|
||||
<h2>Are you sure you want to kill all monkeys?</h2>
|
||||
<p style={{'fontSize': '1.2em', 'marginBottom': '2em'}}>
|
||||
Are you sure you want to kill all monkeys?
|
||||
This might take a few moments...
|
||||
</p>
|
||||
<button type="button" className="btn btn-danger btn-lg" style={{margin: '5px'}}
|
||||
onClick={() => {
|
||||
this.killAllMonkeys();
|
||||
this.setState({showKillDialog: false});
|
||||
}}>
|
||||
Kill all monkeys
|
||||
</button>
|
||||
<button type="button" className="btn btn-success btn-lg" style={{margin: '5px'}}
|
||||
onClick={() => this.setState({showKillDialog: false})}>
|
||||
Cancel
|
||||
</button>
|
||||
<div className="text-center">
|
||||
<button type="button" className="btn btn-danger btn-lg" style={{margin: '5px'}}
|
||||
onClick={() => {
|
||||
this.killAllMonkeys();
|
||||
this.setState({showKillDialog: false});
|
||||
}}>
|
||||
Kill all monkeys
|
||||
</button>
|
||||
<button type="button" className="btn btn-success btn-lg" style={{margin: '5px'}}
|
||||
onClick={() => this.setState({showKillDialog: false})}>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</ModalDialog>
|
||||
</ModalContainer>
|
||||
)
|
||||
|
@ -160,13 +160,31 @@ class MapPageComponent extends React.Component {
|
|||
return (
|
||||
<div>
|
||||
{this.renderKillDialogModal()}
|
||||
<Col xs={12}>
|
||||
<h1 className="page-title">Infection Map</h1>
|
||||
</Col>
|
||||
<Col xs={12}>
|
||||
<img src={legend}/>
|
||||
<Col xs={12} lg={8}>
|
||||
<h1 className="page-title">3. Infection Map</h1>
|
||||
</Col>
|
||||
<Col xs={8}>
|
||||
<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 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>
|
||||
*/
|
||||
}
|
||||
<div style={{height: '80vh'}}>
|
||||
<ReactiveGraph graph={this.state.graph} options={options} events={this.events}/>
|
||||
</div>
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
import React from 'react';
|
||||
import {Col} from 'react-bootstrap';
|
||||
var marked = require('marked');
|
||||
|
||||
var markdown = require('raw!../../README.md');
|
||||
|
||||
class ReadMePageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
return (
|
||||
<Col xs={8}>
|
||||
<h1 className="page-title">Read Me</h1>
|
||||
<div dangerouslySetInnerHTML={{__html: marked(markdown)}} className="markdown-body">
|
||||
</div>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ReadMePageComponent;
|
|
@ -8,9 +8,9 @@ class ReportPageComponent extends React.Component {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<Col xs={8}>
|
||||
<h1 className="page-title">Penetration Test Report</h1>
|
||||
<div style={{'fontSize': '1.5em'}}>
|
||||
<Col xs={12} lg={8}>
|
||||
<h1 className="page-title">4. Security Report</h1>
|
||||
<div style={{'fontSize': '1.2em'}}>
|
||||
<p>
|
||||
Under construction
|
||||
</p>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import {Button, Col, Well, Nav, NavItem} from 'react-bootstrap';
|
||||
import {Button, Col, Well, Nav, NavItem, Collapse} from 'react-bootstrap';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import {Icon} from 'react-fa';
|
||||
import {Link} from 'react-router-dom';
|
||||
|
@ -10,10 +10,11 @@ class RunMonkeyPageComponent extends React.Component {
|
|||
super(props);
|
||||
this.state = {
|
||||
ips: [],
|
||||
selectedIp: '0.0.0.0',
|
||||
runningOnIslandState: 'not_running',
|
||||
runningOnClientState: 'not_running',
|
||||
selectedSection: 'windows-32'
|
||||
selectedIp: '0.0.0.0',
|
||||
selectedOs: 'windows-32',
|
||||
showManual: false
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -21,7 +22,8 @@ class RunMonkeyPageComponent extends React.Component {
|
|||
fetch('/api')
|
||||
.then(res => res.json())
|
||||
.then(res => this.setState({
|
||||
ips: res['ip_addresses']
|
||||
ips: res['ip_addresses'],
|
||||
selectedIp: res['ip_addresses'][0]
|
||||
}));
|
||||
|
||||
fetch('/api/local-monkey')
|
||||
|
@ -80,17 +82,17 @@ class RunMonkeyPageComponent extends React.Component {
|
|||
});
|
||||
};
|
||||
|
||||
generateCmdDiv(ip) {
|
||||
let isLinux = (this.state.selectedSection.split('-')[0] === 'linux');
|
||||
let is32Bit = (this.state.selectedSection.split('-')[1] === '32');
|
||||
generateCmdDiv() {
|
||||
let isLinux = (this.state.selectedOs.split('-')[0] === 'linux');
|
||||
let is32Bit = (this.state.selectedOs.split('-')[1] === '32');
|
||||
let cmdText = '';
|
||||
if (isLinux) {
|
||||
cmdText = this.generateLinuxCmd(ip, is32Bit);
|
||||
cmdText = this.generateLinuxCmd(this.state.selectedIp, is32Bit);
|
||||
} else {
|
||||
cmdText = this.generateWindowsCmd(ip, is32Bit);
|
||||
cmdText = this.generateWindowsCmd(this.state.selectedIp, is32Bit);
|
||||
}
|
||||
return (
|
||||
<Well className="well-sm" style={{'margin': '0.5em'}}>
|
||||
<Well key={'cmdDiv'+this.state.selectedIp} className="well-sm" style={{'margin': '0.5em'}}>
|
||||
<div style={{'overflow': 'auto', 'padding': '0.5em'}}>
|
||||
<CopyToClipboard text={cmdText} className="pull-right btn-sm">
|
||||
<Button style={{margin: '-0.5em'}} title="Copy to Clipboard">
|
||||
|
@ -103,9 +105,15 @@ class RunMonkeyPageComponent extends React.Component {
|
|||
)
|
||||
}
|
||||
|
||||
setSelectedSection = (key) => {
|
||||
setSelectedOs = (key) => {
|
||||
this.setState({
|
||||
selectedSection: key
|
||||
selectedOs: key
|
||||
});
|
||||
};
|
||||
|
||||
setSelectedIp = (key) => {
|
||||
this.setState({
|
||||
selectedIp: key
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -119,18 +127,25 @@ class RunMonkeyPageComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
toggleManual = () => {
|
||||
this.setState({
|
||||
showManual: !this.state.showManual
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Col xs={8}>
|
||||
<h1 className="page-title">Run the Monkey</h1>
|
||||
<p style={{'fontSize': '1.2em', 'marginBottom': '2em'}}>
|
||||
You can run the monkey on the C&C server, on your local machine and basically everywhere.
|
||||
The more the merrier 😄
|
||||
<Col xs={12} lg={8}>
|
||||
<h1 className="page-title">2. Run the Monkey</h1>
|
||||
<p style={{'marginBottom': '2em', 'fontSize': '1.2em'}}>
|
||||
Go ahead and run the monkey!
|
||||
<i> (Or <Link to="/configure">configure the monkey</Link> to fine tune its behavior)</i>
|
||||
</p>
|
||||
<p style={{'marginBottom': '2em'}}>
|
||||
<p>
|
||||
<button onClick={this.runLocalMonkey}
|
||||
className="btn btn-default"
|
||||
disabled={this.state.runningOnIslandState !== 'not_running'}>
|
||||
className="btn btn-default btn-lg center-block"
|
||||
disabled={this.state.runningOnIslandState !== 'not_running'}
|
||||
>
|
||||
Run on C&C Server
|
||||
{ this.renderIconByState(this.state.runningOnIslandState) }
|
||||
</button>
|
||||
|
@ -147,22 +162,40 @@ class RunMonkeyPageComponent extends React.Component {
|
|||
*/
|
||||
}
|
||||
</p>
|
||||
<div className="run-monkey-snippets" style={{'marginBottom': '3em'}}>
|
||||
<p>
|
||||
Run one of those snippets on a host for infecting it with a Monkey:
|
||||
<br/>
|
||||
<span className="text-muted">(The IP address is used as the monkey's C&C address)</span>
|
||||
</p>
|
||||
<Nav pills justified
|
||||
activeKey={this.state.selectedSection} onSelect={this.setSelectedSection}
|
||||
style={{'marginBottom': '2em'}}>
|
||||
<NavItem key='windows-32' eventKey='windows-32'>Windows (32 bit)</NavItem>
|
||||
<NavItem key='windows-64' eventKey='windows-64'>Windows (64 bit)</NavItem>
|
||||
<NavItem key='linux-32' eventKey='linux-32'>Linux (32 bit)</NavItem>
|
||||
<NavItem key='linux-64' eventKey='linux-64'>Linux (64 bit)</NavItem>
|
||||
</Nav>
|
||||
{this.state.ips.map(ip => this.generateCmdDiv(ip))}
|
||||
</div>
|
||||
<p className="text-center">
|
||||
OR
|
||||
</p>
|
||||
<p style={{'marginBottom': '2em'}}>
|
||||
<button onClick={this.toggleManual} className={'btn btn-default btn-lg center-block' + (this.state.showManual ? ' active' : '')}>
|
||||
Run on machine of your choice
|
||||
</button>
|
||||
</p>
|
||||
<Collapse in={this.state.showManual}>
|
||||
<div style={{'marginBottom': '2em'}}>
|
||||
<p style={{'fontSize': '1.2em'}}>
|
||||
Choose the operating system where you want to run the monkey
|
||||
{this.state.ips.length > 1 ? ', and the interface to communicate with.' : '.'}
|
||||
</p>
|
||||
<Nav bsStyle="pills" justified activeKey={this.state.selectedOs} onSelect={this.setSelectedOs}>
|
||||
<NavItem key='windows-32' eventKey='windows-32'>Windows (32 bit)</NavItem>
|
||||
<NavItem key='windows-64' eventKey='windows-64'>Windows (64 bit)</NavItem>
|
||||
<NavItem key='linux-32' eventKey='linux-32'>Linux (32 bit)</NavItem>
|
||||
<NavItem key='linux-64' eventKey='linux-64'>Linux (64 bit)</NavItem>
|
||||
</Nav>
|
||||
{this.state.ips.length > 1 ?
|
||||
<Nav bsStyle="pills" justified activeKey={this.state.selectedIp} onSelect={this.setSelectedIp}
|
||||
style={{'marginBottom': '2em'}}>
|
||||
{this.state.ips.map(ip => <NavItem key={ip} eventKey={ip}>{ip}</NavItem>)}
|
||||
</Nav>
|
||||
: <div style={{'marginBottom': '2em'}} />
|
||||
}
|
||||
<p style={{'fontSize': '1.2em'}}>
|
||||
Copy the following command to your machine and run it with Administrator or root privileges.
|
||||
</p>
|
||||
{this.generateCmdDiv()}
|
||||
</div>
|
||||
</Collapse>
|
||||
|
||||
<p style={{'fontSize': '1.2em'}}>
|
||||
Go ahead and monitor the ongoing infection in the <Link to="/infection/map">Infection Map</Link> view.
|
||||
</p>
|
||||
|
|
|
@ -5,27 +5,26 @@ import {Link} from 'react-router-dom';
|
|||
class RunServerPageComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {ip: '0.0.0.0'};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
fetch('/api')
|
||||
.then(res => res.json())
|
||||
.then(res => this.setState({ip: res['ip_addresses'][0]}));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Col xs={8}>
|
||||
<h1 className="page-title">Monkey Island C&C Server</h1>
|
||||
<div style={{'fontSize': '1.5em'}}>
|
||||
<p>Your Monkey Island server is up and running on <b>{this.state.ip}</b> 👏 👏</p>
|
||||
<Col xs={12} lg={8}>
|
||||
<h1 className="page-title">1. Monkey Island C&C Server</h1>
|
||||
<div style={{'fontSize': '1.2em'}}>
|
||||
<p style={{'marginTop': '30px'}}>Congrats! You have successfully set up the Monkey Island server. 👏 👏</p>
|
||||
<p>
|
||||
The Infection Monkey is an open source security tool for testing a data center's resiliency to perimeter
|
||||
breaches and internal server infections.
|
||||
The Monkey uses various methods to propagate across a data
|
||||
center and reports to this Command and Control (C&C) server.
|
||||
</p>
|
||||
<p>
|
||||
To read more about the Monkey, visit <a href="http://infectionmonkey.com" target="_blank">infectionmonkey.com</a>
|
||||
</p>
|
||||
<p>
|
||||
Go ahead and <Link to="/run-monkey">run the monkey</Link>.
|
||||
</p>
|
||||
<p style={{'marginTop': '30px'}}>
|
||||
<i>(You can make further adjustments by <Link to="/configure">configuring the monkey</Link>)</i>
|
||||
</p>
|
||||
</div>
|
||||
</Col>
|
||||
);
|
||||
|
|
|
@ -33,7 +33,7 @@ class StartOverPageComponent extends React.Component {
|
|||
return (
|
||||
<ModalContainer onClose={() => this.setState({showCleanDialog: false})}>
|
||||
<ModalDialog onClose={() => this.setState({showCleanDialog: false})}>
|
||||
<h1>Reset environment</h1>
|
||||
<h2>Reset environment</h2>
|
||||
<p style={{'fontSize': '1.2em', 'marginBottom': '2em'}}>
|
||||
Are you sure you want to reset the environment?
|
||||
</p>
|
||||
|
@ -46,17 +46,19 @@ class StartOverPageComponent extends React.Component {
|
|||
:
|
||||
<div />
|
||||
}
|
||||
<button type="button" className="btn btn-danger btn-lg" style={{margin: '5px'}}
|
||||
onClick={() => {
|
||||
this.cleanup();
|
||||
this.setState({showCleanDialog: false});
|
||||
}}>
|
||||
Reset environment
|
||||
</button>
|
||||
<button type="button" className="btn btn-success btn-lg" style={{margin: '5px'}}
|
||||
onClick={() => this.setState({showCleanDialog: false})}>
|
||||
Cancel
|
||||
</button>
|
||||
<div className="text-center">
|
||||
<button type="button" className="btn btn-danger btn-lg" style={{margin: '5px'}}
|
||||
onClick={() => {
|
||||
this.cleanup();
|
||||
this.setState({showCleanDialog: false});
|
||||
}}>
|
||||
Reset environment
|
||||
</button>
|
||||
<button type="button" className="btn btn-success btn-lg" style={{margin: '5px'}}
|
||||
onClick={() => this.setState({showCleanDialog: false})}>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</ModalDialog>
|
||||
</ModalContainer>
|
||||
)
|
||||
|
@ -64,16 +66,13 @@ class StartOverPageComponent extends React.Component {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<Col xs={8}>
|
||||
<Col xs={12} lg={8}>
|
||||
{this.renderCleanDialogModal()}
|
||||
<h1 className="page-title">Start Over</h1>
|
||||
<div style={{'fontSize': '1.2em'}}>
|
||||
<p>
|
||||
In order to reset the entire environment, all monkeys will be ordered to kill themselves
|
||||
and the database will be cleaned up.
|
||||
</p>
|
||||
<p>
|
||||
After that you could go back to the <Link to="/run-monkey">Run Monkey</Link> page to start new infections.
|
||||
If you are finished and want to start over with a fresh configuration, erase the logs and clear the map
|
||||
you can go ahead and
|
||||
</p>
|
||||
<p style={{margin: '20px'}}>
|
||||
<button className="btn btn-danger btn-lg center-block"
|
||||
|
@ -81,11 +80,12 @@ class StartOverPageComponent extends React.Component {
|
|||
this.setState({showCleanDialog: true});
|
||||
this.updateMonkeysRunning();}
|
||||
}>
|
||||
Reset Environment
|
||||
Reset the Environment
|
||||
</button>
|
||||
</p>
|
||||
<div className="alert alert-info">
|
||||
<i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/>
|
||||
You don't have to reset the environment to keep running monkeys.
|
||||
You can continue and <Link to="/run-monkey">Run More Monkeys</Link> as you wish,
|
||||
and see the results on the <Link to="/infection/map">Infection Map</Link> without deleting anything.
|
||||
</div>
|
||||
|
|
|
@ -29,8 +29,8 @@ class TelemetryPageComponent extends React.Component {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<Col xs={12}>
|
||||
<h1 className="page-title">Monkey Telemetry</h1>
|
||||
<Col xs={12} lg={8}>
|
||||
<h1 className="page-title">Log</h1>
|
||||
<div className="data-table-container">
|
||||
<DataTable
|
||||
keys="name"
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
import React from 'react';
|
||||
import {Icon} from 'react-fa';
|
||||
import Toggle from 'react-toggle';
|
||||
import {OverlayTrigger, Tooltip} from 'react-bootstrap';
|
||||
|
||||
class PreviewPaneComponent extends React.Component {
|
||||
|
||||
generateToolTip(text) {
|
||||
return (
|
||||
<OverlayTrigger placement="top" overlay={<Tooltip id="tooltip">{text}</Tooltip>}>
|
||||
<a><i className="glyphicon glyphicon-info-sign"/></a>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
}
|
||||
|
||||
osRow(asset) {
|
||||
return (
|
||||
<tr>
|
||||
<th>Operating System</th>
|
||||
<td>{asset.os}</td>
|
||||
<td>{asset.os.charAt(0).toUpperCase() + asset.os.slice(1)}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
@ -34,26 +43,20 @@ class PreviewPaneComponent extends React.Component {
|
|||
accessibleRow(asset) {
|
||||
return (
|
||||
<tr>
|
||||
<th>Accessible From</th>
|
||||
<th>
|
||||
Accessible From
|
||||
{this.generateToolTip('List of machine which can access this one using a network protocol')}
|
||||
</th>
|
||||
<td>{asset.accessible_from_nodes.map(val => <div key={val}>{val}</div>)}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
descriptionRow(asset) {
|
||||
statusRow(asset) {
|
||||
return (
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<td>{asset.description}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
aliveRow(asset) {
|
||||
return (
|
||||
<tr>
|
||||
<th>Alive</th>
|
||||
<td>{(!asset.dead).toString()}</td>
|
||||
<th>Status</th>
|
||||
<td>{(asset.dead) ? 'Dead' : 'Alive'}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
@ -72,10 +75,14 @@ class PreviewPaneComponent extends React.Component {
|
|||
forceKillRow(asset) {
|
||||
return (
|
||||
<tr>
|
||||
<th>Force Kill</th>
|
||||
<th>
|
||||
Force Kill
|
||||
{this.generateToolTip('If this is on, monkey will die next time it communicates')}
|
||||
</th>
|
||||
<td>
|
||||
<Toggle id={asset.id} checked={!asset.config.alive} icons={false}
|
||||
onChange={(e) => this.forceKill(e, asset)} />
|
||||
<Toggle id={asset.id} checked={!asset.config.alive} icons={false} disabled={asset.dead}
|
||||
onChange={(e) => this.forceKill(e, asset)} />
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
@ -88,7 +95,10 @@ class PreviewPaneComponent extends React.Component {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<h4 style={{'marginTop': '2em'}}>Timeline</h4>
|
||||
<h4 style={{'marginTop': '2em'}}>
|
||||
Exploit Timeline
|
||||
{this.generateToolTip('Timeline of exploit attempts. Red is successful. Gray is unsuccessful')}
|
||||
</h4>
|
||||
<ul className="timeline">
|
||||
{ asset.exploits.map(exploit =>
|
||||
<li key={exploit.timestamp}>
|
||||
|
@ -124,9 +134,8 @@ class PreviewPaneComponent extends React.Component {
|
|||
<div>
|
||||
<table className="table table-condensed">
|
||||
<tbody>
|
||||
{this.descriptionRow(asset)}
|
||||
{this.aliveRow(asset)}
|
||||
{this.osRow(asset)}
|
||||
{this.statusRow(asset)}
|
||||
{this.ipsRow(asset)}
|
||||
{this.servicesRow(asset)}
|
||||
{this.accessibleRow(asset)}
|
||||
|
@ -192,7 +201,7 @@ class PreviewPaneComponent extends React.Component {
|
|||
info = this.scanInfo(this.props.item);
|
||||
break;
|
||||
case 'node':
|
||||
info = this.props.item.group.includes('monkey') ?
|
||||
info = this.props.item.group.includes('monkey', 'manual') ?
|
||||
this.infectedAssetInfo(this.props.item) : this.assetInfo(this.props.item);
|
||||
break;
|
||||
case 'island_edge':
|
||||
|
@ -214,7 +223,7 @@ class PreviewPaneComponent extends React.Component {
|
|||
{ !info ?
|
||||
<span>
|
||||
<Icon name="hand-o-left" style={{'marginRight': '0.5em'}} />
|
||||
Select an item on the map for a preview
|
||||
Select an item on the map for a detailed look
|
||||
</span>
|
||||
:
|
||||
<div>
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.5 KiB |
|
@ -270,6 +270,33 @@ body {
|
|||
background: #d30d09;
|
||||
}
|
||||
|
||||
.telemetry-console {
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 70px;
|
||||
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;
|
||||
}
|
||||
|
||||
.telemetry-console .date {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.telemetry-console .source {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.map-legend {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Full Logs Page
|
||||
*/
|
||||
|
@ -314,6 +341,11 @@ body {
|
|||
width: 100px;
|
||||
}
|
||||
|
||||
.license-link a:link, .license-link a:visited {
|
||||
color: #797979 !important;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.data-table-container .pagination {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ if [ -d "/etc/systemd/network" ]; then
|
|||
cp ${MONKEY_FOLDER}/ubuntu/systemd/*.service /lib/systemd/system/
|
||||
chmod +x ${MONKEY_FOLDER}/ubuntu/systemd/start_server.sh
|
||||
systemctl daemon-reload
|
||||
systemctl enable monkey-mongo
|
||||
systemctl enable monkey-island
|
||||
fi
|
||||
|
||||
${MONKEY_FOLDER}/create_certificate.sh
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
[Unit]
|
||||
Description=Monkey Island Service
|
||||
Wants=monkey-mongo.service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
|
|
|
@ -1,26 +1,24 @@
|
|||
How to set C&C server:
|
||||
|
||||
---------------- On Windows ----------------:
|
||||
1. Create bin folder
|
||||
1.1. create folder "bin" under monkey_island
|
||||
1. Create folder "bin" under monkey_island
|
||||
2. Place portable version of Python 2.7
|
||||
2.1. Download and install from: https://www.python.org/download/releases/2.7/
|
||||
2.2. Download & Run get-pip.py from: https://bootstrap.pypa.io/get-pip.py
|
||||
2.3. Install required python libraries using "python -m pip install -r monkey_island\requirements.txt"
|
||||
2.4. Copy Contents from Installation path (Usually C:\Python27) to monkey_island\bin\Python27
|
||||
2.5. Copy Python27.dll from System32 folder (Usually C:\Windows\System32) to monkey_island\bin\Python27
|
||||
2.6. (Optional) You may uninstall Python27 if you like.
|
||||
2.2. Install the required python libraries using "python -m pip install -r monkey_island\requirements.txt"
|
||||
2.3. Copy contents from installation path (Usually C:\Python27) to monkey_island\bin\Python27
|
||||
2.4. Copy Python27.dll from System32 folder (Usually C:\Windows\System32 or C:\Python27) to monkey_island\bin\Python27
|
||||
2.5. (Optional) You may uninstall Python27 if you like.
|
||||
3. Place portable version of mongodb
|
||||
3.1. Download from: http://downloads.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-latest.zip
|
||||
3.1. Download from: https://downloads.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-latest.zip
|
||||
3.2. Extract contents from bin folder to monkey_island\bin\mongodb.
|
||||
4. Place portable version of OpenSSL
|
||||
4.1. Download from: http://downloads.sourceforge.net/gnuwin32/openssl-0.9.8h-1-bin.zip
|
||||
4.1. Download from: https://indy.fulgan.com/SSL/openssl-1.0.2l-i386-win32.zip
|
||||
4.2. Extract content from bin folder to monkey_island\bin\openssl
|
||||
5. Download and install Microsoft Visual C++ Redisutable for Visual Studio 2017
|
||||
5. Download and install Microsoft Visual C++ redistributable for Visual Studio 2017
|
||||
5.1. Download and install from: https://go.microsoft.com/fwlink/?LinkId=746572
|
||||
6. Generate SSL Certificate
|
||||
6.1. run create_certificate.bat when your current working directory is monkey_island
|
||||
7. Put chaos monkey binaries in monkey_island\cc\binaries (create folder if it doesn't exist):
|
||||
7. Create the monkey_island\cc\binaries folder and put chaos monkey binaries inside
|
||||
monkey-linux-64 - monkey binary for linux 64bit
|
||||
monkey-linux-32 - monkey binary for linux 32bit
|
||||
monkey-windows-32.exe - monkey binary for windows 32bit
|
||||
|
@ -29,10 +27,11 @@ How to set C&C server:
|
|||
8.1. Download and install from: https://www.npmjs.com/get-npm
|
||||
9. Build Monkey Island frontend
|
||||
9.1. cd to 'monkey_island\cc\ui'
|
||||
9.2. run 'npm run dist'
|
||||
9.2. run 'npm update'
|
||||
9.3. run 'npm run dist'
|
||||
|
||||
How to run:
|
||||
1. start monkey_island\windows\run_server.bat (when your current working directory is monkey_island)
|
||||
1. When your current working directory is monkey_island, run monkey_island\windows\run_server.bat
|
||||
|
||||
---------------- On Linux ----------------:
|
||||
1. Create the following directories:
|
||||
|
@ -44,6 +43,7 @@ How to run:
|
|||
|
||||
2. Install the packages from monkey_island/requirements.txt:
|
||||
sudo python -m pip install -r /var/monkey_island/requirements.txt
|
||||
If pip is not installed, install the python-pip package. Make sure the server is running Python 2.7 and not Python 3+.
|
||||
|
||||
3. put monkey binaries in /var/monkey_island/cc/binaries
|
||||
monkey-linux-64 - monkey binary for linux 64bit
|
||||
|
@ -52,8 +52,8 @@ How to run:
|
|||
monkey-windows-64.exe - monkey binary for windows 64bi
|
||||
|
||||
4. Download MongoDB and extract it to /var/monkey_island/bin/mongodb
|
||||
for debian64 - https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian71-3.0.7.tgz
|
||||
for ubuntu64 14.10 - https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1410-clang-3.0.7.tgz
|
||||
for debian64 - https://downloads.mongodb.org/linux/mongodb-linux-x86_64-debian81-latest.tgz
|
||||
for ubuntu64 16.10 - https://downloads.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-latest.tgz
|
||||
find more at - https://www.mongodb.org/downloads#production
|
||||
untar.gz with: tar -zxvf filename.tar.gz -C /var/monkey_island/bin/mongodb
|
||||
(make sure the content of the mongo folder is in this directory, meaning this path exists:
|
||||
|
@ -72,7 +72,8 @@ How to run:
|
|||
|
||||
9. Build Monkey Island frontend
|
||||
9.1. cd to 'monkey_island/cc/ui'
|
||||
9.2. run 'npm run dist'
|
||||
9.2. run 'npm update'
|
||||
9.3. run 'npm run dist'
|
||||
|
||||
How to run:
|
||||
1. run run.sh
|
||||
1. run run.sh (located under /linux)
|
||||
|
|
Loading…
Reference in New Issue