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
internet_services = ["www.guardicore.com", "www.google.com"]
internet_services = ["monkey.guardicore.com", "www.google.com"]
###########################
# scanners config
###########################
# Auto detect and scan local subnets
local_network_scan = True
range_class = FixedRange
range_size = 1
range_fixed = ["", ]

View File

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

View File

@ -4,46 +4,74 @@ import socket
import struct
import array
import psutil
import ipaddress
from random import randint
__author__ = 'hoffer'
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
else:
import fcntl
def local_ips():
result = []
try:
is_64bits = sys.maxsize > 2**32
struct_size = 40 if is_64bits else 32
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
max_possible = 8 # initial value
while True:
bytes = max_possible * struct_size
names = array.array('B', '\0' * bytes)
outbytes = struct.unpack('iL', fcntl.ioctl(
s.fileno(),
0x8912, # SIOCGIFCONF
struct.pack('iL', bytes, names.buffer_info()[0])
))[0]
if outbytes == bytes:
max_possible *= 2
else:
break
namestr = names.tostring()
def get_host_subnets(only_ips=False):
"""Get the list of Linux network adapters."""
import fcntl
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', fcntl.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(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):
addr = socket.inet_ntoa(namestr[i+20:i+24])
if not addr.startswith('127'):
result.append(addr)
# name of interface is (namestr[i:i+16].split('\0', 1)[0]
finally:
return result
if only_ips:
network_adapters.append(ip_address)
else:
network_adapters.append((ip_address, subnet_mask))
return network_adapters
def local_ips():
return get_host_subnets(only_ips=True)
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
def check_internet_access(services):
ping_str = "-n 1" if sys.platform.startswith("win") else "-c 1"
for host in services:
if os.system("ping " + ping_str + " " + host) == 0:
return True
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
from . import HostScanner
from config import WormConfiguration
from info import local_ips
from info import local_ips, get_ips_from_interfaces
from range import *
__author__ = 'itamar'
@ -27,10 +27,12 @@ class NetworkScanner(object):
LOG.info("Found local IP addresses of the machine: %r", self._ip_addresses)
# for fixed range, only scan once.
if WormConfiguration.range_class is FixedRange:
self._ranges = [WormConfiguration.range_class('0.0.0.0')]
self._ranges = [WormConfiguration.range_class(None)]
else:
self._ranges = [WormConfiguration.range_class(ip_address)
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)
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):
def __init__(self, base_address, shuffle=True):
def __init__(self, fixed_addresses=None, shuffle=True):
base_address = 0
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):
return "<FixedRange %s>" % (",".join(self._fixed_addresses))

View File

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