Rewrote config parsing. Avoid the horrible cast by example function and avoid possible circular import issues.

This commit is contained in:
Daniel Goldberg 2018-08-26 15:14:04 -04:00
parent 83d41df875
commit 3ce81ee78a
3 changed files with 44 additions and 56 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,50 @@ 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: if 'class' in key:
default_value = getattr(Configuration, key) # handle in cases
except AttributeError: if key == 'finger_classes':
unknown_variables[key] = value class_objects = [getattr(network_import, val) for val in value]
continue setattr(self, key, class_objects)
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:
unknown_items.append(key)
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 +136,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: