Skip to content

Commit

Permalink
Update USAGE document microlibrary to filter out deprecated relations…
Browse files Browse the repository at this point in the history
…hips appropriately
  • Loading branch information
jondricek committed Oct 27, 2021
1 parent 60298e3 commit 01ac8c3
Showing 1 changed file with 56 additions and 15 deletions.
71 changes: 56 additions & 15 deletions USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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"]
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 01ac8c3

Please sign in to comment.