From 5ba035495c6d200d7be81b2b88c87f0daab88661 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Thu, 7 Sep 2017 19:24:09 +0300 Subject: [PATCH 1/9] Changed bat files to reference relative files --- monkey_island/windows/create_certificate.bat | 6 +++--- monkey_island/windows/run_cc.bat | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey_island/windows/create_certificate.bat b/monkey_island/windows/create_certificate.bat index 87071197d..a9372132d 100644 --- a/monkey_island/windows/create_certificate.bat +++ b/monkey_island/windows/create_certificate.bat @@ -1,4 +1,4 @@ -C:\OpenSSL-Win64\bin\openssl.exe genrsa -out cc\server.key 1024 -C:\OpenSSL-Win64\bin\openssl.exe req -new -config C:\OpenSSL-Win64\bin\openssl.cfg -key cc\server.key -out cc\server.csr -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com" -C:\OpenSSL-Win64\bin\openssl.exe x509 -req -days 366 -in cc\server.csr -signkey cc\server.key -out cc\server.crt +bin\openssl\openssl.exe genrsa -out cc\server.key 1024 +bin\openssl\openssl.exe req -new -config bin\openssl\openssl.cfg -key cc\server.key -out cc\server.csr -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com" +bin\openssl\openssl.exe x509 -req -days 366 -in cc\server.csr -signkey cc\server.key -out cc\server.crt pause \ No newline at end of file diff --git a/monkey_island/windows/run_cc.bat b/monkey_island/windows/run_cc.bat index 597ca9ca7..0190b235d 100644 --- a/monkey_island/windows/run_cc.bat +++ b/monkey_island/windows/run_cc.bat @@ -1,4 +1,4 @@ @title C^&C Server @cd cc -@main.py +@..\bin\Python27\python main.py @pause \ No newline at end of file From fcf4b85a5f1bd7d726020d515b0d7c02cf4da3d5 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Sun, 10 Sep 2017 10:27:54 +0300 Subject: [PATCH 2/9] Remve pauses replace cd with pushd/popd --- monkey_island/windows/clear_db.bat | 3 +-- monkey_island/windows/create_certificate.bat | 3 +-- monkey_island/windows/run_cc.bat | 4 ++-- monkey_island/windows/run_mongodb.bat | 3 +-- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/monkey_island/windows/clear_db.bat b/monkey_island/windows/clear_db.bat index af6d4ec73..8597f3d32 100644 --- a/monkey_island/windows/clear_db.bat +++ b/monkey_island/windows/clear_db.bat @@ -1,5 +1,4 @@ @echo Are you sure? (Press Any Key) @pause @rmdir /s /q db -@mkdir db -@pause \ No newline at end of file +@mkdir db \ No newline at end of file diff --git a/monkey_island/windows/create_certificate.bat b/monkey_island/windows/create_certificate.bat index a9372132d..ac6555f0b 100644 --- a/monkey_island/windows/create_certificate.bat +++ b/monkey_island/windows/create_certificate.bat @@ -1,4 +1,3 @@ bin\openssl\openssl.exe genrsa -out cc\server.key 1024 bin\openssl\openssl.exe req -new -config bin\openssl\openssl.cfg -key cc\server.key -out cc\server.csr -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com" -bin\openssl\openssl.exe x509 -req -days 366 -in cc\server.csr -signkey cc\server.key -out cc\server.crt -pause \ No newline at end of file +bin\openssl\openssl.exe x509 -req -days 366 -in cc\server.csr -signkey cc\server.key -out cc\server.crt \ No newline at end of file diff --git a/monkey_island/windows/run_cc.bat b/monkey_island/windows/run_cc.bat index 0190b235d..c16c9fc6b 100644 --- a/monkey_island/windows/run_cc.bat +++ b/monkey_island/windows/run_cc.bat @@ -1,4 +1,4 @@ @title C^&C Server -@cd cc +@pushd cc @..\bin\Python27\python main.py -@pause \ No newline at end of file +@popd \ No newline at end of file diff --git a/monkey_island/windows/run_mongodb.bat b/monkey_island/windows/run_mongodb.bat index ef8876980..ca33c22d7 100644 --- a/monkey_island/windows/run_mongodb.bat +++ b/monkey_island/windows/run_mongodb.bat @@ -1,3 +1,2 @@ @title MongoDB -@bin\mongodb\mongod.exe --dbpath db -@pause \ No newline at end of file +@bin\mongodb\mongod.exe --dbpath db \ No newline at end of file From 671ffc261756ef7960e13051fbd4805b41b82b45 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Sun, 10 Sep 2017 13:17:47 +0300 Subject: [PATCH 3/9] Add batch to execute entire server --- monkey_island/windows/run_server.bat | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 monkey_island/windows/run_server.bat diff --git a/monkey_island/windows/run_server.bat b/monkey_island/windows/run_server.bat new file mode 100644 index 000000000..e2d7b70c1 --- /dev/null +++ b/monkey_island/windows/run_server.bat @@ -0,0 +1,3 @@ +if not exist db mkdir db +start windows\run_mongodb.bat +start windows\run_cc.bat \ No newline at end of file From 53a20308de310b0493ae542a827151dec3a9179b Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Sun, 10 Sep 2017 10:36:00 +0300 Subject: [PATCH 4/9] Rewrite get_host_subnets, drastically simplify Linux implementation. Cleanup code in get_ips_from_interfaces Modern python bug fix --- chaos_monkey/network/info.py | 102 ++++++++++++++--------------------- 1 file changed, 41 insertions(+), 61 deletions(-) diff --git a/chaos_monkey/network/info.py b/chaos_monkey/network/info.py index 59b40a089..6c014a609 100644 --- a/chaos_monkey/network/info.py +++ b/chaos_monkey/network/info.py @@ -1,34 +1,34 @@ import os import sys -import array import socket import struct import psutil import ipaddress +import itertools +import netifaces from subprocess import check_output from random import randint if sys.platform == "win32": - import netifaces - def local_ips(): local_hostname = socket.gethostname() return socket.gethostbyname_ex(local_hostname)[2] - def get_host_subnets(only_ips=False): - network_adapters = [] - valid_ips = local_ips() - if only_ips: - return valid_ips - interfaces = [netifaces.ifaddresses(x) for x in netifaces.interfaces()] - for inte in interfaces: - if netifaces.AF_INET in inte: - for add in inte[netifaces.AF_INET]: - if "netmask" in add and add["addr"] in valid_ips: - network_adapters.append((add["addr"], add["netmask"])) - return network_adapters + def get_host_subnets(): + ipv4_nets = [netifaces.ifaddresses(interface)[netifaces.AF_INET] + for interface in netifaces.interfaces() + if netifaces.AF_INET in netifaces.ifaddresses(interface) + ] + # flatten + ipv4_nets = itertools.chain.from_iterable(ipv4_nets) + # remove loopback + ipv4_nets = [network for network in ipv4_nets if network['addr'] != '127.0.0.1'] + # remove auto conf + ipv4_nets = [network for network in ipv4_nets if not network['addr'].startswith('169.254')] + return ipv4_nets + def get_routes(): raise NotImplementedError() @@ -36,46 +36,26 @@ if sys.platform == "win32": else: from fcntl import ioctl - def get_host_subnets(only_ips=False): - """Get the list of Linux network adapters.""" - max_bytes = 8096 - is_64bits = sys.maxsize > 2 ** 32 - if is_64bits: - offset1 = 16 - offset2 = 40 - else: - offset1 = 32 - offset2 = 32 - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - names = array.array('B', '\0' * max_bytes) - outbytes = struct.unpack('iL', ioctl( - sock.fileno(), - 0x8912, - struct.pack('iL', max_bytes, names.buffer_info()[0])))[0] - adapter_names = [names.tostring()[n_cnt:n_cnt + offset1].split('\0', 1)[0] - for n_cnt in xrange(0, outbytes, offset2)] - network_adapters = [] - for adapter_name in adapter_names: - ip_address = socket.inet_ntoa(ioctl( - sock.fileno(), - 0x8915, - struct.pack('256s', adapter_name))[20:24]) - if ip_address.startswith('127'): - continue - subnet_mask = socket.inet_ntoa(ioctl( - sock.fileno(), - 0x891b, - struct.pack('256s', adapter_name))[20:24]) - if only_ips: - network_adapters.append(ip_address) - else: - network_adapters.append((ip_address, subnet_mask)) + def get_host_subnets(): + ipv4_nets = [netifaces.ifaddresses(interface)[netifaces.AF_INET] + for interface in netifaces.interfaces() + if netifaces.AF_INET in netifaces.ifaddresses(interface) + ] + # flatten + ipv4_nets = list(itertools.chain.from_iterable(ipv4_nets)) + # remove loopback + ipv4_nets = [network for network in ipv4_nets if network['addr'] != '127.0.0.1'] + # remove auto conf + ipv4_nets = [network for network in ipv4_nets if not network['addr'].startswith('169.254')] + return ipv4_nets - return network_adapters def local_ips(): - return get_host_subnets(only_ips=True) + ipv4_nets = get_host_subnets() + valid_ips = [network['addr'] for network in ipv4_nets] + return valid_ips + def get_routes(): # based on scapy implementation for route parsing LOOPBACK_NAME = "lo" @@ -151,17 +131,17 @@ def check_internet_access(services): def get_ips_from_interfaces(): res = [] ifs = get_host_subnets() - for interface in ifs: - ipint = ipaddress.ip_interface(u"%s/%s" % interface) + 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'])) # limit subnet scans to class C only - if ipint.network.num_addresses > 255: - ipint = ipaddress.ip_interface(u"%s/24" % interface[0]) - for addr in ipint.network.hosts(): - if str(addr) == interface[0]: - continue - res.append(str(addr)) + 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] + res.extend(addrs) return res + if sys.platform == "win32": def get_ip_for_connection(target_ip): return None @@ -171,7 +151,7 @@ else: query_str = 'ip route get %s' % target_ip resp = check_output(query_str.split()) substr = resp.split() - src = substr[substr.index('src')+1] + src = substr[substr.index('src') + 1] return src except Exception: - return None \ No newline at end of file + return None From bdc9b2fcb97e2a2d8eab9868f95650e478f5659d Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Sun, 10 Sep 2017 13:11:51 +0300 Subject: [PATCH 5/9] Return network information. --- chaos_monkey/system_info/__init__.py | 4 ++++ chaos_monkey/system_info/linux_info_collector.py | 1 + chaos_monkey/system_info/windows_info_collector.py | 1 + 3 files changed, 6 insertions(+) diff --git a/chaos_monkey/system_info/__init__.py b/chaos_monkey/system_info/__init__.py index 2238fb542..728dd8966 100644 --- a/chaos_monkey/system_info/__init__.py +++ b/chaos_monkey/system_info/__init__.py @@ -2,6 +2,7 @@ import sys import socket import psutil from enum import IntEnum +from network.info import get_host_subnets, local_ips __author__ = 'uri' @@ -69,3 +70,6 @@ class InfoCollector(object): } pass self.info['process_list'] = processes + + def get_network_info(self): + self.info['network'] = {'subnets': get_host_subnets(), 'local_ips': local_ips()} diff --git a/chaos_monkey/system_info/linux_info_collector.py b/chaos_monkey/system_info/linux_info_collector.py index 64a31c865..6c7570fc0 100644 --- a/chaos_monkey/system_info/linux_info_collector.py +++ b/chaos_monkey/system_info/linux_info_collector.py @@ -14,4 +14,5 @@ class LinuxInfoCollector(InfoCollector): def get_info(self): self.get_hostname() self.get_process_list() + self.get_network_info() return self.info diff --git a/chaos_monkey/system_info/windows_info_collector.py b/chaos_monkey/system_info/windows_info_collector.py index 5cb1253ab..2ba26fd34 100644 --- a/chaos_monkey/system_info/windows_info_collector.py +++ b/chaos_monkey/system_info/windows_info_collector.py @@ -14,6 +14,7 @@ class WindowsInfoCollector(InfoCollector): def get_info(self): self.get_hostname() self.get_process_list() + self.get_network_info() mimikatz_collector = MimikatzCollector() self.info["credentials"] = mimikatz_collector.get_logon_info() return self.info From 7b4fb5d4f0c383c855eba52e4a75f149f528962d Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Mon, 11 Sep 2017 16:56:23 +0300 Subject: [PATCH 6/9] Don't return local IPs. Return computer fqdn for further information --- chaos_monkey/system_info/__init__.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/chaos_monkey/system_info/__init__.py b/chaos_monkey/system_info/__init__.py index 728dd8966..fd1aa807f 100644 --- a/chaos_monkey/system_info/__init__.py +++ b/chaos_monkey/system_info/__init__.py @@ -46,7 +46,11 @@ class InfoCollector(object): self.info = {} def get_hostname(self): - self.info['hostname'] = socket.gethostname() + """ + Adds the computer hostname to the system information. + :return: + """ + self.info['hostname'] = socket.getfqdn() def get_process_list(self): processes = {} @@ -72,4 +76,9 @@ class InfoCollector(object): self.info['process_list'] = processes def get_network_info(self): - self.info['network'] = {'subnets': get_host_subnets(), 'local_ips': local_ips()} + """ + Adds network information from the host to the system information. + Currently returns a list of networks accessible from host, containing host ip and the subnet range. + :return: None + """ + self.info['network_info'] = {'networks': get_host_subnets()} From 1bd633a0b13271e856fe44aa87987379c32ba3c9 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Mon, 11 Sep 2017 16:57:37 +0300 Subject: [PATCH 7/9] get subnets is now cross OS since it's identical + remove broadcast key since we don't have anything to do with it. --- chaos_monkey/network/info.py | 46 ++++++++++++++---------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/chaos_monkey/network/info.py b/chaos_monkey/network/info.py index 6c014a609..45b733f45 100644 --- a/chaos_monkey/network/info.py +++ b/chaos_monkey/network/info.py @@ -9,6 +9,24 @@ import netifaces from subprocess import check_output from random import randint + +def get_host_subnets(): + ipv4_nets = [netifaces.ifaddresses(interface)[netifaces.AF_INET] + for interface in netifaces.interfaces() + if netifaces.AF_INET in netifaces.ifaddresses(interface) + ] + # flatten + ipv4_nets = itertools.chain.from_iterable(ipv4_nets) + # remove loopback + ipv4_nets = [network for network in ipv4_nets if network['addr'] != '127.0.0.1'] + # remove auto conf + ipv4_nets = [network for network in ipv4_nets if not network['addr'].startswith('169.254')] + for network in ipv4_nets: + if 'broadcast' in network: + network.pop('broadcast') + return ipv4_nets + + if sys.platform == "win32": def local_ips(): @@ -16,20 +34,6 @@ if sys.platform == "win32": return socket.gethostbyname_ex(local_hostname)[2] - def get_host_subnets(): - ipv4_nets = [netifaces.ifaddresses(interface)[netifaces.AF_INET] - for interface in netifaces.interfaces() - if netifaces.AF_INET in netifaces.ifaddresses(interface) - ] - # flatten - ipv4_nets = itertools.chain.from_iterable(ipv4_nets) - # remove loopback - ipv4_nets = [network for network in ipv4_nets if network['addr'] != '127.0.0.1'] - # remove auto conf - ipv4_nets = [network for network in ipv4_nets if not network['addr'].startswith('169.254')] - return ipv4_nets - - def get_routes(): raise NotImplementedError() @@ -37,20 +41,6 @@ else: from fcntl import ioctl - def get_host_subnets(): - ipv4_nets = [netifaces.ifaddresses(interface)[netifaces.AF_INET] - for interface in netifaces.interfaces() - if netifaces.AF_INET in netifaces.ifaddresses(interface) - ] - # flatten - ipv4_nets = list(itertools.chain.from_iterable(ipv4_nets)) - # remove loopback - ipv4_nets = [network for network in ipv4_nets if network['addr'] != '127.0.0.1'] - # remove auto conf - ipv4_nets = [network for network in ipv4_nets if not network['addr'].startswith('169.254')] - return ipv4_nets - - def local_ips(): ipv4_nets = get_host_subnets() valid_ips = [network['addr'] for network in ipv4_nets] From a85d4e8775d6495ae89aaf206c5edbdc4b7318fa Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Mon, 11 Sep 2017 19:24:18 +0300 Subject: [PATCH 8/9] Documentation --- chaos_monkey/network/info.py | 15 +++++++++++++++ chaos_monkey/system_info/__init__.py | 13 ++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/chaos_monkey/network/info.py b/chaos_monkey/network/info.py index 45b733f45..605799ce3 100644 --- a/chaos_monkey/network/info.py +++ b/chaos_monkey/network/info.py @@ -11,6 +11,11 @@ from random import randint def get_host_subnets(): + """ + Returns a list of subnets visible to host (omitting loopback and auto conf networks) + Each subnet item contains the host IP in that network + the subnet. + :return: List of dict, keys are "addr" and "subnet" + """ ipv4_nets = [netifaces.ifaddresses(interface)[netifaces.AF_INET] for interface in netifaces.interfaces() if netifaces.AF_INET in netifaces.ifaddresses(interface) @@ -111,6 +116,11 @@ def get_free_tcp_port(min_range=1000, max_range=65535): def check_internet_access(services): + """ + Checks if any of the services are accessible, over ICMP + :param services: List of IPs/hostnames + :return: boolean depending on internet access + """ ping_str = "-n 1" if sys.platform.startswith("win") else "-c 1" for host in services: if os.system("ping " + ping_str + " " + host) == 0: @@ -119,6 +129,11 @@ def check_internet_access(services): def get_ips_from_interfaces(): + """ + Returns a list of IPs accessible in the host in each network interface, in the subnet. + Limits to a single class C if the network is larger + :return: List of IPs, marked as strings. + """ res = [] ifs = get_host_subnets() for net_interface in ifs: diff --git a/chaos_monkey/system_info/__init__.py b/chaos_monkey/system_info/__init__.py index fd1aa807f..b9a16d459 100644 --- a/chaos_monkey/system_info/__init__.py +++ b/chaos_monkey/system_info/__init__.py @@ -47,12 +47,18 @@ class InfoCollector(object): def get_hostname(self): """ - Adds the computer hostname to the system information. - :return: + Adds the fully qualified computer hostname to the system information. + :return: Nothing """ self.info['hostname'] = socket.getfqdn() def get_process_list(self): + """ + Adds process information from the host to the system information. + Currently lists process name, ID, parent ID, command line + and the full image path of each process. + :return: Nothing + """ processes = {} for process in psutil.process_iter(): try: @@ -78,7 +84,8 @@ class InfoCollector(object): def get_network_info(self): """ Adds network information from the host to the system information. - Currently returns a list of networks accessible from host, containing host ip and the subnet range. + Currently updates with a list of networks accessible from host, + containing host ip and the subnet range. :return: None """ self.info['network_info'] = {'networks': get_host_subnets()} From bebe1dab4d07e6fd71f14a8080056946f54c46b6 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Tue, 12 Sep 2017 14:25:47 +0300 Subject: [PATCH 9/9] Update island readme --- monkey_island/readme.txt | 45 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/monkey_island/readme.txt b/monkey_island/readme.txt index 0f7ed34ed..b85fe036b 100644 --- a/monkey_island/readme.txt +++ b/monkey_island/readme.txt @@ -1,35 +1,34 @@ How to set C&C server: ---------------- On Windows ----------------: -1. Install python 2.7 - https://www.python.org/download/releases/2.7 -2. Download & Run get-pip.py - https://bootstrap.pypa.io/get-pip.py -3. Run: - setx path "%path%;C:\Python27\;C:\Python27\Scripts" - python -m pip install flask - python -m pip install Flask-Pymongo - python -m pip install Flask-Restful - python -m pip install python-dateutil - mkdir MonkeyIsland\bin - mkdir MonkeyIsland\db - mkdir MonkeyIsland\cc\binaries -4. Put monkey binaries in MonkeyIsland\cc\binaries: +1. Create bin folder + 1.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. +3. Place portable version of mongodb + 3.1. Download from: http://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.2. Extract content from bin folder to monkey_island\bin\openssl +5. Download and install Microsoft Visual C++ Redisutable 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): 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 monkey-windows-64.exe - monkey binary for windows 64bit -4. Download MongoDB & Extract to MonkeyIsland\bin\mongodb - http://downloads.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-latest.zip -5. Install OpenSSL - https://slproweb.com/download/Win64OpenSSL_Light-1_0_2d.exe -6. Generate SSL Certificate, run create_certificate.bat when your current working directory is MonkeyIsland -7. Copy monkey island server to MonkeyIsland\cc How to run: -1. start run_mongodb.bat -2. start run_cc.bat -3. to clear db, run clear_db.bat +1. start monkey_island\windows\run_server.bat (when your current working directory is monkey_island) +2. to clear db, run clear_db.bat ---------------- On Linux ----------------: 1. Create the following directories: