forked from p15670423/monkey
Merge pull request #94 from guardicore/feature/support-subnet-in-config
Feature/support subnet in config
This commit is contained in:
commit
768d1448a2
|
@ -0,0 +1 @@
|
||||||
|
__author__ = 'itay.mizeretz'
|
|
@ -0,0 +1 @@
|
||||||
|
__author__ = 'itay.mizeretz'
|
|
@ -0,0 +1,122 @@
|
||||||
|
import random
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
|
import ipaddress
|
||||||
|
|
||||||
|
__author__ = 'itamar'
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkRange(object):
|
||||||
|
__metaclass__ = ABCMeta
|
||||||
|
|
||||||
|
def __init__(self, shuffle=True):
|
||||||
|
self._shuffle = shuffle
|
||||||
|
|
||||||
|
def get_range(self):
|
||||||
|
"""
|
||||||
|
:return: Returns a sequence of IPs in an internal format (might be numbers)
|
||||||
|
"""
|
||||||
|
return self._get_range()
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
"""
|
||||||
|
Iterator of ip addresses (strings) from the current range.
|
||||||
|
Use get_range if you want it in one go.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
base_range = self.get_range()
|
||||||
|
if self._shuffle:
|
||||||
|
random.shuffle(base_range)
|
||||||
|
|
||||||
|
for x in base_range:
|
||||||
|
yield self._number_to_ip(x)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def is_in_range(self, ip_address):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _get_range(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_range_obj(address_str):
|
||||||
|
address_str = address_str.strip()
|
||||||
|
if not address_str: # Empty string
|
||||||
|
return None
|
||||||
|
if -1 != address_str.find('-'):
|
||||||
|
return IpRange(ip_range=address_str)
|
||||||
|
if -1 != address_str.find('/'):
|
||||||
|
return CidrRange(cidr_range=address_str)
|
||||||
|
return SingleIpRange(ip_address=address_str)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _ip_to_number(address):
|
||||||
|
return struct.unpack(">L", socket.inet_aton(address))[0]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _number_to_ip(num):
|
||||||
|
return socket.inet_ntoa(struct.pack(">L", num))
|
||||||
|
|
||||||
|
|
||||||
|
class CidrRange(NetworkRange):
|
||||||
|
def __init__(self, cidr_range, shuffle=True):
|
||||||
|
super(CidrRange, self).__init__(shuffle=shuffle)
|
||||||
|
self._cidr_range = cidr_range.strip()
|
||||||
|
self._ip_network = ipaddress.ip_network(unicode(self._cidr_range), strict=False)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<CidrRange %s>" % (self._cidr_range,)
|
||||||
|
|
||||||
|
def is_in_range(self, ip_address):
|
||||||
|
return ipaddress.ip_address(ip_address) in self._ip_network
|
||||||
|
|
||||||
|
def _get_range(self):
|
||||||
|
return [CidrRange._ip_to_number(str(x)) for x in self._ip_network if x != self._ip_network.broadcast_address]
|
||||||
|
|
||||||
|
|
||||||
|
class IpRange(NetworkRange):
|
||||||
|
def __init__(self, ip_range=None, lower_end_ip=None, higher_end_ip=None, shuffle=True):
|
||||||
|
super(IpRange, self).__init__(shuffle=shuffle)
|
||||||
|
if ip_range is not None:
|
||||||
|
addresses = ip_range.split('-')
|
||||||
|
if len(addresses) != 2:
|
||||||
|
raise ValueError('Illegal IP range format: %s. Format is 192.168.0.5-192.168.0.20' % ip_range)
|
||||||
|
self._lower_end_ip, self._higher_end_ip = [x.strip() for x in addresses]
|
||||||
|
elif (lower_end_ip is not None) and (higher_end_ip is not None):
|
||||||
|
self._lower_end_ip = lower_end_ip.strip()
|
||||||
|
self._higher_end_ip = higher_end_ip.strip()
|
||||||
|
else:
|
||||||
|
raise ValueError('Illegal IP range: %s' % ip_range)
|
||||||
|
|
||||||
|
self._lower_end_ip_num = self._ip_to_number(self._lower_end_ip)
|
||||||
|
self._higher_end_ip_num = self._ip_to_number(self._higher_end_ip)
|
||||||
|
if self._higher_end_ip_num < self._lower_end_ip_num:
|
||||||
|
raise ValueError(
|
||||||
|
'Higher end IP %s is smaller than lower end IP %s' % (self._lower_end_ip, self._higher_end_ip))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<IpRange %s-%s>" % (self._lower_end_ip, self._higher_end_ip)
|
||||||
|
|
||||||
|
def is_in_range(self, ip_address):
|
||||||
|
return self._lower_end_ip_num <= self._ip_to_number(ip_address) <= self._higher_end_ip_num
|
||||||
|
|
||||||
|
def _get_range(self):
|
||||||
|
return range(self._lower_end_ip_num, self._higher_end_ip_num + 1)
|
||||||
|
|
||||||
|
|
||||||
|
class SingleIpRange(NetworkRange):
|
||||||
|
def __init__(self, ip_address, shuffle=True):
|
||||||
|
super(SingleIpRange, self).__init__(shuffle=shuffle)
|
||||||
|
self._ip_address = ip_address
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<SingleIpRange %s>" % (self._ip_address,)
|
||||||
|
|
||||||
|
def is_in_range(self, ip_address):
|
||||||
|
return self._ip_address == ip_address
|
||||||
|
|
||||||
|
def _get_range(self):
|
||||||
|
return [SingleIpRange._ip_to_number(self._ip_address)]
|
|
@ -8,7 +8,6 @@ from itertools import product
|
||||||
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \
|
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \
|
||||||
SambaCryExploiter, ElasticGroovyExploiter
|
SambaCryExploiter, ElasticGroovyExploiter
|
||||||
from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger
|
from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger
|
||||||
from network.range import FixedRange
|
|
||||||
|
|
||||||
__author__ = 'itamar'
|
__author__ = 'itamar'
|
||||||
|
|
||||||
|
@ -183,8 +182,7 @@ class Configuration(object):
|
||||||
# Auto detect and scan local subnets
|
# Auto detect and scan local subnets
|
||||||
local_network_scan = True
|
local_network_scan = True
|
||||||
|
|
||||||
range_class = FixedRange
|
subnet_scan_list = ['', ]
|
||||||
range_fixed = ['', ]
|
|
||||||
|
|
||||||
blocked_ips = ['', ]
|
blocked_ips = ['', ]
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
"www.google.com"
|
"www.google.com"
|
||||||
],
|
],
|
||||||
"keep_tunnel_open_time": 60,
|
"keep_tunnel_open_time": 60,
|
||||||
"range_class": "RelativeRange",
|
"subnet_scan_list": [
|
||||||
"range_fixed": [
|
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"blocked_ips": [""],
|
"blocked_ips": [""],
|
||||||
|
|
|
@ -4,7 +4,7 @@ block_cipher = None
|
||||||
|
|
||||||
|
|
||||||
a = Analysis(['main.py'],
|
a = Analysis(['main.py'],
|
||||||
pathex=['.'],
|
pathex=['.', '..'],
|
||||||
binaries=None,
|
binaries=None,
|
||||||
datas=None,
|
datas=None,
|
||||||
hiddenimports=['_cffi_backend'],
|
hiddenimports=['_cffi_backend'],
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
a = Analysis(['main.py'],
|
a = Analysis(['main.py'],
|
||||||
pathex=['.'],
|
pathex=['.', '..'],
|
||||||
hiddenimports=['_cffi_backend', 'queue'],
|
hiddenimports=['_cffi_backend', 'queue'],
|
||||||
hookspath=None,
|
hookspath=None,
|
||||||
runtime_hooks=None)
|
runtime_hooks=None)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import itertools
|
||||||
import netifaces
|
import netifaces
|
||||||
from subprocess import check_output
|
from subprocess import check_output
|
||||||
from random import randint
|
from random import randint
|
||||||
|
from common.network.network_range import CidrRange
|
||||||
|
|
||||||
|
|
||||||
def get_host_subnets():
|
def get_host_subnets():
|
||||||
|
@ -129,7 +130,7 @@ def check_internet_access(services):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_ips_from_interfaces():
|
def get_interfaces_ranges():
|
||||||
"""
|
"""
|
||||||
Returns a list of IPs accessible in the host in each network interface, in the subnet.
|
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
|
Limits to a single class C if the network is larger
|
||||||
|
@ -138,15 +139,11 @@ def get_ips_from_interfaces():
|
||||||
res = []
|
res = []
|
||||||
ifs = get_host_subnets()
|
ifs = get_host_subnets()
|
||||||
for net_interface in ifs:
|
for net_interface in ifs:
|
||||||
address_str = unicode(net_interface['addr'])
|
address_str = net_interface['addr']
|
||||||
netmask_str = unicode(net_interface['netmask'])
|
netmask_str = net_interface['netmask']
|
||||||
host_address = ipaddress.ip_address(address_str)
|
|
||||||
ip_interface = ipaddress.ip_interface(u"%s/%s" % (address_str, netmask_str))
|
ip_interface = ipaddress.ip_interface(u"%s/%s" % (address_str, netmask_str))
|
||||||
# limit subnet scans to class C only
|
# limit subnet scans to class C only
|
||||||
if ip_interface.network.num_addresses > 255:
|
res.append(CidrRange(cidr_range="%s/%s" % (address_str, netmask_str)))
|
||||||
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
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,9 @@ import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from config import WormConfiguration
|
from config import WormConfiguration
|
||||||
from info import local_ips, get_ips_from_interfaces
|
from info import local_ips, get_interfaces_ranges
|
||||||
from range import *
|
from common.network.network_range import *
|
||||||
|
from model import VictimHost
|
||||||
from . import HostScanner
|
from . import HostScanner
|
||||||
|
|
||||||
__author__ = 'itamar'
|
__author__ = 'itamar'
|
||||||
|
@ -20,9 +21,8 @@ class NetworkScanner(object):
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
"""
|
"""
|
||||||
Set up scanning based on configuration
|
Set up scanning.
|
||||||
FixedRange -> Reads from range_fixed field in configuration
|
based on configuration: scans local network and/or scans fixed list of IPs/subnets.
|
||||||
otherwise, takes a range from every IP address the current host has.
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
# get local ip addresses
|
# get local ip addresses
|
||||||
|
@ -33,13 +33,9 @@ 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:
|
self._ranges = [NetworkRange.get_range_obj(address_str=x) for x in WormConfiguration.subnet_scan_list]
|
||||||
self._ranges = [WormConfiguration.range_class(fixed_addresses=WormConfiguration.range_fixed)]
|
|
||||||
else:
|
|
||||||
self._ranges = [WormConfiguration.range_class(ip_address)
|
|
||||||
for ip_address in self._ip_addresses]
|
|
||||||
if WormConfiguration.local_network_scan:
|
if WormConfiguration.local_network_scan:
|
||||||
self._ranges += [FixedRange([ip_address for ip_address in get_ips_from_interfaces()])]
|
self._ranges += get_interfaces_ranges()
|
||||||
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):
|
||||||
|
@ -50,7 +46,8 @@ class NetworkScanner(object):
|
||||||
|
|
||||||
for net_range in self._ranges:
|
for net_range in self._ranges:
|
||||||
LOG.debug("Scanning for potential victims in the network %r", net_range)
|
LOG.debug("Scanning for potential victims in the network %r", net_range)
|
||||||
for victim in net_range:
|
for ip_addr in net_range:
|
||||||
|
victim = VictimHost(ip_addr)
|
||||||
if stop_callback and stop_callback():
|
if stop_callback and stop_callback():
|
||||||
LOG.debug("Got stop signal")
|
LOG.debug("Got stop signal")
|
||||||
break
|
break
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
import random
|
|
||||||
import socket
|
|
||||||
import struct
|
|
||||||
from abc import ABCMeta, abstractmethod
|
|
||||||
|
|
||||||
from model.host import VictimHost
|
|
||||||
|
|
||||||
__author__ = 'itamar'
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkRange(object):
|
|
||||||
__metaclass__ = ABCMeta
|
|
||||||
|
|
||||||
def __init__(self, base_address, shuffle=True):
|
|
||||||
self._base_address = base_address
|
|
||||||
self._shuffle = shuffle
|
|
||||||
self._config = __import__('config').WormConfiguration
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def _get_range(self):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
base_range = self._get_range()
|
|
||||||
if self._shuffle:
|
|
||||||
random.shuffle(base_range)
|
|
||||||
|
|
||||||
for x in base_range:
|
|
||||||
yield VictimHost(socket.inet_ntoa(struct.pack(">L", self._base_address + x)))
|
|
||||||
|
|
||||||
|
|
||||||
class ClassCRange(NetworkRange):
|
|
||||||
def __init__(self, base_address, shuffle=True):
|
|
||||||
base_address = struct.unpack(">L", socket.inet_aton(base_address))[0] & 0xFFFFFF00
|
|
||||||
super(ClassCRange, self).__init__(base_address, shuffle=shuffle)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<ClassCRange %s-%s>" % (socket.inet_ntoa(struct.pack(">L", self._base_address + 1)),
|
|
||||||
socket.inet_ntoa(struct.pack(">L", self._base_address + 254)))
|
|
||||||
|
|
||||||
def _get_range(self):
|
|
||||||
return range(1, 254)
|
|
||||||
|
|
||||||
|
|
||||||
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 = 1
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<RelativeRange %s-%s>" % (socket.inet_ntoa(struct.pack(">L", self._base_address - self._size)),
|
|
||||||
socket.inet_ntoa(struct.pack(">L", self._base_address + self._size)))
|
|
||||||
|
|
||||||
def _get_range(self):
|
|
||||||
lower_end = -(self._size / 2)
|
|
||||||
higher_end = lower_end + self._size
|
|
||||||
return range(lower_end, higher_end + 1)
|
|
||||||
|
|
||||||
|
|
||||||
class FixedRange(NetworkRange):
|
|
||||||
def __init__(self, fixed_addresses=None, shuffle=True):
|
|
||||||
base_address = 0
|
|
||||||
super(FixedRange, self).__init__(base_address, shuffle=shuffle)
|
|
||||||
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))
|
|
||||||
|
|
||||||
def _get_range(self):
|
|
||||||
address_range = []
|
|
||||||
for address in self._fixed_addresses:
|
|
||||||
if not address: # Empty string
|
|
||||||
continue
|
|
||||||
address_range.append(struct.unpack(">L", socket.inet_aton(address.strip()))[0])
|
|
||||||
return address_range
|
|
|
@ -202,32 +202,9 @@ SCHEMA = {
|
||||||
"Amount of hops allowed for the monkey to spread from the island. "
|
"Amount of hops allowed for the monkey to spread from the island. "
|
||||||
+ WARNING_SIGN
|
+ WARNING_SIGN
|
||||||
+ " Note that setting this value too high may result in the monkey propagating too far"
|
+ " Note that setting this value too high may result in the monkey propagating too far"
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"network_range": {
|
|
||||||
"title": "Network range",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"range_class": {
|
|
||||||
"title": "Range class",
|
|
||||||
"type": "string",
|
|
||||||
"default": "FixedRange",
|
|
||||||
"enum": [
|
|
||||||
"FixedRange",
|
|
||||||
"ClassCRange"
|
|
||||||
],
|
|
||||||
"enumNames": [
|
|
||||||
"Fixed 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."
|
|
||||||
" Class C Range will scan machines in the Class C network the monkey's on."
|
|
||||||
},
|
},
|
||||||
"range_fixed": {
|
"subnet_scan_list": {
|
||||||
"title": "Fixed range IP list",
|
"title": "Scan IP/subnet list",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"uniqueItems": True,
|
"uniqueItems": True,
|
||||||
"items": {
|
"items": {
|
||||||
|
@ -236,8 +213,8 @@ SCHEMA = {
|
||||||
"default": [
|
"default": [
|
||||||
],
|
],
|
||||||
"description":
|
"description":
|
||||||
"List of IPs to include when using FixedRange"
|
"List of IPs/subnets the monkey should scan."
|
||||||
" (Only relevant for Fixed Range)"
|
" Examples: \"192.168.0.1\", \"192.168.0.5-192.168.0.20\", \"192.168.0.5/24\""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -349,10 +349,7 @@ class ReportService:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_config_ips():
|
def get_config_ips():
|
||||||
if ConfigService.get_config_value(['basic_network', 'network_range', 'range_class'], True,
|
return ConfigService.get_config_value(['basic_network', 'general', 'subnet_scan_list'], True, True)
|
||||||
True) != 'FixedRange':
|
|
||||||
return []
|
|
||||||
return ConfigService.get_config_value(['basic_network', 'network_range', 'range_fixed'], True, True)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_config_scan():
|
def get_config_scan():
|
||||||
|
|
Loading…
Reference in New Issue