Merge remote-tracking branch 'origin/develop' into feature/refactor-monkey-island

This commit is contained in:
Itay Mizeretz 2017-09-24 18:03:17 +03:00
commit 11fa27a2f7
11 changed files with 113 additions and 101 deletions

View File

@ -1,3 +1,9 @@
#!/usr/bin/env bash #!/usr/bin/env bash
gcc -c -Wall -Werror -fpic sc_monkey_runner.c gcc -c -Wall -Werror -fpic -m64 sc_monkey_runner.c
gcc -shared -o sc_monkey_runner.so sc_monkey_runner.o gcc -shared -m64 -o sc_monkey_runner_64.so sc_monkey_runner.o
rm sc_monkey_runner.o
strip sc_monkey_runner_64.so
gcc -c -Wall -Werror -fpic -m32 sc_monkey_runner.c
gcc -shared -m32 -o sc_monkey_runner_32.so sc_monkey_runner.o
rm sc_monkey_runner.o
strip sc_monkey_runner_32.so

View File

@ -1,81 +1,56 @@
import os import os
import sys import sys
import array
import socket import socket
import struct import struct
import psutil import psutil
import ipaddress import ipaddress
import itertools
import netifaces
from subprocess import check_output from subprocess import check_output
from random import randint from random import randint
if sys.platform == "win32":
import netifaces
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)
]
# 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(): def local_ips():
local_hostname = socket.gethostname() local_hostname = socket.gethostname()
return socket.gethostbyname_ex(local_hostname)[2] 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_routes(): def get_routes():
raise NotImplementedError() raise NotImplementedError()
else: else:
from fcntl import ioctl 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))
return network_adapters
def local_ips(): 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 def get_routes(): # based on scapy implementation for route parsing
LOOPBACK_NAME = "lo" LOOPBACK_NAME = "lo"
@ -141,6 +116,11 @@ def get_free_tcp_port(min_range=1000, max_range=65535):
def check_internet_access(services): 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" ping_str = "-n 1" if sys.platform.startswith("win") else "-c 1"
for host in services: for host in services:
if os.system("ping " + ping_str + " " + host) == 0: if os.system("ping " + ping_str + " " + host) == 0:
@ -149,19 +129,24 @@ def check_internet_access(services):
def get_ips_from_interfaces(): 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 = [] res = []
ifs = get_host_subnets() ifs = get_host_subnets()
for interface in ifs: for net_interface in ifs:
ipint = ipaddress.ip_interface(u"%s/%s" % interface) 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 # limit subnet scans to class C only
if ipint.network.num_addresses > 255: if ip_interface.network.num_addresses > 255:
ipint = ipaddress.ip_interface(u"%s/24" % interface[0]) ip_interface = ipaddress.ip_interface(u"%s/24" % net_interface['addr'])
for addr in ipint.network.hosts(): addrs = [str(addr) for addr in ip_interface.network.hosts() if addr != host_addr]
if str(addr) == interface[0]: res.extend(addrs)
continue
res.append(str(addr))
return res return res
if sys.platform == "win32": if sys.platform == "win32":
def get_ip_for_connection(target_ip): def get_ip_for_connection(target_ip):
return None return None
@ -171,7 +156,7 @@ else:
query_str = 'ip route get %s' % target_ip query_str = 'ip route get %s' % target_ip
resp = check_output(query_str.split()) resp = check_output(query_str.split())
substr = resp.split() substr = resp.split()
src = substr[substr.index('src')+1] src = substr[substr.index('src') + 1]
return src return src
except Exception: except Exception:
return None return None

View File

@ -2,6 +2,7 @@ import sys
import socket import socket
import psutil import psutil
from enum import IntEnum from enum import IntEnum
from network.info import get_host_subnets, local_ips
__author__ = 'uri' __author__ = 'uri'
@ -45,9 +46,19 @@ class InfoCollector(object):
self.info = {} self.info = {}
def get_hostname(self): def get_hostname(self):
self.info['hostname'] = socket.gethostname() """
Adds the fully qualified computer hostname to the system information.
:return: Nothing
"""
self.info['hostname'] = socket.getfqdn()
def get_process_list(self): 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 = {} processes = {}
for process in psutil.process_iter(): for process in psutil.process_iter():
try: try:
@ -69,3 +80,12 @@ class InfoCollector(object):
} }
pass pass
self.info['process_list'] = processes self.info['process_list'] = processes
def get_network_info(self):
"""
Adds network information from the host to the system information.
Currently updates with a list of networks accessible from host,
containing host ip and the subnet range.
:return: None
"""
self.info['network_info'] = {'networks': get_host_subnets()}

View File

@ -14,4 +14,5 @@ class LinuxInfoCollector(InfoCollector):
def get_info(self): def get_info(self):
self.get_hostname() self.get_hostname()
self.get_process_list() self.get_process_list()
self.get_network_info()
return self.info return self.info

View File

@ -14,6 +14,7 @@ class WindowsInfoCollector(InfoCollector):
def get_info(self): def get_info(self):
self.get_hostname() self.get_hostname()
self.get_process_list() self.get_process_list()
self.get_network_info()
mimikatz_collector = MimikatzCollector() mimikatz_collector = MimikatzCollector()
self.info["credentials"] = mimikatz_collector.get_logon_info() self.info["credentials"] = mimikatz_collector.get_logon_info()
return self.info return self.info

View File

@ -1,35 +1,34 @@
How to set C&C server: How to set C&C server:
---------------- On Windows ----------------: ---------------- On Windows ----------------:
1. Install python 2.7 1. Create bin folder
https://www.python.org/download/releases/2.7 1.1. create folder "bin" under monkey_island
2. Download & Run get-pip.py 2. Place portable version of Python 2.7
https://bootstrap.pypa.io/get-pip.py 2.1. Download and install from: https://www.python.org/download/releases/2.7/
3. Run: 2.2. Download & Run get-pip.py from: https://bootstrap.pypa.io/get-pip.py
setx path "%path%;C:\Python27\;C:\Python27\Scripts" 2.3. Install required python libraries using "python -m pip install -r monkey_island\requirements.txt"
python -m pip install flask 2.4. Copy Contents from Installation path (Usually C:\Python27) to monkey_island\bin\Python27
python -m pip install Flask-Pymongo 2.5. Copy Python27.dll from System32 folder (Usually C:\Windows\System32) to monkey_island\bin\Python27
python -m pip install Flask-Restful 2.6. (Optional) You may uninstall Python27 if you like.
python -m pip install python-dateutil 3. Place portable version of mongodb
mkdir MonkeyIsland\bin 3.1. Download from: http://downloads.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-latest.zip
mkdir MonkeyIsland\db 3.2. Extract contents from bin folder to monkey_island\bin\mongodb.
mkdir MonkeyIsland\cc\binaries 4. Place portable version of OpenSSL
4. Put monkey binaries in MonkeyIsland\cc\binaries: 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-64 - monkey binary for linux 64bit
monkey-linux-32 - monkey binary for linux 32bit monkey-linux-32 - monkey binary for linux 32bit
monkey-windows-32.exe - monkey binary for windows 32bit monkey-windows-32.exe - monkey binary for windows 32bit
monkey-windows-64.exe - monkey binary for windows 64bit 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: How to run:
1. start run_mongodb.bat 1. start monkey_island\windows\run_server.bat (when your current working directory is monkey_island)
2. start run_cc.bat 2. to clear db, run clear_db.bat
3. to clear db, run clear_db.bat
---------------- On Linux ----------------: ---------------- On Linux ----------------:
1. Create the following directories: 1. Create the following directories:

View File

@ -2,4 +2,3 @@
@pause @pause
@rmdir /s /q db @rmdir /s /q db
@mkdir db @mkdir db
@pause

View File

@ -1,4 +1,3 @@
C:\OpenSSL-Win64\bin\openssl.exe genrsa -out cc\server.key 1024 bin\openssl\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" 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"
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 x509 -req -days 366 -in cc\server.csr -signkey cc\server.key -out cc\server.crt
pause

View File

@ -1,4 +1,4 @@
@title C^&C Server @title C^&C Server
@cd cc @pushd cc
@main.py @..\bin\Python27\python main.py
@pause @popd

View File

@ -1,3 +1,2 @@
@title MongoDB @title MongoDB
@bin\mongodb\mongod.exe --dbpath db @bin\mongodb\mongod.exe --dbpath db
@pause

View File

@ -0,0 +1,3 @@
if not exist db mkdir db
start windows\run_mongodb.bat
start windows\run_cc.bat