From 7e6ff60e25dd00b1daffd3a5aa5c872ff185a735 Mon Sep 17 00:00:00 2001 From: Thorsten Sick Date: Tue, 7 Sep 2021 16:54:27 +0200 Subject: [PATCH] improving human readable documents by adding results of the attack --- app/attack_log.py | 106 +++++--- app/calderacontrol.py | 12 +- app/metasploit.py | 33 ++- doc_generator.py | 22 +- .../FIN7/fin7_section1.py | 36 +-- templates/attack_description.rst | 251 +++++++++++------- tools/human_readable_documentation/Makefile | 23 ++ .../{ => source}/conf.py | 1 + 8 files changed, 306 insertions(+), 178 deletions(-) create mode 100644 tools/human_readable_documentation/Makefile rename tools/human_readable_documentation/{ => source}/conf.py (98%) diff --git a/app/attack_log.py b/app/attack_log.py index 9ab08f0..ec4b61e 100644 --- a/app/attack_log.py +++ b/app/attack_log.py @@ -5,9 +5,10 @@ import json import datetime from random import randint +from typing import Optional -def __mitre_fix_ttp__(ttp): +def __mitre_fix_ttp__(ttp: Optional[str]) -> str: """ enforce some systematic naming scheme for MITRE TTPs """ if ttp is None: @@ -22,12 +23,12 @@ def __mitre_fix_ttp__(ttp): class AttackLog(): """ A specific logger class to log the progress of the attack steps """ - def __init__(self, verbosity=0): + def __init__(self, verbosity: int = 0): """ @param verbosity: verbosity setting from 0 to 3 for stdout printing """ - self.log = [] + self.log: list[dict] = [] self.verbosity = verbosity # TODO. As soon as someone wants custom timestamps, make the format variable @@ -41,12 +42,12 @@ class AttackLog(): self.log.append(item) - def __get_timestamp__(self): + def __get_timestamp__(self) -> str: """ Get the timestamp to add to the log entries. Currently not configurable """ return datetime.datetime.now().strftime(self.datetime_format) - def get_caldera_default_name(self, ability_id): + def get_caldera_default_name(self, ability_id: str): """ Returns the default name for this ability based on a db """ data = {"bd527b63-9f9e-46e0-9816-b8434d2b8989": "whoami"} if ability_id not in data: @@ -54,7 +55,7 @@ class AttackLog(): return data[ability_id] - def get_caldera_default_description(self, ability_id): + def get_caldera_default_description(self, ability_id: str): """ Returns the default description for this ability based on a db """ data = {"bd527b63-9f9e-46e0-9816-b8434d2b8989": "Obtain user from current session"} @@ -63,16 +64,16 @@ class AttackLog(): return data[ability_id] - def get_caldera_default_tactics(self, ability_id): + def get_caldera_default_tactics(self, ability_id: str): """ Returns the default tactics for this ability based on a db """ - data = {"bd527b63-9f9e-46e0-9816-b8434d2b8989": " System Owner/User Discovery"} + data = {"bd527b63-9f9e-46e0-9816-b8434d2b8989": "System Owner/User Discovery"} if ability_id not in data: return None return data[ability_id] - def get_caldera_default_tactics_id(self, ability_id): + def get_caldera_default_tactics_id(self, ability_id: str): """ Returns the default name for this ability based on a db """ data = {"bd527b63-9f9e-46e0-9816-b8434d2b8989": "T1033"} @@ -81,7 +82,7 @@ class AttackLog(): return data[ability_id] - def get_caldera_default_situation_description(self, ability_id): + def get_caldera_default_situation_description(self, ability_id: str): """ Returns the default situation description for this ability based on a db """ data = {"bd527b63-9f9e-46e0-9816-b8434d2b8989": None} @@ -90,7 +91,7 @@ class AttackLog(): return data[ability_id] - def get_caldera_default_countermeasure(self, ability_id): + def get_caldera_default_countermeasure(self, ability_id: str): """ Returns the default countermeasure for this ability based on a db """ data = {"bd527b63-9f9e-46e0-9816-b8434d2b8989": None} @@ -99,7 +100,7 @@ class AttackLog(): return data[ability_id] - def start_caldera_attack(self, source, paw, group, ability_id, ttp=None, **kwargs): + def start_caldera_attack(self, source: str, paw: str, group: str, ability_id: str, ttp: str = None, **kwargs): """ Mark the start of a caldera attack @param source: source of the attack. Attack IP @@ -131,6 +132,7 @@ class AttackLog(): "countermeasure": kwargs.get("countermeasure", self.get_caldera_default_countermeasure(ability_id)), # Set by the attack "obfuscator": kwargs.get("obfuscator", "default"), "jitter": kwargs.get("jitter", "default"), + "result": None, } self.__add_to_log__(data) @@ -141,7 +143,7 @@ class AttackLog(): # TODO: Add config # TODO: Add results - def stop_caldera_attack(self, source, paw, group, ability_id, ttp=None, **kwargs): + def stop_caldera_attack(self, source: str, paw: str, group: str, ability_id: str, ttp: str = None, **kwargs): """ Mark the end of a caldera attack @param source: source of the attack. Attack IP @@ -168,11 +170,12 @@ class AttackLog(): "description": kwargs.get("description", ""), "obfuscator": kwargs.get("obfuscator", "default"), "jitter": kwargs.get("jitter", "default"), - "logid": kwargs.get("logid", None) + "logid": kwargs.get("logid", None), + "result": kwargs.get("result", None), } self.__add_to_log__(data) - def start_file_write(self, source, target, file_name): + def start_file_write(self, source: str, target: str, file_name: str): """ Mark the start of a file being written to the target (payload !) @param source: source of the attack. Attack IP (empty if written from controller) @@ -196,7 +199,7 @@ class AttackLog(): self.__add_to_log__(data) return logid - def stop_file_write(self, source, target, file_name, **kwargs): + def stop_file_write(self, source: str, target: str, file_name: str, **kwargs): """ Mark the stop of a file being written to the target (payload !) @param source: source of the attack. Attack IP (empty if written from controller) @@ -220,12 +223,12 @@ class AttackLog(): self.__add_to_log__(data) - def start_execute_payload(self, source, target, command): + def start_execute_payload(self, source: str, target: str, command: str): """ Mark the start of a payload being executed @param source: source of the attack. Attack IP (empty if written from controller) @param target: Target machine of the attack - @param command: Name of the file being written + @param command: """ timestamp = self.__get_timestamp__() @@ -245,7 +248,7 @@ class AttackLog(): return logid - def stop_execute_payload(self, source, target, command, **kwargs): + def stop_execute_payload(self, source: str, target: str, command: str, **kwargs): """ Mark the stop of a payload being executed @param source: source of the attack. Attack IP (empty if written from controller) @@ -266,7 +269,7 @@ class AttackLog(): } self.__add_to_log__(data) - def start_kali_attack(self, source, target, attack_name, ttp=None, **kwargs): + def start_kali_attack(self, source: str, target: str, attack_name: str, ttp: str = None, **kwargs): """ Mark the start of a Kali based attack @param source: source of the attack. Attack IP @@ -295,6 +298,7 @@ class AttackLog(): "description": kwargs.get("description", None), # Generic description for this attack. Set by the attack "situation_description": kwargs.get("situation_description", None), # Description for the situation this attack was run in. Set by the plugin or attacker emulation "countermeasure": kwargs.get("countermeasure", None), # Set by the attack + "result": None, } self.__add_to_log__(data) @@ -304,7 +308,7 @@ class AttackLog(): # TODO: Add config # TODO: Add results - def stop_kali_attack(self, source, target, attack_name, ttp=None, **kwargs): + def stop_kali_attack(self, source: str, target: str, attack_name: str, ttp: str = None, **kwargs): """ Mark the end of a Kali based attack @param source: source of the attack. Attack IP @@ -321,11 +325,12 @@ class AttackLog(): "target": target, "kali_name": attack_name, "hunting_tag": __mitre_fix_ttp__(ttp), - "logid": kwargs.get("logid", None) + "logid": kwargs.get("logid", None), + "result": kwargs.get("result", None), } self.__add_to_log__(data) - def start_narration(self, text): + def start_narration(self, text: str): """ Add some user defined narration. Can be used in plugins to describe the situation before and after the attack, ... At the moment there is no stop narration command. I do not think we need one. But I want to stick to the structure @@ -344,6 +349,42 @@ class AttackLog(): self.__add_to_log__(data) return logid + def start_attack_step(self, text: str): + """ Mark the start of an attack step (several attacks in a chunk) + + @param text: description of the attack step being started + """ + + timestamp = self.__get_timestamp__() + logid = timestamp + "_" + str(randint(1, 100000)) + + data = {"timestamp": timestamp, + "timestamp_end": None, + "event": "start", + "type": "attack_step", + "sub_type": "user defined attack step", + "text": text, + "logid": logid, + } + self.__add_to_log__(data) + + return logid + + def stop_attack_step(self, text: str, **kwargs): + """ Mark the end of an attack step (several attacks in a chunk) + + @param text: description of the attack step being stopped + """ + + data = {"timestamp": self.__get_timestamp__(), + "event": "stop", + "type": "attack_step", + "sub_type": "user defined attack step", + "text": text, + "logid": kwargs.get("logid", None) + } + self.__add_to_log__(data) + def start_build(self, **kwargs): """ Mark the start of a tool building/compilation process @@ -401,7 +442,7 @@ class AttackLog(): } self.__add_to_log__(data) - def start_metasploit_attack(self, source, target, metasploit_command, ttp=None, **kwargs): + def start_metasploit_attack(self, source: str, target: str, metasploit_command: str, ttp: str = None, **kwargs): """ Mark the start of a Metasploit based attack @param source: source of the attack. Attack IP @@ -429,12 +470,13 @@ class AttackLog(): "description": kwargs.get("description", None), # Generic description for this attack. Set by the attack "situation_description": kwargs.get("situation_description", None), # Description for the situation this attack was run in. Set by the plugin or attacker emulation "countermeasure": kwargs.get("countermeasure", None), # Set by the attack + "result": None } self.__add_to_log__(data) return logid - def stop_metasploit_attack(self, source, target, metasploit_command, ttp=None, **kwargs): + def stop_metasploit_attack(self, source: str, target: str, metasploit_command: str, ttp: str = None, **kwargs): """ Mark the start of a Metasploit based attack @param source: source of the attack. Attack IP @@ -451,11 +493,12 @@ class AttackLog(): "target": target, "metasploit_command": metasploit_command, "hunting_tag": __mitre_fix_ttp__(ttp), - "logid": kwargs.get("logid", None) + "logid": kwargs.get("logid", None), + "result": kwargs.get("result", None) } self.__add_to_log__(data) - def start_attack_plugin(self, source, target, plugin_name, ttp=None): + def start_attack_plugin(self, source: str, target: str, plugin_name: str, ttp: str = None): """ Mark the start of an attack plugin @param source: source of the attack. Attack IP @@ -485,7 +528,7 @@ class AttackLog(): # TODO: Add config # TODO: Add results - def stop_attack_plugin(self, source, target, plugin_name, **kwargs): + def stop_attack_plugin(self, source: str, target: str, plugin_name: str, **kwargs): """ Mark the end of an attack plugin @param source: source of the attack. Attack IP @@ -507,7 +550,7 @@ class AttackLog(): } self.__add_to_log__(data) - def write_json(self, filename): + def write_json(self, filename: str): """ Write the json data for this log @param filename: Name of the json file @@ -526,6 +569,9 @@ class AttackLog(): if replace_entry["event"] == "start" and "logid" in replace_entry and replace_entry["logid"] == logid: # Found matching start event. Updating it replace_entry["timestamp_end"] = entry["timestamp"] + if "result" in entry: + replace_entry["result"] = entry["result"] + def get_dict(self): """ Return logged data in dict format """ @@ -540,7 +586,7 @@ class AttackLog(): # TODO: Return full doc - def vprint(self, text, verbosity): + def vprint(self, text: str, verbosity: int): """ verbosity based stdout printing 0: Errors only diff --git a/app/calderacontrol.py b/app/calderacontrol.py index cf54039..a1d80a2 100644 --- a/app/calderacontrol.py +++ b/app/calderacontrol.py @@ -625,7 +625,7 @@ class CalderaControl(): self.add_adversary(adversary_name, ability_id) adid = self.get_adversary(adversary_name)["adversary_id"] - self.attack_logger.start_caldera_attack(source=self.url, + logid = self.attack_logger.start_caldera_attack(source=self.url, paw=paw, group=group, ability_id=ability_id, @@ -682,9 +682,11 @@ class CalderaControl(): except CalderaError: pass + outp="" + if output is None: - output = str(self.get_operation_by_id(opid)) - self.attack_logger.vprint(f"{CommandlineColors.FAIL}Failed getting operation data. We just have: {output} from get_operation_by_id{CommandlineColors.ENDC}", 0) + outp = str(self.get_operation_by_id(opid)) + self.attack_logger.vprint(f"{CommandlineColors.FAIL}Failed getting operation data. We just have: {outp} from get_operation_by_id{CommandlineColors.ENDC}", 0) else: outp = str(output) self.attack_logger.vprint(f"{CommandlineColors.BACKGROUND_GREEN} Output: {outp} {CommandlineColors.ENDC}", 2) @@ -704,7 +706,9 @@ class CalderaControl(): name=self.get_ability(ability_id)[0]["name"], description=self.get_ability(ability_id)[0]["description"], obfuscator=obfuscator, - jitter=jitter + jitter=jitter, + logid=logid, + result=[outp] ) return True diff --git a/app/metasploit.py b/app/metasploit.py index b09b63a..0e6405e 100644 --- a/app/metasploit.py +++ b/app/metasploit.py @@ -438,7 +438,8 @@ class MetasploitInstant(Metasploit): target=target.get_ip(), metasploit_command=command, ttp=ttp, - logid=logid) + logid=logid, + result=res) return res def migrate(self, target, user=None, name=None, arch=None): @@ -472,7 +473,8 @@ class MetasploitInstant(Metasploit): self.attack_logger.stop_metasploit_attack(source=self.attacker.get_ip(), target=target.get_ip(), metasploit_command=command, - ttp=ttp) + ttp=ttp, + result=res) return res def arp_network_discovery(self, target, **kwargs): @@ -505,7 +507,8 @@ class MetasploitInstant(Metasploit): target=target.get_ip(), metasploit_command=command, ttp=ttp, - logid=logid) + logid=logid, + result=res) return res def nslookup(self, target, target2, **kwargs): @@ -542,7 +545,8 @@ class MetasploitInstant(Metasploit): target=target.get_ip(), metasploit_command=command, ttp=ttp, - logid=logid) + logid=logid, + result=res) return res def getsystem(self, target, **kwargs): @@ -580,7 +584,8 @@ Elevate privileges from local administrator to SYSTEM. Three ways to do that wil target=target.get_ip(), metasploit_command=command, ttp=ttp, - logid=logid) + logid=logid, + result=res) return res def clearev(self, target): @@ -601,7 +606,8 @@ Elevate privileges from local administrator to SYSTEM. Three ways to do that wil self.attack_logger.stop_metasploit_attack(source=self.attacker.get_ip(), target=target.get_ip(), metasploit_command=command, - ttp=ttp) + ttp=ttp, + result=res) return res def screengrab(self, target): @@ -628,7 +634,8 @@ Elevate privileges from local administrator to SYSTEM. Three ways to do that wil self.attack_logger.stop_metasploit_attack(source=self.attacker.get_ip(), target=target.get_ip(), metasploit_command=command, - ttp=ttp) + ttp=ttp, + result=res) return res def keylogging(self, target, monitoring_time): @@ -660,7 +667,8 @@ Elevate privileges from local administrator to SYSTEM. Three ways to do that wil self.attack_logger.stop_metasploit_attack(source=self.attacker.get_ip(), target=target.get_ip(), metasploit_command=command, - ttp=ttp) + ttp=ttp, + result=res) return res def getuid(self, target): @@ -683,7 +691,8 @@ Elevate privileges from local administrator to SYSTEM. Three ways to do that wil self.attack_logger.stop_metasploit_attack(source=self.attacker.get_ip(), target=target.get_ip(), metasploit_command=command, - ttp=ttp) + ttp=ttp, + result=res) return res[0] def sysinfo(self, target): @@ -706,7 +715,8 @@ Elevate privileges from local administrator to SYSTEM. Three ways to do that wil self.attack_logger.stop_metasploit_attack(source=self.attacker.get_ip(), target=target.get_ip(), metasploit_command=command, - ttp=ttp) + ttp=ttp, + result=res) return res[0] def upload(self, target, src, dst, **kwargs): @@ -745,5 +755,6 @@ Uploading new files to the target. Can be config files, tools, implants, ... target=target.get_ip(), metasploit_command=command, ttp=ttp, - logid=logid) + logid=logid, + result=res) return res diff --git a/doc_generator.py b/doc_generator.py index 5660193..4be9895 100755 --- a/doc_generator.py +++ b/doc_generator.py @@ -12,19 +12,18 @@ def generate(jfile, outfile): loader=FileSystemLoader("templates", encoding='utf-8', followlinks=False), autoescape=select_autoescape(), trim_blocks=True, - lstrip_blocks=True + # lstrip_blocks=True ) template = env.get_template("attack_description.rst") with open(jfile) as fh: events = json.load(fh) - print(template.render(events=events)) - # pprint(events) - # 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) + rendered = template.render(events=events) + print(rendered) + + with open(outfile, "wt") as fh: + fh.write(rendered) if __name__ == "__main__": @@ -34,8 +33,13 @@ if __name__ == "__main__": # generate("loot/2021_07_28___12_09_00/attack.json", # "tools/human_readable_documentation/contents.rst") # FIN 7 The last minute locally generated thing - generate("loot/2021_08_30___14_40_23/attack.json", - "tools/human_readable_documentation/contents.rst") # FIN 7 With genereated files added + # generate("loot/2021_08_30___14_40_23/attack.json", + # "tools/human_readable_documentation/contents.rst") # FIN 7 With genereated files added + # generate("removeme/loot/2021_09_07___08_59_42/attack.json", + # "tools/human_readable_documentation/source/contents.rst") # FIN 7 first run on environment + generate("loot/2021_09_07___16_20_48/attack.json", + "tools/human_readable_documentation/source/contents.rst") # FIN 7 locally with extended data (older: loot/2021_09_07___14_38_14) + # generate("loot/2021_07_19___15_10_45/attack.json", "tools/human_readable_documentation/contents.rst") # generate("removeme.json", "tools/human_readable_documentation/contents.rst") diff --git a/plugins/default/adversary_emulations/FIN7/fin7_section1.py b/plugins/default/adversary_emulations/FIN7/fin7_section1.py index 1332989..e035b3e 100644 --- a/plugins/default/adversary_emulations/FIN7/fin7_section1.py +++ b/plugins/default/adversary_emulations/FIN7/fin7_section1.py @@ -44,9 +44,7 @@ class FIN7Plugin(AttackPlugin): def step1(self): self.attack_logger.vprint(f"{CommandlineColors.OKBLUE}Step 1 (target hotelmanager): Initial Breach{CommandlineColors.ENDC}", 1) - - self.attack_logger.start_narration( - "Step 1 (target hotelmanager): Initial Breach\n----------------------------") + self.attack_logger.start_attack_step("Step 1 (target hotelmanager): Initial Breach") self.attack_logger.start_narration(""" NOT IMPLEMENTED YET @@ -73,8 +71,7 @@ This is the initial attack step that requires user interaction. Maybe it is bett def step2(self): self.attack_logger.vprint(f"{CommandlineColors.OKBLUE}Step 2 (target hotelmanager): Delayed Malware Execution{CommandlineColors.ENDC}", 1) - self.attack_logger.start_narration( - "Step 2 (target hotelmanager): Delayed Malware Execution\n----------------------------") + self.attack_logger.start_attack_step("Step 2 (target hotelmanager): Delayed Malware Execution") self.attack_logger.start_narration(""" NOT IMPLEMENTED YET @@ -97,7 +94,7 @@ In this simulation sql-rat.js communication will be replaced by Caldera communic def step3(self): self.attack_logger.vprint( f"{CommandlineColors.OKBLUE}Step 3 (target hotelmanager): Target Assessment{CommandlineColors.ENDC}", 1) - self.attack_logger.start_narration("Step 3 (target hotelmanager): Target Assessment\n----------------------------") + self.attack_logger.start_attack_step("Step 3 (target hotelmanager): Target Assessment") # TODO: Make sure logging is nice and complete @@ -254,8 +251,7 @@ In this simulation sql-rat.js communication will be replaced by Caldera communic """ self.attack_logger.vprint( f"{CommandlineColors.OKBLUE}Step 4 (target hotelmanager): Staging Interactive Toolkit{CommandlineColors.ENDC}", 1) - self.attack_logger.start_narration( - "Step 4 (target hotelmanager): Staging Interactive Toolkit\n----------------------------") + self.attack_logger.start_attack_step("Step 4 (target hotelmanager): Staging Interactive Toolkit") self.attack_logger.start_narration(""" In the original attack Babymetal payload is a dll. Currently we are using a simplification here (directly calling a exe). The original steps are: * Target already runs adb156.exe. This one gets the shellcode over the network connection and decodes it. @@ -305,8 +301,7 @@ In the original attack Babymetal payload is a dll. Currently we are using a simp def step5(self): self.attack_logger.vprint( f"{CommandlineColors.OKBLUE}Step 5 (target hotelmanager): Escalate Privileges{CommandlineColors.ENDC}", 1) - self.attack_logger.start_narration( - "Step 5 (target hotelmanager): Escalate Privileges\n----------------------------") + self.attack_logger.start_attack_step("Step 5 (target hotelmanager): Escalate Privileges") hotelmanager = self.get_target_by_name("hotelmanager") @@ -388,12 +383,14 @@ In the original attack Babymetal payload is a dll. Currently we are using a simp situation_description="Executing Mimikatz through UAC bypassing powershell", countermeasure="Behaviour detection" ) - print(metasploit.meterpreter_execute_on([execute_samcats], hotelmanager, delay=20)) + result = metasploit.meterpreter_execute_on([execute_samcats], hotelmanager, delay=20) + print(result) self.attack_logger.stop_metasploit_attack(source=self.attacker_machine_plugin.get_ip(), target=hotelmanager.get_ip(), metasploit_command=execute_samcats, ttp="T1003", - logid=logid) + logid=logid, + result=result) # samcat.exe: reads local credentials https://attack.mitre.org/techniques/T1003/001/ @@ -481,8 +478,7 @@ In the original attack Babymetal payload is a dll. Currently we are using a simp def step6(self): self.attack_logger.vprint( f"{CommandlineColors.OKBLUE}Step 6 (target hotelmanager -> itadmin): Expand Access{CommandlineColors.ENDC}", 1) - self.attack_logger.start_narration( - "Step 6 (target hotelmanager and itadmin): Expand Access\n----------------------------") + self.attack_logger.start_attack_step("Step 6 (target hotelmanager and itadmin): Expand Access") self.attack_logger.start_narration(""" NOT IMPLEMENTED YET. NEEDS A SECOND MACHINE FOR LATERAL MOVEMENT * powershell download: paexec.exe and hollow.exe https://attack.mitre.org/techniques/T1105/ @@ -543,8 +539,7 @@ NOT IMPLEMENTED YET. NEEDS A SECOND MACHINE FOR LATERAL MOVEMENT def step7(self): self.attack_logger.vprint( f"{CommandlineColors.OKBLUE}Step 7 on itadmin: Setup User Monitoring{CommandlineColors.ENDC}", 1) - self.attack_logger.start_narration( - "Step 7 (target itadmin): Setup User Monitoring\n----------------------------") + self.attack_logger.start_attack_step("Step 7 (target itadmin): Setup User Monitoring") self.attack_logger.start_narration(""" NOT IMPLEMENTED YET. A REPLACEMENT FOR THE ALOHA COMMAND CENTER IS NEEDED @@ -579,8 +574,7 @@ NOT IMPLEMENTED YET. A REPLACEMENT FOR THE ALOHA COMMAND CENTER IS NEEDED def step8(self): self.attack_logger.vprint( f"{CommandlineColors.OKBLUE}Step 8 (target: itadmin as domain_admin): User Monitoring{CommandlineColors.ENDC}", 1) - self.attack_logger.start_narration( - "Step 8 (target itadmin): User Monitoring\n----------------------------") + self.attack_logger.start_attack_step("Step 8 (target itadmin): User Monitoring") self.attack_logger.start_narration(""" NOT IMPLEMENTED YET. MAYBE DO THIS PARTIAL. KEYLOGGING NEEDS USER INTERACTION. (Screen spying and keylogging are already implemented as standalone metasploit attacks. Use them) @@ -700,8 +694,7 @@ NOT IMPLEMENTED YET. MAYBE DO THIS PARTIAL. KEYLOGGING NEEDS USER INTERACTION. def step9(self): self.attack_logger.vprint( f"{CommandlineColors.OKBLUE}Step 9 (target: accounting): Setup Shim Persistence{CommandlineColors.ENDC}", 1) - self.attack_logger.start_narration( - "Step 9 (target accounting): Setup Shim Persistence\n----------------------------") + self.attack_logger.start_attack_step("Step 9 (target accounting): Setup Shim Persistence") self.attack_logger.start_narration(""" NOT IMPLEMENTED YET @@ -791,8 +784,7 @@ NOT IMPLEMENTED YET self.attack_logger.vprint( f"{CommandlineColors.OKBLUE}Step 10 (target: accounting): Steal Payment Data{CommandlineColors.ENDC}", 1) - self.attack_logger.start_narration( - "Step 10 (target accounting): Steal Payment Data\n----------------------------") + self.attack_logger.start_attack_step("Step 10 (target accounting): Steal Payment Data") self.attack_logger.start_narration(""" NOT IMPLEMENTED YET. NEEDS TARGET REBOOTING: NO IDEA IF ATTACKX CAN SUPPORT THAT diff --git a/templates/attack_description.rst b/templates/attack_description.rst index 65db52a..7f43539 100644 --- a/templates/attack_description.rst +++ b/templates/attack_description.rst @@ -7,114 +7,161 @@ Target systems Attack steps ------------ {% for e in events %} - {% if e.event is eq("start") %} - {% if e.type is eq("dropping_file") %} - Dropping file to target - ~~~~~~~~~~~~~~~~~~~~~~~ - At {{ e.timestamp }} - The file {{ e.file_name }} is dropped to the target {{ e.target }}. - {% endif %} - {% if e.type is eq("execute_payload") %} - Executing payload on target - ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - At {{ e.timestamp }} - The command {{ e.command }} is used to start a file on the target {{ e.target }}. - {% endif %} - {% if e.type is eq("narration") %} - {{ e.text }} - {% endif %} - {% if e.sub_type is eq("metasploit") %} - Metasploit attack {{ e.name }} - ~~~~~~~~~~~~~~~~~~~~~~~~~~ - Tactics: {{ e.tactics }} - Tactics ID: {{ e.tactics_id }} - Hunting Tag: {{ e.hunting_tag}} - At {{ e.timestamp }} a Metasploit command {{ e.name }} was used to attack {{ e.target }} from {{ e.source }}. - {{ e.description }} - {% if e.metasploit_command is string() %} - Metasploit command: {{ e.metasploit_command }} - {% endif %} - {% if e.situation_description is string() %} - Situation: {{ e.situation_description }} - {% endif %} - {% if e.countermeasure is string() %} - Countermeasure: {{ e.countermeasure }} - {% endif %} - {% endif %} - {% if e.sub_type is eq("kali") %} - Kali attack {{ e.name }} - ~~~~~~~~~~~~~~~~~~~~~~~~~~ - Tactics: {{ e.tactics }} - Tactics ID: {{ e.tactics_id }} - Hunting Tag: {{ e.hunting_tag}} - At {{ e.timestamp }} a Kali command {{ e.kali_name }} was used to attack {{ e.target }} from {{ e.source }}. - {{ e.description }} - {% if e.kali_command is string() %} - Kali command: {{ e.kali_command }} - {% endif %} - {% if e.situation_description is string() %} - Situation: {{ e.situation_description }} - {% endif %} - {% if e.countermeasure is string() %} - Countermeasure: {{ e.countermeasure }} - {% endif %} - {% endif %} - {% if e.sub_type is eq("caldera") %} - Caldera attack {{ e.name }} - ~~~~~~~~~~~~~~~~~~~~~~~~~~ - Tactics: {{ e.tactics }} - Tactics ID: {{ e.tactics_id }} - Hunting Tag: {{ e.hunting_tag}} - At {{ e.timestamp }} a Caldera ability {{ e.ability_id }}/"{{ e.name }}" was used to attack the group {{ e.target_group }} from {{ e.source }}. - {{ e.description }} - {% if e.situation_description is string() %} - Situation: {{ e.situation_description }} - {% endif %} - {% if e.countermeasure is string() %} - Countermeasure: {{ e.countermeasure }} - {% endif %} - {% endif %} - {% endif %} {# event equal start #} +{% if e.event is eq("start") %} +{% if e.type is eq("attack_step") %} + + +{{ e.text }} +~~~~~~~~~~~~ +{% endif %} {# end attack_step #} +{% if e.type is eq("dropping_file") %} + +Dropping file to target +_______________________ +At {{ e.timestamp }} +The file {{ e.file_name }} is dropped to the target {{ e.target }}. +{% endif %} +{% if e.type is eq("execute_payload") %} + +Executing payload on target +___________________________ +At {{ e.timestamp }} +The command {{ e.command }} is used to start a file on the target {{ e.target }}. +{% endif %} +{% if e.type is eq("narration") %} +{{ e.text }} +{% endif %} +{% if e.sub_type is eq("metasploit") %} + +Metasploit attack {{ e.name }} +______________________________ ++ Tactics: {{ e.tactics }} ++ Tactics ID: {{ e.tactics_id }} ++ Hunting Tag: {{ e.hunting_tag}} ++ At {{ e.timestamp }} a Metasploit command {{ e.name }} was used to attack {{ e.target }} from {{ e.source }}. ++ Description: {{ e.description }} +{% if e.metasploit_command is string() %} ++ Metasploit command: {{ e.metasploit_command }} +{% endif %} +{% if e.situation_description is string() %} ++ Situation: {{ e.situation_description }} +{% endif %} +{% if e.countermeasure is string() %} ++ Countermeasure: {{ e.countermeasure }} +{% endif %} +{% if e.result is string() %} +Attack result:: + + {{ e.result }} +{% endif %} +{% if e.result is iterable() %} +Attack result:: + +{% for item in e.result %} + {{ item|trim()|indent(4) }} +{% endfor %} +{% endif %} +{% endif %} +{% if e.sub_type is eq("kali") %} + +Kali attack {{ e.name }} +________________________ ++ Tactics: {{ e.tactics }} ++ Tactics ID: {{ e.tactics_id }} ++ Hunting Tag: {{ e.hunting_tag}} ++ At {{ e.timestamp }} a Kali command {{ e.kali_name }} was used to attack {{ e.target }} from {{ e.source }}. ++ Description: {{ e.description }} +{% if e.kali_command is string() %} ++ Kali command: {{ e.kali_command }} +{% endif %} +{% if e.situation_description is string() %} ++ Situation: {{ e.situation_description }} +{% endif %} +{% if e.countermeasure is string() %} ++ Countermeasure: {{ e.countermeasure }} +{% endif %} +{% if e.result is string() %} +Attack result:: + + {{ e.result }} +{% endif %} +{% if e.result is iterable() %} +Attack result:: + +{% for item in e.result %} + {{ item|trim()|indent(4) }} +{% endfor %} +{% endif %} +{% endif %} +{% if e.sub_type is eq("caldera") %} + +Caldera attack {{ e.name }} +___________________________ ++ Tactics: {{ e.tactics }} ++ Tactics ID: {{ e.tactics_id }} ++ Hunting Tag: {{ e.hunting_tag}} ++ At {{ e.timestamp }} a Caldera ability {{ e.ability_id }}/"{{ e.name }}" was used to attack the group {{ e.target_group }} from {{ e.source }}. ++ Description: {{ e.description }} +{% if e.situation_description is string() %} ++ Situation: {{ e.situation_description }} +{% endif %} +{% if e.countermeasure is string() %} ++ Countermeasure: {{ e.countermeasure }} +{% endif %} +{% if e.result is string() %} +Attack result:: + + {{ e.result }} +{% endif %} +{% if e.result is iterable() %} +Attack result:: + +{% for item in e.result %} + {{ item|trim()|indent(4) }} +{% endfor %} +{% endif %} +{% endif %} +{% endif %} {# event equal start #} {% endfor %} Tools ----- {% for e in events %} - {% if e.event is eq("start") %} - {% if e.type is eq("build") %} - Building tool {{ e.filename }} - ~~~~~~~~~~~~~~~~~~~~~~~ - The file {{ e.filename }} is built - {% if e.for_step %} - It will be used in Step {{ e.for_step }} - {% endif %} - Build time is between {{ e.timestamp }} and {{ e.timestamp_end }} - {% if e.dl_uri is string() %} - Built from source downloaded from {{ e.dl_uri }} - {% endif %} - {% if e.dl_uris %} - Built from sources downloaded from - {% for i in e.dl_uris %} - * {{ i }} - {% endfor %} - {% endif %} - {% if e.payload is string() %} - The attack tool uses a Meterpreter payload. The payload is {{ e.payload }}. The payload is built for the {{ e.platform }} platform and the {{ e.architecture }} architecture. - The settings for lhost and lport are {{ e.lhost }}/{{ e.lport }}. - {% endif %} - {% if e.encoding is string() %} - The file was encoded using {{ e.encoding }} after compilation. - {% endif %} - {% if e.encoded_filename is string() %} - The encoded version is named {{ e.encoded_filename }}. - {% endif %} - {% if e.SRDI_conversion %} - The attack tool was converted to position independent shellcode. See: https://github.com/monoxgas/sRDI - {% endif %} - {{ e.comment }} - {% endif %} - {% endif %} +{% if e.event is eq("start") %} +{% if e.type is eq("build") %} +Building tool {{ e.filename }} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The file {{ e.filename }} is built +{% if e.for_step %} +It will be used in Step {{ e.for_step }} +{% endif %} +Build time is between {{ e.timestamp }} and {{ e.timestamp_end }} +{% if e.dl_uri is string() %} +Built from source downloaded from {{ e.dl_uri }} +{% endif %} +{% if e.dl_uris %} +Built from sources downloaded from +{% for i in e.dl_uris %} +* {{ i }} +{% endfor %} +{% endif %} +{% if e.payload is string() %} +The attack tool uses a Meterpreter payload. The payload is {{ e.payload }}. The payload is built for the {{ e.platform }} platform and the {{ e.architecture }} architecture. +The settings for lhost and lport are {{ e.lhost }}/{{ e.lport }}. +{% endif %} +{% if e.encoding is string() %} +The file was encoded using {{ e.encoding }} after compilation. +{% endif %} +{% if e.encoded_filename is string() %} +The encoded version is named {{ e.encoded_filename }}. +{% endif %} +{% if e.SRDI_conversion %} +The attack tool was converted to position independent shellcode. See: https://github.com/monoxgas/sRDI +{% endif %} +{{ e.comment }} +{% endif %} +{% endif %} {% endfor %} diff --git a/tools/human_readable_documentation/Makefile b/tools/human_readable_documentation/Makefile new file mode 100644 index 0000000..5ea5ad1 --- /dev/null +++ b/tools/human_readable_documentation/Makefile @@ -0,0 +1,23 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= python3 -m sphinx +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile all + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +all: html epub latexpdf text man + diff --git a/tools/human_readable_documentation/conf.py b/tools/human_readable_documentation/source/conf.py similarity index 98% rename from tools/human_readable_documentation/conf.py rename to tools/human_readable_documentation/source/conf.py index 5b3404c..013c9b8 100644 --- a/tools/human_readable_documentation/conf.py +++ b/tools/human_readable_documentation/source/conf.py @@ -13,6 +13,7 @@ # import os # import sys +master_doc = 'contents' # -- Project information -----------------------------------------------------