From 2de2f035585ea9b87a66aa16ab46f3df04976520 Mon Sep 17 00:00:00 2001 From: Chase Dillard Date: Wed, 5 Jul 2023 11:48:18 -0400 Subject: [PATCH 1/9] Add tactics by technique function and example script --- mitreattack/get_tactics_by_technique.py | 14 ++++++++++++++ mitreattack/stix20/MitreAttackData.py | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 mitreattack/get_tactics_by_technique.py diff --git a/mitreattack/get_tactics_by_technique.py b/mitreattack/get_tactics_by_technique.py new file mode 100644 index 00000000..d8efb1e1 --- /dev/null +++ b/mitreattack/get_tactics_by_technique.py @@ -0,0 +1,14 @@ +from mitreattack.stix20 import MitreAttackData + + +def main(): + mitre_attack_data = MitreAttackData("enterprise-attack.json") + + tactics_map = mitre_attack_data.get_tactics_by_technique() + matrix_map = mitre_attack_data.get_tactics_by_matrix() + + print(f"Retrieved {len(tactics_map)} techniques.") + + +if __name__ == "__main__": + main() diff --git a/mitreattack/stix20/MitreAttackData.py b/mitreattack/stix20/MitreAttackData.py index feb3a267..ab744f04 100644 --- a/mitreattack/stix20/MitreAttackData.py +++ b/mitreattack/stix20/MitreAttackData.py @@ -419,6 +419,29 @@ def get_tactics_by_matrix(self) -> dict: tactics[matrices[i]["name"]].append(self.src.get(tactic_id)) return tactics + + def get_tactics_by_technique(self) -> dict: + """Retrieve the strutured list of tactics within each technique. + + The order of the tactics in the list matches the ordering of tactics in that technique. + + Returns + ------- + dict + a mapping of tactics to techniques {technique_name: [Tactics]} + """ + tactics = {} + techniques = self.src.query( + Filter("type", "=", "attack-pattern") + ) + + for i in range(len(techniques)): + tactics[techniques[i]["name"]] = [] + + for tactic_id in techniques[i]["kill_chain_phases"]: + tactics[techniques[i]["name"]].append(tactic_id) + + return tactics def get_objects_created_after(self, timestamp: str, remove_revoked_deprecated=False) -> list: """Retrieve objects which have been created after a given time. From 6cdb8939dc2865cbf74b159414deb4c031b61e37 Mon Sep 17 00:00:00 2001 From: Chase Dillard Date: Mon, 10 Jul 2023 10:23:53 -0400 Subject: [PATCH 2/9] Update example text and add parameter to query --- .../get_tactics_by_technique.py | 4 ++-- mitreattack/stix20/MitreAttackData.py | 15 ++------------- 2 files changed, 4 insertions(+), 15 deletions(-) rename {mitreattack => examples}/get_tactics_by_technique.py (60%) diff --git a/mitreattack/get_tactics_by_technique.py b/examples/get_tactics_by_technique.py similarity index 60% rename from mitreattack/get_tactics_by_technique.py rename to examples/get_tactics_by_technique.py index d8efb1e1..c256715c 100644 --- a/mitreattack/get_tactics_by_technique.py +++ b/examples/get_tactics_by_technique.py @@ -3,9 +3,9 @@ def main(): mitre_attack_data = MitreAttackData("enterprise-attack.json") + technique_id = "attack-pattern--7e150503-88e7-4861-866b-ff1ac82c4475" - tactics_map = mitre_attack_data.get_tactics_by_technique() - matrix_map = mitre_attack_data.get_tactics_by_matrix() + tactics_map = mitre_attack_data.get_tactics_by_technique(technique_id) print(f"Retrieved {len(tactics_map)} techniques.") diff --git a/mitreattack/stix20/MitreAttackData.py b/mitreattack/stix20/MitreAttackData.py index ab744f04..b15ba1dd 100644 --- a/mitreattack/stix20/MitreAttackData.py +++ b/mitreattack/stix20/MitreAttackData.py @@ -420,7 +420,7 @@ def get_tactics_by_matrix(self) -> dict: return tactics - def get_tactics_by_technique(self) -> dict: + def get_tactics_by_technique(self, stix_id) -> dict: """Retrieve the strutured list of tactics within each technique. The order of the tactics in the list matches the ordering of tactics in that technique. @@ -430,18 +430,7 @@ def get_tactics_by_technique(self) -> dict: dict a mapping of tactics to techniques {technique_name: [Tactics]} """ - tactics = {} - techniques = self.src.query( - Filter("type", "=", "attack-pattern") - ) - - for i in range(len(techniques)): - tactics[techniques[i]["name"]] = [] - - for tactic_id in techniques[i]["kill_chain_phases"]: - tactics[techniques[i]["name"]].append(tactic_id) - - return tactics + return [phase["phase_name"] for phase in self.get_object_by_stix_id(stix_id)["kill_chain_phases"]] def get_objects_created_after(self, timestamp: str, remove_revoked_deprecated=False) -> list: """Retrieve objects which have been created after a given time. From b92f605e1df8d1fc1a50009494840120345ef2cb Mon Sep 17 00:00:00 2001 From: Chase Dillard Date: Mon, 10 Jul 2023 10:28:47 -0400 Subject: [PATCH 3/9] Update comments for query --- mitreattack/stix20/MitreAttackData.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/mitreattack/stix20/MitreAttackData.py b/mitreattack/stix20/MitreAttackData.py index b15ba1dd..f5ac1914 100644 --- a/mitreattack/stix20/MitreAttackData.py +++ b/mitreattack/stix20/MitreAttackData.py @@ -421,14 +421,17 @@ def get_tactics_by_matrix(self) -> dict: return tactics def get_tactics_by_technique(self, stix_id) -> dict: - """Retrieve the strutured list of tactics within each technique. + """Retrieve the list of tactics within a particular technique. - The order of the tactics in the list matches the ordering of tactics in that technique. + Parameters + ---------- + stix_id : str + the stix id of the technique to be queried. Returns ------- - dict - a mapping of tactics to techniques {technique_name: [Tactics]} + list + a list of tactics that the technique to be queried contains. """ return [phase["phase_name"] for phase in self.get_object_by_stix_id(stix_id)["kill_chain_phases"]] From 756e0aafa873dfcd300c7c8064e7f646e6d693be Mon Sep 17 00:00:00 2001 From: Chase Dillard Date: Mon, 10 Jul 2023 11:45:29 -0400 Subject: [PATCH 4/9] Update comment to remove code smell --- mitreattack/stix20/MitreAttackData.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mitreattack/stix20/MitreAttackData.py b/mitreattack/stix20/MitreAttackData.py index f5ac1914..d9f3f708 100644 --- a/mitreattack/stix20/MitreAttackData.py +++ b/mitreattack/stix20/MitreAttackData.py @@ -430,8 +430,8 @@ def get_tactics_by_technique(self, stix_id) -> dict: Returns ------- - list - a list of tactics that the technique to be queried contains. + dict + a dictionary of tactics that the technique to be queried contains. """ return [phase["phase_name"] for phase in self.get_object_by_stix_id(stix_id)["kill_chain_phases"]] From 411b58aa9a641498ac9f9675dae8ef9af2f7952c Mon Sep 17 00:00:00 2001 From: Chase Dillard Date: Mon, 10 Jul 2023 14:16:05 -0400 Subject: [PATCH 5/9] Change get_tactics_by_technique type hint to list --- mitreattack/stix20/MitreAttackData.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mitreattack/stix20/MitreAttackData.py b/mitreattack/stix20/MitreAttackData.py index d9f3f708..baa9bcad 100644 --- a/mitreattack/stix20/MitreAttackData.py +++ b/mitreattack/stix20/MitreAttackData.py @@ -420,7 +420,7 @@ def get_tactics_by_matrix(self) -> dict: return tactics - def get_tactics_by_technique(self, stix_id) -> dict: + def get_tactics_by_technique(self, stix_id) -> list: """Retrieve the list of tactics within a particular technique. Parameters @@ -430,8 +430,8 @@ def get_tactics_by_technique(self, stix_id) -> dict: Returns ------- - dict - a dictionary of tactics that the technique to be queried contains. + list + a list of tactics that the technique to be queried contains. """ return [phase["phase_name"] for phase in self.get_object_by_stix_id(stix_id)["kill_chain_phases"]] From faaa864aad74cb86c2c072f1bb40a049d3d7f615 Mon Sep 17 00:00:00 2001 From: Chase Dillard Date: Tue, 11 Jul 2023 13:29:30 -0400 Subject: [PATCH 6/9] Replace single line statement with something that makes more sense --- mitreattack/stix20/MitreAttackData.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mitreattack/stix20/MitreAttackData.py b/mitreattack/stix20/MitreAttackData.py index baa9bcad..658fa666 100644 --- a/mitreattack/stix20/MitreAttackData.py +++ b/mitreattack/stix20/MitreAttackData.py @@ -433,8 +433,14 @@ def get_tactics_by_technique(self, stix_id) -> list: list a list of tactics that the technique to be queried contains. """ - return [phase["phase_name"] for phase in self.get_object_by_stix_id(stix_id)["kill_chain_phases"]] - + tactics = [] + phases = self.get_object_by_stix_id(stix_id) + + for phase in phases["kill_chain_phases"]: + tactics.append(phase["phase_name"]) + + return tactics + def get_objects_created_after(self, timestamp: str, remove_revoked_deprecated=False) -> list: """Retrieve objects which have been created after a given time. From 9b3be737232c67db2a1534a2cec7d0372fc363c1 Mon Sep 17 00:00:00 2001 From: Chase Dillard Date: Tue, 11 Jul 2023 14:40:37 -0400 Subject: [PATCH 7/9] Change method from __getobject__ to get --- mitreattack/stix20/MitreAttackData.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mitreattack/stix20/MitreAttackData.py b/mitreattack/stix20/MitreAttackData.py index 658fa666..6c688a19 100644 --- a/mitreattack/stix20/MitreAttackData.py +++ b/mitreattack/stix20/MitreAttackData.py @@ -436,7 +436,7 @@ def get_tactics_by_technique(self, stix_id) -> list: tactics = [] phases = self.get_object_by_stix_id(stix_id) - for phase in phases["kill_chain_phases"]: + for phase in phases.get("kill_chain_phases"): tactics.append(phase["phase_name"]) return tactics From 7aec1e264e6df05bfa7f5a385edeed20109cdfb9 Mon Sep 17 00:00:00 2001 From: Charissa Miller <48832936+clemiller@users.noreply.github.com> Date: Fri, 13 Oct 2023 09:50:04 -0400 Subject: [PATCH 8/9] map shortnames to tactic objects --- examples/get_tactics_by_technique.py | 7 +++++-- mitreattack/stix20/MitreAttackData.py | 22 +++++++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/examples/get_tactics_by_technique.py b/examples/get_tactics_by_technique.py index c256715c..91425e4d 100644 --- a/examples/get_tactics_by_technique.py +++ b/examples/get_tactics_by_technique.py @@ -5,9 +5,12 @@ def main(): mitre_attack_data = MitreAttackData("enterprise-attack.json") technique_id = "attack-pattern--7e150503-88e7-4861-866b-ff1ac82c4475" - tactics_map = mitre_attack_data.get_tactics_by_technique(technique_id) + tactics = mitre_attack_data.get_tactics_by_technique(technique_id) - print(f"Retrieved {len(tactics_map)} techniques.") + print(f"Retrieved {len(tactics)} tactic(s):") + + for t in tactics: + print(f"* {t.name}") if __name__ == "__main__": diff --git a/mitreattack/stix20/MitreAttackData.py b/mitreattack/stix20/MitreAttackData.py index c48932b1..e19452ef 100644 --- a/mitreattack/stix20/MitreAttackData.py +++ b/mitreattack/stix20/MitreAttackData.py @@ -438,13 +438,21 @@ def get_tactics_by_technique(self, stix_id) -> list: list a list of tactics that the technique to be queried contains. """ - tactics = [] - phases = self.get_object_by_stix_id(stix_id) - - for phase in phases.get("kill_chain_phases"): - tactics.append(phase["phase_name"]) - - return tactics + technique = self.get_object_by_stix_id(stix_id) + + # get tactic shortnames from technique + shortnames = [] + for phase in technique.get("kill_chain_phases"): + shortnames.append(phase["phase_name"]) + + # map shortnames to tactic objects + all_tactics = self.get_tactics() + technique_tactics = [] + for tactic in all_tactics: + if tactic.get_shortname() in shortnames: + technique_tactics.append(tactic) + + return technique_tactics def get_objects_created_after(self, timestamp: str, remove_revoked_deprecated=False) -> list: """Retrieve objects which have been created after a given time. From 755884269c01e9972361407b18b6a0164ef3b32c Mon Sep 17 00:00:00 2001 From: Charissa Miller <48832936+clemiller@users.noreply.github.com> Date: Fri, 13 Oct 2023 09:51:13 -0400 Subject: [PATCH 9/9] update docs --- docs/mitre_attack_data/examples.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/mitre_attack_data/examples.rst b/docs/mitre_attack_data/examples.rst index 8c3e4f4e..f905e2d5 100644 --- a/docs/mitre_attack_data/examples.rst +++ b/docs/mitre_attack_data/examples.rst @@ -39,6 +39,7 @@ Getting Multiple ATT&CK Objects * `get_tactics_by_matrix.py `_ * `get_techniques_by_tactic.py `_ +* `get_tactics_by_technique.py `_ * `get_techniques_by_platform.py `_ * `get_objects_by_content.py `_ * `get_objects_created_after.py `_