Added auto-scan subnets option

Monkey is now able to auto scan the local host subnets, removing the
need to preconfigure it to scan the network subnets (option is on by
default)
This commit is contained in:
itsikkes 2016-07-15 16:54:46 +03:00
parent 1449f1ef93
commit 3da0c1a57c
6 changed files with 93 additions and 36 deletions

View File

@ -141,12 +141,15 @@ class Configuration(object):
retry_failed_explotation = True retry_failed_explotation = True
internet_services = ["www.guardicore.com", "www.google.com"] internet_services = ["monkey.guardicore.com", "www.google.com"]
########################### ###########################
# scanners config # scanners config
########################### ###########################
# Auto detect and scan local subnets
local_network_scan = True
range_class = FixedRange range_class = FixedRange
range_size = 1 range_size = 1
range_fixed = ["", ] range_fixed = ["", ]

View File

@ -53,7 +53,8 @@ class ControlClient(object):
data=json.dumps(monkey), data=json.dumps(monkey),
headers={'content-type': 'application/json'}, headers={'content-type': 'application/json'},
verify=False, verify=False,
proxies=ControlClient.proxies) proxies=ControlClient.proxies,
timeout=20)
break break
except Exception, exc: except Exception, exc:

View File

@ -4,46 +4,74 @@ import socket
import struct import struct
import array import array
import psutil import psutil
import ipaddress
from random import randint from random import randint
__author__ = 'hoffer'
if sys.platform == "win32": if sys.platform == "win32":
import netifaces
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
else: else:
import fcntl import fcntl
def local_ips(): def get_host_subnets(only_ips=False):
result = [] """Get the list of Linux network adapters."""
try: import fcntl
is_64bits = sys.maxsize > 2**32 max_bytes = 8096
struct_size = 40 if is_64bits else 32 is_64bits = sys.maxsize > 2 ** 32
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) if is_64bits:
max_possible = 8 # initial value offset1 = 16
while True: offset2 = 40
bytes = max_possible * struct_size else:
names = array.array('B', '\0' * bytes) offset1 = 32
outbytes = struct.unpack('iL', fcntl.ioctl( offset2 = 32
s.fileno(), sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
0x8912, # SIOCGIFCONF names = array.array('B', '\0' * max_bytes)
struct.pack('iL', bytes, names.buffer_info()[0]) outbytes = struct.unpack('iL', fcntl.ioctl(
))[0] sock.fileno(),
if outbytes == bytes: 0x8912,
max_possible *= 2 struct.pack('iL', max_bytes, names.buffer_info()[0])))[0]
else: adapter_names = [names.tostring()[n_cnt:n_cnt + offset1].split('\0', 1)[0]
break for n_cnt in xrange(0, outbytes, offset2)]
namestr = names.tostring() network_adapters = []
for adapter_name in adapter_names:
ip_address = socket.inet_ntoa(fcntl.ioctl(
sock.fileno(),
0x8915,
struct.pack('256s', adapter_name))[20:24])
if ip_address.startswith('127'):
continue
subnet_mask = socket.inet_ntoa(fcntl.ioctl(
sock.fileno(),
0x891b,
struct.pack('256s', adapter_name))[20:24])
for i in range(0, outbytes, struct_size): if only_ips:
addr = socket.inet_ntoa(namestr[i+20:i+24]) network_adapters.append(ip_address)
if not addr.startswith('127'): else:
result.append(addr) network_adapters.append((ip_address, subnet_mask))
# name of interface is (namestr[i:i+16].split('\0', 1)[0]
finally: return network_adapters
return result
def local_ips():
return get_host_subnets(only_ips=True)
def get_free_tcp_port(min_range=1000, max_range=65535): def get_free_tcp_port(min_range=1000, max_range=65535):
@ -60,9 +88,25 @@ def get_free_tcp_port(min_range=1000, max_range=65535):
return None return None
def check_internet_access(services): def check_internet_access(services):
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:
return True return True
return False return False
def get_ips_from_interfaces():
res = []
ifs = get_host_subnets()
for interface in ifs:
ipint = ipaddress.ip_interface(u"%s/%s" % interface)
# 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))
return res

View File

@ -2,7 +2,7 @@ import time
import logging import logging
from . import HostScanner from . import HostScanner
from config import WormConfiguration from config import WormConfiguration
from info import local_ips from info import local_ips, get_ips_from_interfaces
from range import * from range import *
__author__ = 'itamar' __author__ = 'itamar'
@ -27,10 +27,12 @@ class NetworkScanner(object):
LOG.info("Found local IP addresses of the machine: %r", self._ip_addresses) LOG.info("Found local IP addresses of the machine: %r", self._ip_addresses)
# for fixed range, only scan once. # for fixed range, only scan once.
if WormConfiguration.range_class is FixedRange: if WormConfiguration.range_class is FixedRange:
self._ranges = [WormConfiguration.range_class('0.0.0.0')] self._ranges = [WormConfiguration.range_class(None)]
else: else:
self._ranges = [WormConfiguration.range_class(ip_address) self._ranges = [WormConfiguration.range_class(ip_address)
for ip_address in self._ip_addresses] for ip_address in self._ip_addresses]
if WormConfiguration.local_network_scan:
self._ranges += [FixedRange([ip_address for ip_address in get_ips_from_interfaces()])]
LOG.info("Base local networks to scan are: %r", self._ranges) LOG.info("Base local networks to scan are: %r", self._ranges)
def get_victim_machines(self, scan_type, max_find=5, stop_callback=None): def get_victim_machines(self, scan_type, max_find=5, stop_callback=None):

View File

@ -58,10 +58,16 @@ class RelativeRange(NetworkRange):
class FixedRange(NetworkRange): class FixedRange(NetworkRange):
def __init__(self, base_address, shuffle=True): def __init__(self, fixed_addresses=None, shuffle=True):
base_address = 0 base_address = 0
super(FixedRange, self).__init__(base_address, shuffle=shuffle) super(FixedRange, self).__init__(base_address, shuffle=shuffle)
self._fixed_addresses = self._config.range_fixed if not fixed_addresses:
self._fixed_addresses = self._config.range_fixed
else:
if type(fixed_addresses) is str:
self._fixed_addresses = [fixed_addresses]
else:
self._fixed_addresses = list(fixed_addresses)
def __repr__(self): def __repr__(self):
return "<FixedRange %s>" % (",".join(self._fixed_addresses)) return "<FixedRange %s>" % (",".join(self._fixed_addresses))

View File

@ -21,7 +21,8 @@ Windows:
python -m pip install requests python -m pip install requests
python -m pip install odict python -m pip install odict
python -m pip install paramiko python -m pip install paramiko
python -m pip install psutil python -m pip install psutil
python -m pip install netifaces
python -m pip install PyInstaller python -m pip install PyInstaller
type > C:\Python27\Lib\site-packages\zope\__init__.py type > C:\Python27\Lib\site-packages\zope\__init__.py
7. Download and extract UPX binary to [source-path]\monkey\chaos_monkey\bin\upx.exe: 7. Download and extract UPX binary to [source-path]\monkey\chaos_monkey\bin\upx.exe: