From 3d9780d57edbc953db103b6bbc5fb157c5d37536 Mon Sep 17 00:00:00 2001 From: Thorsten Sick Date: Tue, 4 May 2021 11:09:25 +0200 Subject: [PATCH] Added plugin manager --- app/machinecontrol.py | 148 +++++++++++------------------------- app/pluginmanager.py | 74 ++++++++++++++++++ plugin_manager.py | 34 +++++++++ plugins/base/plugin_base.py | 17 +++++ 4 files changed, 171 insertions(+), 102 deletions(-) create mode 100644 app/pluginmanager.py create mode 100644 plugin_manager.py diff --git a/app/machinecontrol.py b/app/machinecontrol.py index c50b08c..2d63be8 100644 --- a/app/machinecontrol.py +++ b/app/machinecontrol.py @@ -4,13 +4,12 @@ import os import time -from glob import glob import requests -import straight.plugin from app.config import MachineConfig, ExperimentConfig from app.exceptions import ServerError, ConfigurationError +from app.pluginmanager import PluginManager from app.calderacontrol import CalderaControl from app.interface_sfx import CommandlineColors from plugins.base.kali import KaliPlugin @@ -37,6 +36,8 @@ class Machine(): else: self.config = MachineConfig(config) + self.plugin_manager = PluginManager() + # TODO: Read config from plugin if self.config.vmcontroller() == "vagrant": self.__parse_vagrant_config__() @@ -155,59 +156,31 @@ class Machine(): @returns: The output of the cmdline attacking tool """ - def get_handlers(plugin) -> [KaliPlugin]: - return plugin.produce() - - base = "plugins/**/*.py" - - plugin_dirs = set() - for a_glob in glob(base, recursive=True): - plugin_dirs.add(os.path.dirname(a_glob)) - - for a_directory in plugin_dirs: - plugins = straight.plugin.load(a_directory, subclasses=KaliPlugin) - - handlers = get_handlers(plugins) + for plugin in self.plugin_manager.get_plugins(KaliPlugin, [attack]): + name = plugin.get_name() - for plugin in handlers: - name = plugin.get_name() - if name == attack: - print(f"{CommandlineColors.OKBLUE}Running Kali plugin {name}{CommandlineColors.ENDC}") - syscon = {"abs_machinepath_internal": self.abs_machinepath_internal, - "abs_machinepath_external": self.abs_machinepath_external} - plugin.set_sysconf(syscon) - plugin.set_machine_plugin(self.vm_manager) - plugin.__set_logger__(self.attack_logger) - plugin.__execute__([target], config.kali_conf(name)) + print(f"{CommandlineColors.OKBLUE}Running Kali plugin {name}{CommandlineColors.ENDC}") + syscon = {"abs_machinepath_internal": self.abs_machinepath_internal, + "abs_machinepath_external": self.abs_machinepath_external} + plugin.set_sysconf(syscon) + plugin.set_machine_plugin(self.vm_manager) + plugin.__set_logger__(self.attack_logger) + plugin.__execute__([target], config.kali_conf(name)) def load_machine_plugin(self): """ Loads the matching machine plugin """ - def get_handlers(a_plugin) -> [MachineryPlugin]: - return a_plugin.produce() + for plugin in self.plugin_manager.get_plugins(MachineryPlugin, [self.config.vmcontroller()]): - base = "plugins/**/*.py" - - plugin_dirs = set() - for a_glob in glob(base, recursive=True): - plugin_dirs.add(os.path.dirname(a_glob)) - - for a_dir in plugin_dirs: - plugins = straight.plugin.load(a_dir, subclasses=MachineryPlugin) - - handlers = get_handlers(plugins) - - for plugin in handlers: - name = plugin.get_name() - if name == self.config.vmcontroller(): - print(f"{CommandlineColors.OKBLUE}Installing sensor: {name}{CommandlineColors.ENDC}") + name = plugin.get_name() + print(f"{CommandlineColors.OKBLUE}Installing sensor: {name}{CommandlineColors.ENDC}") - syscon = {"abs_machinepath_internal": self.abs_machinepath_internal, - "abs_machinepath_external": self.abs_machinepath_external} - plugin.set_sysconf(syscon) - plugin.__call_process_config__(self.config) - self.vm_manager = plugin - break + syscon = {"abs_machinepath_internal": self.abs_machinepath_internal, + "abs_machinepath_external": self.abs_machinepath_external} + plugin.set_sysconf(syscon) + plugin.__call_process_config__(self.config) + self.vm_manager = plugin + break def prime_sensors(self): """ Prime sensors from plugins (hard core installs that could require a reboot) @@ -216,35 +189,22 @@ class Machine(): """ - def get_handlers(a_plugin) -> [SensorPlugin]: - return a_plugin.produce() - - base = "plugins/**/*.py" reboot = False - plugin_dirs = set() - for a_glob in glob(base, recursive=True): - plugin_dirs.add(os.path.dirname(a_glob)) - - for a_dir in plugin_dirs: - plugins = straight.plugin.load(a_dir, subclasses=SensorPlugin) - - handlers = get_handlers(plugins) - - for plugin in handlers: - name = plugin.get_name() - if name in self.config.sensors(): - print(f"{CommandlineColors.OKBLUE}Priming sensor: {name}{CommandlineColors.ENDC}") - syscon = {"abs_machinepath_internal": self.abs_machinepath_internal, - "abs_machinepath_external": self.abs_machinepath_external, - "sensor_specific": self.config.raw_config.get(name, {}) - } - plugin.set_sysconf(syscon) - plugin.set_machine_plugin(self.vm_manager) - plugin.setup() - reboot |= plugin.prime() - self.sensors.append(plugin) - print(f"{CommandlineColors.OKGREEN}Primed sensor: {name}{CommandlineColors.ENDC}") + for plugin in self.plugin_manager.get_plugins(SensorPlugin, self.config.sensors()): + name = plugin.get_name() + # if name in self.config.sensors(): + print(f"{CommandlineColors.OKBLUE}Priming sensor: {name}{CommandlineColors.ENDC}") + syscon = {"abs_machinepath_internal": self.abs_machinepath_internal, + "abs_machinepath_external": self.abs_machinepath_external, + "sensor_specific": self.config.raw_config.get(name, {}) + } + plugin.set_sysconf(syscon) + plugin.set_machine_plugin(self.vm_manager) + plugin.setup() + reboot |= plugin.prime() + self.sensors.append(plugin) + print(f"{CommandlineColors.OKGREEN}Primed sensor: {name}{CommandlineColors.ENDC}") return reboot def install_sensors(self): @@ -266,7 +226,6 @@ class Machine(): plugin.setup() plugin.install() print(f"{CommandlineColors.OKGREEN}Installed sensor: {name}{CommandlineColors.ENDC}") - # self.sensors.append(plugin) def get_sensors(self) -> [SensorPlugin]: """ Returns a list of running sensors """ @@ -323,32 +282,17 @@ class Machine(): """ - def get_handlers(a_plugin) -> [SensorPlugin]: - return a_plugin.produce() - - base = "plugins/**/*.py" - - plugin_dirs = set() - for a_glob in glob(base, recursive=True): - plugin_dirs.add(os.path.dirname(a_glob)) - - for a_dir in plugin_dirs: - plugins = straight.plugin.load(a_dir, subclasses=VulnerabilityPlugin) - - handlers = get_handlers(plugins) - - for plugin in handlers: - name = plugin.get_name() - print(f"Configured vulnerabilities: {self.config.vulnerabilities()}") - if name in self.config.vulnerabilities(): - print(f"{CommandlineColors.OKBLUE}Installing vulnerability: {name}{CommandlineColors.ENDC}") - syscon = {"abs_machinepath_internal": self.abs_machinepath_internal, - "abs_machinepath_external": self.abs_machinepath_external} - plugin.set_sysconf(syscon) - plugin.set_machine_plugin(self.vm_manager) - plugin.setup() - plugin.install(self.vm_manager) - self.vulnerabilities.append(plugin) + for plugin in self.plugin_manager.get_plugins(VulnerabilityPlugin, self.config.vulnerabilities()): + name = plugin.get_name() + print(f"Configured vulnerabilities: {self.config.vulnerabilities()}") + print(f"{CommandlineColors.OKBLUE}Installing vulnerability: {name}{CommandlineColors.ENDC}") + syscon = {"abs_machinepath_internal": self.abs_machinepath_internal, + "abs_machinepath_external": self.abs_machinepath_external} + plugin.set_sysconf(syscon) + plugin.set_machine_plugin(self.vm_manager) + plugin.setup() + plugin.install(self.vm_manager) + self.vulnerabilities.append(plugin) def get_vulnerabilities(self) -> [SensorPlugin]: """ Returns a list of installed vulnerabilities """ diff --git a/app/pluginmanager.py b/app/pluginmanager.py new file mode 100644 index 0000000..7d769df --- /dev/null +++ b/app/pluginmanager.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +""" Manage plugins """ + +import straight.plugin +from glob import glob +import os + +from plugins.base.plugin_base import BasePlugin +from plugins.base.kali import KaliPlugin +from plugins.base.machinery import MachineryPlugin +from plugins.base.sensor import SensorPlugin +from plugins.base.vulnerability_plugin import VulnerabilityPlugin +# from app.interface_sfx import CommandlineColors + + +class PluginManager(): + """ Manage plugins """ + + def __init__(self): + self.base = "plugins/**/*.py" + + def get_plugins(self, subclass, name_filter=None) -> [BasePlugin]: + """ Returns a list plugins matching specified criteria + + + :param subclass: The subclass to use to filter plugins. Currently: KaliPlugin, MachineryPlugin, SensorPlugin, VulnerabilityPlugin + :param name_filter: an optional list of names to select the plugins by + :return: A list of instantiated plugins + """ + + res = [] + + def get_handlers(a_plugin) -> [subclass]: + return a_plugin.produce() + + plugin_dirs = set() + for a_glob in glob(self.base, recursive=True): + plugin_dirs.add(os.path.dirname(a_glob)) + + for a_dir in plugin_dirs: + plugins = straight.plugin.load(a_dir, subclasses=subclass) + + handlers = get_handlers(plugins) + + for plugin in handlers: + if name_filter is None: + res.append(plugin) + else: + names = set(plugin.get_names()) + intersection = names.intersection(name_filter) + if len(intersection): + res.append(plugin) + return res + + def print_list(self): + """ Print a pretty list of all available plugins """ + + sections = [{"name": "Vulnerabilities", + "subclass": VulnerabilityPlugin}, + {"name": "Machinery", + "subclass": MachineryPlugin}, + {"name": "Kali", + "subclass": KaliPlugin}, + {"name": "Sensors", + "subclass": SensorPlugin}, + ] + + for section in sections: + print(f'\t\t{section["name"]}') + plugins = self.get_plugins(section["subclass"]) + for plugin in plugins: + print(f"Name: {plugin.get_name()}") + print(f"Description: {plugin.get_description()}") + print("\t") diff --git a/plugin_manager.py b/plugin_manager.py new file mode 100644 index 0000000..d98adcc --- /dev/null +++ b/plugin_manager.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +""" Managing plugins """ + +import argparse +from app.pluginmanager import PluginManager + + +def list_plugins(arguments): + p = PluginManager() + p.print_list() + + +def create_parser(): + """ Creates the parser for the command line arguments""" + + main_parser = argparse.ArgumentParser("Manage plugins") + subparsers = main_parser.add_subparsers(help="sub-commands") + + # Sub parser for machine creation + parser_create = subparsers.add_parser("list", help="list plugins") + parser_create.set_defaults(func=list_plugins) + # parser_create.add_argument("--configfile", default="experiment.yaml", help="Config file to create from") + + # TODO: Get default config + return main_parser + + +if __name__ == "__main__": + + parser = create_parser() + + args = parser.parse_args() + + args.func(args) diff --git a/plugins/base/plugin_base.py b/plugins/base/plugin_base.py index aa8aafe..b6e27a4 100644 --- a/plugins/base/plugin_base.py +++ b/plugins/base/plugin_base.py @@ -10,6 +10,7 @@ class BasePlugin(): required_files = None # a list of files shipped with the plugin to be installed name = None # The name of the plugin + alternative_names = [] # The is an optional list of alternative names description = None # The description of this plugin def __init__(self): @@ -78,6 +79,22 @@ class BasePlugin(): raise NotImplementedError + def get_names(self) -> []: + """ Adds the name of the plugin to the alternative names and returns the list """ + + res = set() + + if self.name: + res.add(self.name) + + for i in self.alternative_names: + res.add(i) + + if len(res): + return list(res) + + raise NotImplementedError + def get_description(self): """ Returns the description of the plugin, please set in boilerplate """ if self.description: