Improving kali plugins towards being more flexible and handling any kind of attacks. Step 1

pull/3/head
Thorsten Sick 3 years ago
parent 090cd7c16a
commit 95e9efb966

@ -15,6 +15,8 @@ from app.interface_sfx import CommandlineColors
from caldera_control import CalderaControl
from machine_control import Machine
from app.exceptions import ServerError
from plugins.base.kali import KaliPlugin
from app.pluginmanager import PluginManager
# TODO: Multi threading at least when starting machines
@ -33,6 +35,7 @@ class Experiment():
self.experiment_config = ExperimentConfig(configfile)
self.attack_logger = AttackLog(verbosity)
self.plugin_manager = PluginManager(self.attack_logger)
self.__start_attacker()
caldera_url = "http://" + self.attacker_1.getip() + ":8888"
caldera_control = CalderaControl(caldera_url, attack_logger=self.attack_logger, config=self.experiment_config)
@ -155,7 +158,8 @@ class Experiment():
for attack in kali_attacks:
# TODO: Work with snapshots
self.attack_logger.vprint(f"Attacking machine with PAW: {target_1.get_paw()} with attack: {attack}", 1)
self.attacker_1.kali_attack(attack, target_1.getip(), self.experiment_config)
# self.attacker_1.kali_attack(attack, target_1.getip(), self.experiment_config)
self.attack(target_1, attack)
self.attack_logger.vprint(f"Pausing before next attack (config: nap_time): {self.experiment_config.get_nap_time()}", 3)
time.sleep(self.experiment_config.get_nap_time())
@ -181,6 +185,26 @@ class Experiment():
self.attack_logger.write_json(os.path.join(self.lootdir, "attack.json"))
self.zip_loot()
def attack(self, target, attack):
""" Pick an attack and run it
@param attack: Name of the attack to run
@param target: IP address of the target
@returns: The output of the cmdline attacking tool
"""
# TODO: Extend beyond Kali
for plugin in self.plugin_manager.get_plugins(KaliPlugin, [attack]):
name = plugin.get_name()
self.attack_logger.vprint(f"{CommandlineColors.OKBLUE}Running Kali plugin {name}{CommandlineColors.ENDC}", 2)
plugin.process_config(self.experiment_config.kali_conf(plugin.get_config_section_name())) # TODO: De-kalify
plugin.set_attacker_machine(self.attacker_1)
# plugin.__set_logger__(self.attack_logger)
plugin.__execute__([target.getip()])
def zip_loot(self):
""" Zip the loot together """

@ -7,12 +7,11 @@ import time
import requests
from app.config import MachineConfig, ExperimentConfig
from app.config import MachineConfig
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
from plugins.base.machinery import MachineryPlugin
from plugins.base.sensor import SensorPlugin
from plugins.base.vulnerability_plugin import VulnerabilityPlugin
@ -149,27 +148,6 @@ class Machine():
return self.vm_manager.__call_remote_run__(cmd, disown)
def kali_attack(self, attack, target, config: ExperimentConfig):
""" Pick a Kali attack and run it
@param attack: Name of the attack to run
@param target: IP address of the target
@param config: A full experiment config object that has the methog "kali_conf" (just in case I want to split the config later)
@returns: The output of the cmdline attacking tool
"""
for plugin in self.plugin_manager.get_plugins(KaliPlugin, [attack]):
name = plugin.get_name()
self.attack_logger.vprint(f"{CommandlineColors.OKBLUE}Running Kali plugin {name}{CommandlineColors.ENDC}", 2)
syscon = {"abs_machinepath_internal": self.abs_machinepath_internal,
"abs_machinepath_external": self.abs_machinepath_external}
plugin.set_sysconf(syscon)
plugin.process_config(config.kali_conf(plugin.get_config_section_name()))
plugin.set_machine_plugin(self.vm_manager)
# plugin.__set_logger__(self.attack_logger)
plugin.__execute__([target])
def load_machine_plugin(self):
""" Loads the matching machine plugin """

@ -2,6 +2,8 @@
""" Base class for Kali plugins """
from plugins.base.plugin_base import BasePlugin
from app.exceptions import PluginError
import os
class KaliPlugin(BasePlugin):
@ -13,7 +15,9 @@ class KaliPlugin(BasePlugin):
ttp = None
references = None
required_files = []
required_files = [] # Better use the other required_files features
required_files_attacker = [] # a list of files to automatically install to the attacker
required_files_target = [] # a list of files to automatically copy to the targets
# TODO: parse results
@ -21,11 +25,80 @@ class KaliPlugin(BasePlugin):
super().__init__()
self.conf = {} # Plugin specific configuration
self.sysconf = {} # System configuration. common for all plugins
self.attacker_machine_plugin = None # The machine plugin referencing the attacker. The Kali machine should be the perfect candidate
self.target_machine_plugin = None # The machine plugin referencing the target
def copy_to_attacker_and_defender(self):
""" Copy attacker/defender specific files to the machines. Called by setup, do not call it yourself. template processing happens before """
for a_file in self.required_files_attacker:
src = os.path.join(os.path.dirname(self.plugin_path), a_file)
self.vprint(src, 3)
self.attacker_machine_plugin.put(src, self.attacker_machine_plugin.get_playground())
# TODO: add target(s)
def teardown(self):
""" Cleanup afterwards """
pass # pylint: disable=unnecessary-pass
def attacker_run_cmd(self, command, warn=True, disown=False):
""" Execute a command on the attacker
@param command: Command to execute
@param disown: Run in background
"""
if self.attacker_machine_plugin is None:
raise PluginError("machine to run command on is not registered")
self.vprint(f" Plugin running command {command}", 3)
res = self.attacker_machine_plugin.__call_remote_run__(command, disown=disown)
return res
def targets_run_cmd(self, command, warn=True, disown=False):
""" Execute a command on the target
@param command: Command to execute
@param disown: Run in background
"""
if self.target_machine_plugin is None:
raise PluginError("machine to run command on is not registered")
self.vprint(f" Plugin running command {command}", 3)
res = self.target_machine_plugin.__call_remote_run__(command, disown=disown)
return res
def set_target_machines(self, machine):
""" Set the machine to target
@param machine: Machine plugin to communicate with
"""
self.target_machine_plugin = machine.vm_manager
def set_attacker_machine(self, machine):
""" Set the machine plugin class to target
@param machine: Machine to communicate with
"""
self.attacker_machine_plugin = machine.vm_manager
def get_attacker_playground(self):
""" Returns the attacker machine specific playground
Which is the folder on the machine where we run our tasks in
"""
if self.attacker_machine_plugin is None:
raise PluginError("Attacker machine not configured.")
return self.attacker_machine_plugin.get_playground()
def run(self, targets):
""" Run the command
@ -40,10 +113,10 @@ class KaliPlugin(BasePlugin):
"""
self.setup()
self.attack_logger.start_kali_attack(self.machine_plugin.config.vmname(), targets, self.name, ttp=self.get_ttp())
self.attack_logger.start_kali_attack(self.attacker_machine_plugin.config.vmname(), targets, self.name, ttp=self.get_ttp())
res = self.run(targets)
self.teardown()
self.attack_logger.stop_kali_attack(self.machine_plugin.config.vmname(), targets, self.name, ttp=self.get_ttp())
self.attack_logger.stop_kali_attack(self.attacker_machine_plugin.config.vmname(), targets, self.name, ttp=self.get_ttp())
return res
def get_ttp(self):

@ -4,6 +4,7 @@
import os
import yaml
# from shutil import copy
from app.exceptions import PluginError
# TODO: Proper planning and re-building of plugin system. Especially the default config handling should be streamlined. All the plugin types should have a very similar programming interface.
@ -32,6 +33,9 @@ class BasePlugin():
Which is the folder on the machine where we run our tasks in
"""
if self.machine_plugin is None:
raise PluginError("Default machine not configured. Maybe you are creating an attack plugin. Then there are special attack/target machines")
return self.machine_plugin.get_playground()
def set_logger(self, attack_logger):
@ -43,6 +47,11 @@ class BasePlugin():
return
def copy_to_attacker_and_defender(self):
""" Copy attacker/defender specific files to the machines """
return
def setup(self):
""" Prepare everything for the plugin """
@ -53,6 +62,8 @@ class BasePlugin():
self.vprint(src, 3)
self.copy_to_machine(src)
self.copy_to_attacker_and_defender()
def set_machine_plugin(self, machine_plugin):
""" Set the machine plugin class to communicate with
@ -87,7 +98,8 @@ class BasePlugin():
@param filename: File from the plugin folder to copy to the machine share.
"""
self.machine_plugin.put(filename, self.machine_plugin.get_playground())
if self.machine_plugin is not None:
self.machine_plugin.put(filename, self.machine_plugin.get_playground())
def get_from_machine(self, src, dst):
""" Get a file from the machine """
@ -100,6 +112,9 @@ class BasePlugin():
@param disown: Run in background
"""
if self.machine_plugin is None:
raise PluginError("machine to run command on is not registered")
self.vprint(f" Plugin running command {command}", 3)
res = self.machine_plugin.__call_remote_run__(command, disown=disown)

@ -13,7 +13,7 @@ class HydraPlugin(KaliPlugin):
ttp = "T1110"
references = ["https://attack.mitre.org/techniques/T1110/"]
required_files = ["passwords.txt", "users.txt"] # Files shipped with the plugin which are needed by the kali tool. Will be copied to the kali share
required_files_attacker = ["passwords.txt", "users.txt"] # Files shipped with the plugin which are needed by the kali tool. Will be copied to the kali share
def __init__(self):
super().__init__()
@ -26,7 +26,7 @@ class HydraPlugin(KaliPlugin):
"""
# Set defaults if not present in config
playground = self.machine_plugin.get_playground()
playground = self.attacker_machine_plugin.get_playground()
# Generate command
cmd = f"cd {playground};"
@ -35,6 +35,6 @@ class HydraPlugin(KaliPlugin):
for p in self.conf['protocols']:
cmd += f"hydra -L {self.conf['userfile']} -P {self.conf['pwdfile']} {p}://{t};"
res = self.run_cmd(cmd) or ""
res = self.attacker_run_cmd(cmd) or ""
return res

@ -78,11 +78,13 @@ class NmapPlugin(KaliPlugin):
res = ""
cmd = f"cd {self.get_playground()};"
pg = self.get_attacker_playground()
cmd = f"cd {pg};"
for t in targets:
cmd += f"nmap {t};"
res += self.run_cmd(cmd) or ""
res += self.attacker_run_cmd(cmd) or ""
return res

@ -26,12 +26,12 @@ class NmapSneakyPlugin(KaliPlugin):
"""
res = ""
cmd = f"cd {self.get_playground()};"
pg = self.get_attacker_playground()
cmd = f"cd {pg};"
for t in targets:
cmd += f"sudo nmap -T1 -F -D RND:5 -f --randomize-hosts {t};"
res += self.run_cmd(cmd) or ""
res += self.attacker_run_cmd(cmd) or ""
return res

@ -27,11 +27,13 @@ class NmapStresstestPlugin(KaliPlugin):
res = ""
cmd = f"cd {self.get_playground()};"
pg = self.get_attacker_playground()
cmd = f"cd {pg};"
for t in targets:
cmd += f"nmap -T5 --min-parallelism 100 --max-scan-delay 1 {t};"
res += self.run_cmd(cmd) or ""
res += self.attacker_run_cmd(cmd) or ""
return res

Loading…
Cancel
Save