Merge pull request #177 from acepace/feature/common-folder-import-rewrite

Rewrote config parsing
This commit is contained in:
itaymmguardicore 2018-08-30 15:00:55 +03:00 committed by GitHub
commit 66876fb970
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 57 deletions

View File

@ -1,15 +1,13 @@
import os import os
import struct import json
import sys import sys
import types import types
import uuid import uuid
from abc import ABCMeta from abc import ABCMeta
from itertools import product from itertools import product
import importlib
from infection_monkey.exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, \ importlib.import_module('infection_monkey', 'network')
SambaCryExploiter, ElasticGroovyExploiter, Struts2Exploiter, ShellShockExploiter
from infection_monkey.network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, \
ElasticFinger, MSSQLFinger
__author__ = 'itamar' __author__ = 'itamar'
@ -18,57 +16,47 @@ GUID = str(uuid.getnode())
EXTERNAL_CONFIG_FILE = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'monkey.bin') EXTERNAL_CONFIG_FILE = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'monkey.bin')
def _cast_by_example(value, example):
"""
a method that casts a value to the type of the parameter given as example
"""
example_type = type(example)
if example_type is str:
return os.path.expandvars(value).encode("utf8")
elif example_type is tuple and len(example) != 0:
if value is None or value == tuple([None]):
return tuple()
return tuple([_cast_by_example(x, example[0]) for x in value])
elif example_type is list and len(example) != 0:
if value is None or value == [None]:
return []
return [_cast_by_example(x, example[0]) for x in value]
elif example_type is type(value):
return value
elif example_type is bool:
return value.lower() == 'true'
elif example_type is int:
return int(value)
elif example_type is float:
return float(value)
elif example_type in (type, ABCMeta):
return globals()[value]
else:
return None
class Configuration(object): class Configuration(object):
def from_dict(self, data):
""" def from_kv(self, formatted_data):
Get a dict of config variables, set known variables as attributes on self. # now we won't work at <2.7 for sure
Return dict of unknown variables encountered. network_import = importlib.import_module('infection_monkey.network')
""" exploit_import = importlib.import_module('infection_monkey.exploit')
unknown_variables = {}
for key, value in data.items(): unknown_items = []
for key, value in formatted_data.items():
if key.startswith('_'): if key.startswith('_'):
continue continue
if key in ["name", "id", "current_server"]: if key in ["name", "id", "current_server"]:
continue continue
if self._depth_from_commandline and key == "depth": if self._depth_from_commandline and key == "depth":
continue continue
try: # handle in cases
default_value = getattr(Configuration, key) if key == 'finger_classes':
except AttributeError: class_objects = [getattr(network_import, val) for val in value]
unknown_variables[key] = value setattr(self, key, class_objects)
continue elif key == 'scanner_class':
scanner_object = getattr(network_import, value)
setattr(self, key, scanner_object)
elif key == 'exploiter_classes':
class_objects = [getattr(exploit_import, val) for val in value]
setattr(self, key, class_objects)
else:
if hasattr(self, key):
setattr(self, key, value)
else:
unknown_items.append(key)
return unknown_items
setattr(self, key, _cast_by_example(value, default_value)) def from_json(self, json_data):
return unknown_variables """
Gets a json data object, parses it and applies it to the configuration
:param json_data:
:return:
"""
formatted_data = json.loads(json_data)
result = self.from_kv(formatted_data)
return result
def as_dict(self): def as_dict(self):
result = {} result = {}
@ -145,12 +133,9 @@ class Configuration(object):
# how many scan iterations to perform on each run # how many scan iterations to perform on each run
max_iterations = 1 max_iterations = 1
scanner_class = TcpScanner scanner_class = None
finger_classes = [SMBFinger, SSHFinger, PingScanner, HTTPFinger, MySQLFinger, ElasticFinger, MSSQLFinger] finger_classes = []
exploiter_classes = [SmbExploiter, WmiExploiter, # Windows exploits exploiter_classes = []
SSHExploiter, ShellShockExploiter, SambaCryExploiter, # Linux
ElasticGroovyExploiter, Struts2Exploiter # multi
]
# how many victims to look for in a single scan iteration # how many victims to look for in a single scan iteration
victims_max_find = 30 victims_max_find = 30

View File

@ -160,7 +160,7 @@ class ControlClient(object):
return return
try: try:
unknown_variables = WormConfiguration.from_dict(reply.json().get('config')) unknown_variables = WormConfiguration.from_kv(reply.json().get('config'))
LOG.info("New configuration was loaded from server: %r" % (WormConfiguration.as_dict(),)) LOG.info("New configuration was loaded from server: %r" % (WormConfiguration.as_dict(),))
except Exception as exc: except Exception as exc:
# we don't continue with default conf here because it might be dangerous # we don't continue with default conf here because it might be dangerous

View File

@ -60,7 +60,7 @@ def main():
try: try:
with open(config_file) as config_fo: with open(config_file) as config_fo:
json_dict = json.load(config_fo) json_dict = json.load(config_fo)
WormConfiguration.from_dict(json_dict) WormConfiguration.from_kv(json_dict)
except ValueError as e: except ValueError as e:
print("Error loading config: %s, using default" % (e,)) print("Error loading config: %s, using default" % (e,))
else: else:

View File

@ -39,7 +39,15 @@ class NetworkScanner(object):
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):
assert issubclass(scan_type, HostScanner) """
Finds machines according to the ranges specified in the object
:param scan_type: A hostscanner class, will be instanced and used to scan for new machines
:param max_find: Max number of victims to find regardless of ranges
:param stop_callback: A callback to check at any point if we should stop scanning
:return: yields a sequence of VictimHost instances
"""
if not scan_type:
return
scanner = scan_type() scanner = scan_type()
victims_count = 0 victims_count = 0