From 0cf8400eae383dcb07a86740ff60519f9e9f319b Mon Sep 17 00:00:00 2001 From: Thorsten Sick Date: Wed, 13 Oct 2021 12:46:25 +0200 Subject: [PATCH] Some performance improvements when initializing machines --- app/experimentcontrol.py | 48 ++++++++++++++++++++++++++++++++++------ app/pluginmanager.py | 28 +++++++++++++++++++++++ 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/app/experimentcontrol.py b/app/experimentcontrol.py index 539ca4a..dfb406f 100644 --- a/app/experimentcontrol.py +++ b/app/experimentcontrol.py @@ -67,11 +67,14 @@ class Experiment(): except subprocess.CalledProcessError: # Maybe the machine just does not exist yet pass - target_1.install_caldera_service() + if self.machine_needs_caldera(target_1, caldera_attacks): + target_1.install_caldera_service() target_1.up() needs_reboot = target_1.prime_vulnerabilities() needs_reboot |= target_1.prime_sensors() if needs_reboot: + self.attack_logger.vprint( + f"{CommandlineColors.OKBLUE}rebooting target {tname} ....{CommandlineColors.ENDC}", 1) target_1.reboot() self.attack_logger.vprint(f"{CommandlineColors.OKGREEN}Target is up: {tname} {CommandlineColors.ENDC}", 1) self.targets.append(target_1) @@ -89,10 +92,15 @@ class Experiment(): a_target.start_sensors() # First start of caldera implants + at_least_one_caldera_started = False for target_1 in self.targets: - target_1.start_caldera_client() - self.attack_logger.vprint(f"{CommandlineColors.OKGREEN}Initial start of caldera client: {tname} {CommandlineColors.ENDC}", 1) - time.sleep(20) # Wait for all the clients to contact the caldera server + if self.machine_needs_caldera(target_1, caldera_attacks): + target_1.start_caldera_client() + self.attack_logger.vprint(f"{CommandlineColors.OKGREEN}Initial start of caldera client: {tname} {CommandlineColors.ENDC}", 1) + else: + at_least_one_caldera_started = True + if at_least_one_caldera_started: + time.sleep(20) # Wait for all the clients to contact the caldera server # TODO: Smarter wait self.attack_logger.vprint(f"{CommandlineColors.OKBLUE}Contacting caldera agents on all targets ....{CommandlineColors.ENDC}", 1) @@ -101,6 +109,9 @@ class Experiment(): running_agents = self.caldera_control.list_paws_of_running_agents() self.attack_logger.vprint(f"Agents currently running: {running_agents}", 2) while target_1.get_paw() not in running_agents: + if self.machine_needs_caldera(target_1, caldera_attacks) == 0: + self.attack_logger.vprint(f"No caldera agent needed for: {target_1.get_paw()} ", 3) + break self.attack_logger.vprint(f"Connecting to caldera {caldera_url}, running agents are: {running_agents}", 3) self.attack_logger.vprint(f"Missing agent: {target_1.get_paw()} ...", 3) target_1.start_caldera_client() @@ -124,9 +135,11 @@ class Experiment(): for target_1 in self.targets: if caldera_attacks is None: # Run caldera attacks - caldera_attacks = self.experiment_config.get_caldera_attacks(target_1.get_os()) - if caldera_attacks: - for attack in caldera_attacks: + new_caldera_attacks = self.experiment_config.get_caldera_attacks(target_1.get_os()) + else: + new_caldera_attacks = caldera_attacks + if new_caldera_attacks: + for attack in new_caldera_attacks: # TODO: Work with snapshots # TODO: If we have several targets in the same group, it is nonsense to attack each one separately. Make this smarter self.attack_logger.vprint(f"Attacking machine with PAW: {target_1.get_paw()} with {attack}", 2) @@ -150,6 +163,9 @@ class Experiment(): time.sleep(self.experiment_config.get_nap_time()) retries = 100 for target_system in self.targets: + if self.machine_needs_caldera(target_system, caldera_attacks) == 0: + self.attack_logger.vprint(f"No caldera agent needed for: {target_system.get_paw()} ", 3) + continue running_agents = self.caldera_control.list_paws_of_running_agents() self.attack_logger.vprint(f"Agents currently connected to the server: {running_agents}", 2) while target_system.get_paw() not in running_agents: @@ -168,6 +184,8 @@ class Experiment(): self.attack_logger.vprint(f"{CommandlineColors.OKBLUE}Running attack plugins{CommandlineColors.ENDC}", 1) for target_1 in self.targets: plugin_based_attacks = self.experiment_config.get_plugin_based_attacks(target_1.get_os()) + metasploit_plugins = self.plugin_manager.count_caldera_requirements(AttackPlugin, plugin_based_attacks) + print(f"Plugins needing metasploit for {target_1.get_paw()} : {metasploit_plugins}") for attack in plugin_based_attacks: # TODO: Work with snapshots self.attack_logger.vprint(f"Attacking machine with PAW: {target_1.get_paw()} with attack: {attack}", 1) @@ -205,6 +223,21 @@ class Experiment(): zip_this += dg.get_outfile_paths() self.zip_loot(zip_this) + def machine_needs_caldera(self, target, caldera_conf): + """ Counts the attacks and plugins needing caldera that are registered for this machine """ + + c_cmdline = 0 + if caldera_conf is not None: + c_cmdline = len(caldera_conf) + c_conffile = len(self.experiment_config.get_caldera_attacks(target.get_os())) + plugin_based_attacks = self.experiment_config.get_plugin_based_attacks(target.get_os()) + c_plugins = self.plugin_manager.count_caldera_requirements(AttackPlugin, plugin_based_attacks) + + print(f"Caldera count: From cmdline: {c_cmdline}, From conf: {c_conffile} from plugins: {c_plugins}") + + return c_cmdline + c_conffile + c_plugins + + def attack(self, target, attack): """ Pick an attack and run it @@ -222,6 +255,7 @@ class Experiment(): plugin.set_sysconf({}) plugin.set_logger(self.attack_logger) plugin.set_caldera(self.caldera_control) + plugin.connect_metasploit() plugin.install() # plugin.__set_logger__(self.attack_logger) diff --git a/app/pluginmanager.py b/app/pluginmanager.py index 83349d2..00209ab 100644 --- a/app/pluginmanager.py +++ b/app/pluginmanager.py @@ -71,6 +71,34 @@ class PluginManager(): res.append(plugin) return res + def count_caldera_requirements(self, subclass, name_filter=None) -> int: + """ Count the plugins matching the filter that have caldera requirements """ + + # So far it only supports attack plugins. Maybe this will be extended to other plugin types later. + assert subclass == AttackPlugin + + plugins = self.get_plugins(subclass, name_filter) + res = 0 + for plugin in plugins: + if plugin.needs_caldera(): + res += 1 + + return res + + def count_metasploit_requirements(self, subclass, name_filter=None) -> int: + """ Count the plugins matching the filter that have metasploit requirements """ + + # So far it only supports attack plugins. Maybe this will be extended to other plugin types later. + assert subclass == AttackPlugin + + plugins = self.get_plugins(subclass, name_filter) + res = 0 + for plugin in plugins: + if plugin.needs_metasploit(): + res += 1 + + return res + def print_list(self): """ Print a pretty list of all available plugins """