From 01ac8c3e16f50b99a60554f142cfb6ba1f367fb5 Mon Sep 17 00:00:00 2001 From: Jared Ondricek Date: Tue, 26 Oct 2021 22:43:34 -0500 Subject: [PATCH] Update USAGE document microlibrary to filter out deprecated relationships appropriately --- USAGE.md | 71 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/USAGE.md b/USAGE.md index 3cc685d..6623dda 100644 --- a/USAGE.md +++ b/USAGE.md @@ -741,8 +741,22 @@ The following microlibrary can be used to build a lookup table of stixID to rela The argument to each accessor function is a STIX2 MemoryStore to build the relationship mappings from. ```python +from pprint import pprint from stix2 import MemoryStore, Filter -from itertools import chain + + +# See section below on "Removing revoked and deprecated objects" +def remove_revoked_deprecated(stix_objects): + """Remove any revoked or deprecated objects from queries made to the data source""" + # Note we use .get() because the property may not be present in the JSON data. The default is False + # if the property is not set. + return list( + filter( + lambda x: x.get("x_mitre_deprecated", False) is False and x.get("revoked", False) is False, + stix_objects + ) + ) + def get_related(thesrc, src_type, rel_type, target_type, reverse=False): """build relationship mappings @@ -758,15 +772,17 @@ def get_related(thesrc, src_type, rel_type, target_type, reverse=False): Filter('type', '=', 'relationship'), Filter('relationship_type', '=', rel_type), Filter('revoked', '=', False), - Filter('x_mitre_deprecated", "=", False) ]) + # See section below on "Removing revoked and deprecated objects" + relationships = remove_revoked_deprecated(relationships) + # stix_id => [ { relationship, related_object_id } for each related object ] id_to_related = {} # build the dict for relationship in relationships: - if (src_type in relationship.source_ref and target_type in relationship.target_ref): + if src_type in relationship.source_ref and target_type in relationship.target_ref: if (relationship.source_ref in id_to_related and not reverse) or (relationship.target_ref in id_to_related and reverse): # append to existing entry if not reverse: @@ -814,7 +830,7 @@ def get_related(thesrc, src_type, rel_type, target_type, reverse=False): value = [] for related in id_to_related[stix_id]: if not related["id"] in id_to_target: - continue # targeting a revoked object + continue # targeting a revoked object value.append({ "object": id_to_target[related["id"]], "relationship": related["relationship"] @@ -826,63 +842,83 @@ def get_related(thesrc, src_type, rel_type, target_type, reverse=False): # software:group def software_used_by_groups(thesrc): """returns group_id => {software, relationship} for each software used by the group.""" - return get_related(thesrc, "intrusion-set", "uses", "tool") + get_related(thesrc, "intrusion-set", "uses", "malware") + tools_used_by_group = get_related(thesrc, "intrusion-set", "uses", "tool") + malware_used_by_group = get_related(thesrc, "intrusion-set", "uses", "malware") + return {**tools_used_by_group, **malware_used_by_group} + def groups_using_software(thesrc): """returns software_id => {group, relationship} for each group using the software.""" - return get_related(thesrc, "intrusion-set", "uses", "tool", reverse=True) + get_related(thesrc, "intrusion-set", "uses", "malware", reverse=True) + groups_using_tool = get_related(thesrc, "intrusion-set", "uses", "tool", reverse=True) + groups_using_malware = get_related(thesrc, "intrusion-set", "uses", "malware", reverse=True) + return {**groups_using_tool, **groups_using_malware} + # technique:group def techniques_used_by_groups(thesrc): """returns group_id => {technique, relationship} for each technique used by the group.""" return get_related(thesrc, "intrusion-set", "uses", "attack-pattern") + def groups_using_technique(thesrc): """returns technique_id => {group, relationship} for each group using the technique.""" return get_related(thesrc, "intrusion-set", "uses", "attack-pattern", reverse=True) + # technique:software def techniques_used_by_software(thesrc): """return software_id => {technique, relationship} for each technique used by the software.""" - return get_related(thesrc, "malware", "uses", "attack-pattern") + get_related(thesrc, "tool", "uses", "attack-pattern") + techniques_by_tool = get_related(thesrc, "tool", "uses", "attack-pattern") + techniques_by_malware = get_related(thesrc, "malware", "uses", "attack-pattern") + return {**techniques_by_tool, **techniques_by_malware} + def software_using_technique(thesrc): """return technique_id => {software, relationship} for each software using the technique.""" - return get_related(thesrc, "malware", "uses", "attack-pattern", reverse=True) + get_related(thesrc, "tool", "uses", "attack-pattern", reverse=True) + tools_by_technique_id = get_related(thesrc, "tool", "uses", "attack-pattern", reverse=True) + malware_by_technique_id = get_related(thesrc, "malware", "uses", "attack-pattern", reverse=True) + return {**tools_by_technique_id, **malware_by_technique_id} + # technique:mitigation def mitigation_mitigates_techniques(thesrc): """return mitigation_id => {technique, relationship} for each technique mitigated by the mitigation.""" return get_related(thesrc, "course-of-action", "mitigates", "attack-pattern", reverse=False) + def technique_mitigated_by_mitigations(thesrc): """return technique_id => {mitigation, relationship} for each mitigation of the technique.""" return get_related(thesrc, "course-of-action", "mitigates", "attack-pattern", reverse=True) + # technique:sub-technique def subtechniques_of(thesrc): """return technique_id => {subtechnique, relationship} for each subtechnique of the technique.""" return get_related(thesrc, "attack-pattern", "subtechnique-of", "attack-pattern", reverse=True) + def parent_technique_of(thesrc): """return subtechnique_id => {technique, relationship} describing the parent technique of the subtechnique""" return get_related(thesrc, "attack-pattern", "subtechnique-of", "attack-pattern")[0] + # technique:data-component def datacomponent_detects_techniques(thesrc): """return datacomponent_id => {technique, relationship} describing the detections of each data component""" return get_related(thesrc, "x-mitre-data-component", "detects", "attack-pattern") + def technique_detected_by_datacomponents(thesrc): """return technique_id => {datacomponent, relationship} describing the data components that can detect the technique""" return get_related(thesrc, "x-mitre-data-component", "detects", "attack-pattern", reverse=True) -``` -Example usage: -```python -group_id_to_software = groups_using_software(src) -group_id_to_software["intrusion-set--2a158b0a-7ef8-43cb-9985-bf34d1e12050"] # G0019 +# Example usage: +src = MemoryStore() +src.load_from_file("path/to/enterprise-attack.json") + +group_id_to_software = software_used_by_groups(src) +pprint(group_id_to_software["intrusion-set--2a158b0a-7ef8-43cb-9985-bf34d1e12050"]) # G0019 # [ # { # "object": Malware, # S0061 @@ -930,7 +966,10 @@ get_techniques_by_group_software(src, "intrusion-set--f047ee18-7985-4946-8bfb-4e ## Working with deprecated and revoked objects -Objects that are deemed no longer beneficial to track as part of the knowledge base are marked as deprecated, and objects which are replaced by a different object are revoked. In both cases, the old object is marked with a field (either `x_mitre_deprecated` or `revoked`) noting their status. In the case of revoked objects, a relationship of type `revoked-by` is also created targeting the replacing object. +Objects that are deemed no longer beneficial to track as part of the knowledge base are marked as deprecated, +and objects which are replaced by a different object are revoked. +In both cases, the old object is marked with a field (either `x_mitre_deprecated` or `revoked`) noting their status. +In the case of revoked objects, a relationship of type `revoked-by` is also created targeting the replacing object. ### Removing revoked and deprecated objects @@ -960,7 +999,9 @@ mitigations = remove_revoked_deprecated(mitigations) ### Getting a revoking object -When an object is replaced by another object, it is marked with the field `revoked` and a relationship of type `revoked-by` is created where the `source_ref` is the revoked object and the `target_ref` is the revoking object. This relationship can be followed to find the replacing object: +When an object is replaced by another object, it is marked with the field `revoked` and a relationship of type `revoked-by` +is created where the `source_ref` is the revoked object and the `target_ref` is the revoking object. +This relationship can be followed to find the replacing object: ```python from stix2 import Filter