diff --git a/.gitignore b/.gitignore
index a57b1721080..59588f224dc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@ attack-theme/templates/contribute
attack-theme/templates/datasources
attack-theme/templates/groups
attack-theme/templates/campaigns
+attack-theme/templates/assets
attack-theme/templates/matrices
attack-theme/templates/mitigations
attack-theme/templates/resources
diff --git a/attack-search/src/settings.js b/attack-search/src/settings.js
index db171b6711a..a01ad11ff3e 100644
--- a/attack-search/src/settings.js
+++ b/attack-search/src/settings.js
@@ -2,6 +2,7 @@ const baseURL = ''; // TODO migrate from base_url (generated via Pelican)
const searchFilePaths = [
"campaigns.json",
+ "assets.json",
"datasources.json",
"groups.json",
"matrices.json",
diff --git a/modules/assets/__init__.py b/modules/assets/__init__.py
new file mode 100644
index 00000000000..f39002b53e5
--- /dev/null
+++ b/modules/assets/__init__.py
@@ -0,0 +1,9 @@
+from . import assets
+from . import assets_config
+
+
+def get_priority():
+ return assets_config.priority
+
+def run_module():
+ return (assets.generate_assets(), assets_config.module_name)
diff --git a/modules/assets/assets.py b/modules/assets/assets.py
new file mode 100644
index 00000000000..25c166e201e
--- /dev/null
+++ b/modules/assets/assets.py
@@ -0,0 +1,248 @@
+import collections
+import json
+import os
+
+from loguru import logger
+
+from modules import util
+
+from . import assets_config
+from .. import site_config
+
+
+def generate_assets():
+ """Responsible for verifying asset directory and starting off asset markdown generation."""
+ # Create content pages directory if does not already exist
+ util.buildhelpers.create_content_pages_dir()
+
+ # Move templates to templates directory
+ util.buildhelpers.move_templates(assets_config.module_name, assets_config.assets_templates_path)
+
+ # Verify if directory exists
+ if not os.path.isdir(assets_config.asset_markdown_path):
+ os.mkdir(assets_config.asset_markdown_path)
+
+ # Generate redirections
+ util.buildhelpers.generate_redirections(
+ redirections_filename=assets_config.assets_redirection_location, redirect_md=site_config.redirect_md
+ )
+
+ # Generates the markdown files to be used for page generation
+ assets_generated = generate_markdown_files()
+
+ if not assets_generated:
+ util.buildhelpers.remove_module_from_menu(assets_config.module_name)
+
+
+def generate_markdown_files():
+ """
+ Responsible for generating asset index page and getting shared data for all assets.
+ """
+ has_asset = False
+
+ asset_list = util.relationshipgetters.get_asset_list()
+
+ asset_list_no_deprecated_revoked = util.buildhelpers.filter_deprecated_revoked(asset_list)
+
+ if asset_list_no_deprecated_revoked:
+ has_asset = True
+
+ if has_asset:
+ data = {}
+
+ notes = util.relationshipgetters.get_objects_using_notes()
+ side_menu_data = util.buildhelpers.get_side_menu_data(
+ "Assets", "/assets/", asset_list_no_deprecated_revoked
+ )
+ data["side_menu_data"] = side_menu_data
+ data["assets_table"] = get_assets_table_data(asset_list_no_deprecated_revoked)
+ data["assets_list_len"] = str(len(asset_list_no_deprecated_revoked))
+
+ subs = assets_config.asset_index_md + json.dumps(data)
+
+ with open(
+ os.path.join(assets_config.asset_markdown_path, "overview.md"), "w", encoding="utf8"
+ ) as md_file:
+ md_file.write(subs)
+
+ # Create the markdown for assets
+ for asset in asset_list:
+ generate_asset_md(asset, side_menu_data, notes)
+
+ return has_asset
+
+
+def generate_asset_md(asset, side_menu_data, notes):
+ """Responsible for generating markdown of all assets."""
+
+ attack_id = util.buildhelpers.get_attack_id(asset)
+
+ if not attack_id: return
+
+ data = {}
+ data["attack_id"] = attack_id
+ data["side_menu_data"] = side_menu_data
+ data["notes"] = notes.get(asset["id"])
+
+ dates = util.buildhelpers.get_created_and_modified_dates(asset)
+ if dates.get("created"):
+ data["created"] = dates["created"]
+ if dates.get("modified"):
+ data["modified"] = dates["modified"]
+ if asset.get("name"):
+ data["name"] = asset["name"]
+ if asset.get("x_mitre_version"):
+ data["version"] = asset["x_mitre_version"]
+
+ if isinstance(asset.get("x_mitre_contributors"), collections.abc.Iterable):
+ data["contributors_list"] = asset["x_mitre_contributors"]
+
+ if asset.get("x_mitre_platforms"):
+ asset["x_mitre_platforms"].sort()
+ data["platforms"] = ", ".join(asset["x_mitre_platforms"])
+
+ if asset.get("x_mitre_sectors"):
+ asset["x_mitre_sectors"].sort()
+ data["sectors"] = ", ".join(asset["x_mitre_sectors"])
+
+ # Get initial reference list
+ reference_list = {"current_number": 0}
+
+ # Get initial reference list from asset object
+ reference_list = util.buildhelpers.update_reference_list(reference_list, asset)
+
+ if asset.get("description"):
+ data["descr"] = asset["description"]
+ if asset.get("x_mitre_deprecated"):
+ data["deprecated"] = True
+
+ # Get technique data for technique table
+ data["technique_table_data"] = get_techniques_targeting_asset_data(asset, reference_list)
+
+ # Get navigator layers for this asset
+ layers = util.buildhelpers.get_navigator_layers(
+ data["name"],
+ data["attack_id"],
+ "asset",
+ "targeted by",
+ data["version"] if "version" in data else None,
+ data["technique_table_data"],
+ )
+
+ data["layers"] = []
+ for layer in layers:
+ with open(
+ os.path.join(
+ assets_config.asset_markdown_path,
+ "-".join([data["attack_id"], "techniques", layer["domain"]]) + ".md",
+ ),
+ "w",
+ encoding="utf8",
+ ) as layer_json:
+ subs = site_config.layer_md.substitute(
+ {
+ "attack_id": data["attack_id"],
+ "path": "assets/" + data["attack_id"],
+ "domain": layer["domain"],
+ }
+ )
+ subs = subs + layer["layer"]
+ layer_json.write(subs)
+ data["layers"].append(
+ {
+ "domain": layer["domain"],
+ "name": layer["name"],
+ "filename": layer["filename"],
+ "navigator_link": site_config.navigator_link,
+ }
+ )
+
+ if asset.get("x_mitre_related_assets"):
+ data["related_assets_table"] = get_related_asset_data(asset["x_mitre_related_assets"])
+
+ data["citations"] = reference_list
+ data["versioning_feature"] = site_config.check_versions_module()
+
+ subs = assets_config.asset_md.substitute(data)
+ subs = subs + json.dumps(data)
+
+ # Write out the markdown file
+ with open(
+ os.path.join(assets_config.asset_markdown_path, data["attack_id"] + ".md"), "w", encoding="utf8"
+ ) as md_file:
+ md_file.write(subs)
+
+
+def get_assets_table_data(asset_list):
+ """Responsible for generating asset table data for the asset index page"""
+ assets_table_data = []
+ for asset in asset_list:
+ attack_id = util.buildhelpers.get_attack_id(asset)
+ if not attack_id: continue
+
+ domain_list = util.buildhelpers.get_domain_name(asset)
+ row = {
+ "id": attack_id,
+ "name": asset["name"] if asset.get("name") else attack_id,
+ }
+
+ for domain_idx in range(len(domain_list)):
+ domain_list[domain_idx] = domain_list[domain_idx].replace('-attack','')
+ if domain_list[domain_idx] == "ics":
+ domain_list[domain_idx] = domain_list[domain_idx].upper()
+ else:
+ domain_list[domain_idx] = domain_list[domain_idx].capitalize()
+ row["domains"] = domain_list
+
+ if asset.get("description"):
+ row["descr"] = asset["description"]
+
+ if asset.get("x_mitre_deprecated"):
+ row["deprecated"] = True
+
+ assets_table_data.append(row)
+
+ return assets_table_data
+
+
+def get_related_asset_data(related_assets):
+ if not related_assets: return []
+
+ related_asset_data = []
+ for related_asset in related_assets:
+ row = {
+ "name": related_asset["name"], # required
+ }
+ if related_asset.get("related_asset_sectors"):
+ related_asset["related_asset_sectors"].sort()
+ row["sectors"] = ", ".join(related_asset["related_asset_sectors"])
+ if related_asset.get("description"):
+ row["descr"] = related_asset["description"]
+ related_asset_data.append(row)
+ return related_asset_data
+
+
+def get_techniques_targeting_asset_data(asset, reference_list):
+ """Given an asset and its reference list, get the techniques targeting the asset.
+ Check the reference list for citations, if not found in list, add it.
+ """
+ technique_list = {}
+ techniques_targeting_assets = util.relationshipgetters.get_techniques_targeting_assets()
+
+ if techniques_targeting_assets.get(asset.get("id")):
+ for technique in techniques_targeting_assets[asset["id"]]:
+ # Do not add if technique is deprecated
+ if not technique["object"].get("x_mitre_deprecated"):
+ technique_list = util.buildhelpers.technique_used_helper(technique_list, technique, reference_list)
+
+ technique_data = []
+ for item in technique_list:
+ technique_data.append(technique_list[item])
+ # Sort by technique name
+ technique_data = sorted(technique_data, key=lambda k: k["name"].lower())
+
+ # Sort by domain name
+ technique_data = sorted(
+ technique_data, key=lambda k: [site_config.custom_alphabet.index(c) for c in k["domain"].lower()]
+ )
+ return technique_data
diff --git a/modules/assets/assets_config.py b/modules/assets/assets_config.py
new file mode 100644
index 00000000000..deafdb527d0
--- /dev/null
+++ b/modules/assets/assets_config.py
@@ -0,0 +1,22 @@
+from string import Template
+
+module_name = "Assets"
+priority = 7.2
+
+# Markdown path for assets
+asset_markdown_path = "content/pages/assets/"
+
+# String template for asset index page
+asset_index_md = (
+ "Title: Asset overview\nTemplate: assets/assets-index\nsave_as: assets/index.html\ndata: "
+)
+
+# String template for asset page
+asset_md = Template(
+ "Title: ${name}\nTemplate: assets/asset\nsave_as: assets/${attack_id}/index.html\ndata: "
+)
+
+# Path for templates
+assets_templates_path = "modules/assets/templates/"
+
+assets_redirection_location = "modules/assets/assets_redirections.json"
diff --git a/modules/assets/assets_redirections.json b/modules/assets/assets_redirections.json
new file mode 100644
index 00000000000..16b713b22f4
--- /dev/null
+++ b/modules/assets/assets_redirections.json
@@ -0,0 +1,7 @@
+[
+ {
+ "title": "assets-redirect",
+ "from": "assets.html",
+ "to": "/assets/"
+ }
+]
diff --git a/modules/assets/templates/asset.html b/modules/assets/templates/asset.html
new file mode 100644
index 00000000000..0dfb2452ac9
--- /dev/null
+++ b/modules/assets/templates/asset.html
@@ -0,0 +1,172 @@
+{% extends "general/two-column.html" %}
+{% set parsed = page.data | from_json %}
+{% set title = parsed.name + ", Asset " + parsed.attack_id + " | MITRE ATT&CK®" -%}
+{% set active_page = "assets" -%}
+
+{% import 'macros/deprecated.html' as deprecated %}
+{% import 'macros/navigator.html' as navigator %}
+{% import 'macros/references.html' as references %}
+{% import 'macros/navigation.html' as navigation %}
+{% import 'macros/techniques_used.html' as techniques_used %}
+{% import 'macros/versioning.html' as versioning %}
+{% import 'macros/clean_output.html' as clean_output %}
+{% import 'macros/citations.html' as citations %}
+{% import 'macros/notes.html' as notes %}
+
+{% block head %}
+ {{ super () }}
+ {% if parsed.deprecated %}
+
+ {% endif %}
+{% endblock %}
+
+{% block innerleft %}
+
+
+ {{ navigation.sidenav(parsed.side_menu_data, output_file) }}
+
+
+{% endblock %}
+
+{% block innerright %}
+{{ super () }}
+
+
+ - Home
+ - Assets
+ - {{ parsed.name }}
+
+
+
+
+
+
+
{{ parsed.name }}
+
+ {% if parsed.deprecated %}
+ {% if parsed.descr %}
+ {{ deprecated.deprecated(clean_output.stixToHTML(parsed.descr, firstParagraphOnly=True)) }}
+ {% else %}
+ {{ deprecated.deprecated("This asset has been deprecated.") }}
+ {% endif %}
+ {% else %}
+
+
+ {% if parsed.descr %}
+
+ {{ clean_output.stixToHTML(parsed.descr, parsed.citations) }}
+
+ {% endif %}
+ {% if parsed.notes %}
+ {{ notes.notes_section(parsed.notes) }}
+ {% endif %}
+
+
+
+
+ {% if parsed.attack_id %}
+
+
+ ID: {{ parsed.attack_id }}
+
+
+ {% endif %}
+ {% if parsed.platforms %}
+
+
+ ⓘ
+
+
+ {{ "Platform" if parsed.platforms | length < 2 else "Platforms" }}: {{parsed.platforms}}
+
+
+ {% endif %}
+ {% if parsed.sectors %}
+
+
+ {{ "Sector" if parsed.sectors | length < 2 else "Sectors" }}: {{parsed.sectors}}
+
+
+ {% endif %}
+ {% if parsed.contributors_list %}
+
+
+ Contributors: {{ parsed.contributors_list|join("; ") }}
+
+
+ {% endif %}
+ {% if parsed.version %}
+
+
+ Version: {{ parsed.version }}
+
+
+ {% endif %}
+ {% if parsed.created %}
+
+
+ Created: {{ parsed.created }}
+
+
+ {% endif %}
+ {% if parsed.modified %}
+
+
+ Last Modified: {{ parsed.modified }}
+
+
+ {% endif %}
+
+
+ {% if parsed.versioning_feature %}
+ {{ versioning.permalink_button(output_file, parsed.attack_id)}}
+ {% endif %}
+
+
+
+ {% if parsed.related_assets_table %}
+
+
+
+
+ Name |
+ Sectors |
+ Description |
+
+
+
+ {% for related_asset in parsed.related_assets_table %}
+
+ {{ related_asset.name }} |
+ {{ related_asset.sectors }} |
+
+ {{ clean_output.stixToHTML(related_asset.descr, parsed.citations) }}
+ |
+
+ {% endfor %}
+
+
+ {% endif %}
+
+ {% if parsed.technique_table_data %}
+ {{ navigator.layer_links(parsed.attack_id, "assets", parsed.layers) }}
+ {{ techniques_used.techniques_used(parsed.technique_table_data, "Techniques", true, "", parsed.citations) }}
+ {% endif %}
+
+ {{ citations.reference_section(parsed.citations) }}
+ {% endif %}
+
+
+
+
+
+{% endblock %}
+
+{% block scripts %}
+ {{ super() }}
+
+
+
+
+
+{% endblock %}
diff --git a/modules/assets/templates/assets-index.html b/modules/assets/templates/assets-index.html
new file mode 100644
index 00000000000..7c3d9f910d3
--- /dev/null
+++ b/modules/assets/templates/assets-index.html
@@ -0,0 +1,80 @@
+{% extends "general/two-column.html" %}
+{% set title = "Assets | MITRE ATT&CK®" %}
+{% set parsed = page.data | from_json %}
+{% set active_page = "assets" -%}
+{% import 'macros/navigation.html' as navigation %}
+{% import 'macros/clean_output.html' as clean_output %}
+
+{% block innerleft %}
+
+
+ {{ navigation.sidenav(parsed.side_menu_data, output_file) }}
+
+
+{% endblock %}
+
+{% block innerright %}
+ {{ super () }}
+
+ - Home
+ - Assets
+
+
+
+
+
+
+
+
Assets
+
+ <PLACEHOLDER DESCRIPTION>
+
+
+
Assets: {{ parsed.assets_list_len }}
+
+
+
+ ID |
+ Name |
+ Domain |
+ Description |
+
+
+
+ {% for row in parsed.assets_table %}
+
+
+ {{ row.id | upper }}
+ |
+
+ {{ row.name }}
+ |
+
+ {% for dom in row.domains %}
+ {{dom}}
+
+ {% endfor %}
+ |
+
+ {% if row.deprecated %}
+ ****Deprecation Warning****
+ {% endif %}
+ {{ clean_output.stixToHTML(row.descr) }}
+ |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block scripts %}
+ {{ super() }}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/modules/campaigns/campaigns.py b/modules/campaigns/campaigns.py
index 0a4eba57575..4ebfa2aa80e 100644
--- a/modules/campaigns/campaigns.py
+++ b/modules/campaigns/campaigns.py
@@ -141,6 +141,7 @@ def generate_campaign_md(campaign, side_menu_data, notes):
data["name"],
data["attack_id"],
"campaign",
+ "used by",
data["version"] if "version" in data else None,
data["technique_table_data"],
)
diff --git a/modules/datasources/__init__.py b/modules/datasources/__init__.py
index 5c33c5c4402..3a40ecd1342 100644
--- a/modules/datasources/__init__.py
+++ b/modules/datasources/__init__.py
@@ -24,6 +24,7 @@ def get_menu():
{"display_name": "Groups", "url": "/groups", "external_link": False, "children": []},
{"display_name": "Software", "url": "/software", "external_link": False, "children": []},
{"display_name": "Campaigns", "url": "/campaigns", "external_link": False, "children": []},
+ {"display_name": "Assets", "url": "/assets", "external_link": False, "children": [] },
],
}
diff --git a/modules/groups/groups.py b/modules/groups/groups.py
index 5b2c888bd6c..28310a4e083 100644
--- a/modules/groups/groups.py
+++ b/modules/groups/groups.py
@@ -132,6 +132,7 @@ def generate_group_md(group, side_menu_data, notes):
data["name"],
data["attack_id"],
"group",
+ "used by",
data["version"] if "version" in data else None,
data["technique_table_data"],
inheritance, # extend legend to include color coding for inherited techniques, if applicable
diff --git a/modules/mitigations/mitigations.py b/modules/mitigations/mitigations.py
index e6e6eb64e3c..bb8ad266725 100644
--- a/modules/mitigations/mitigations.py
+++ b/modules/mitigations/mitigations.py
@@ -137,6 +137,7 @@ def generate_mitigation_md(mitigation, domain, side_menu_data, notes):
data["name"],
data["attack_id"],
"mitigation",
+ "mitigated by",
data["version"] if "version" in data else None,
data["techniques_addressed_data"],
)
diff --git a/modules/random_page/random_page.py b/modules/random_page/random_page.py
index 11236283fc0..604e7e65e17 100644
--- a/modules/random_page/random_page.py
+++ b/modules/random_page/random_page.py
@@ -16,6 +16,7 @@ def generate_json():
"groups": "Group",
"software": "Software",
"campaigns": "Campaign",
+ "assets": "Asset"
}
routes = {}
@@ -57,6 +58,8 @@ def generate_json():
add_to_json = True
elif route == "campaigns" and re.search(r"C[0-9]{4}", thepath):
add_to_json = True
+ elif route == "assets" and re.search(r"A[0-9]{4}", thepath):
+ add_to_json = True
if add_to_json:
json_data[value].append(thepath[6:])
diff --git a/modules/resources/resources.py b/modules/resources/resources.py
index 4433bde8ddb..07f9940b32c 100644
--- a/modules/resources/resources.py
+++ b/modules/resources/resources.py
@@ -319,6 +319,7 @@ def generate_working_with_attack():
"techniques",
"datasources",
"campaigns",
+ "assets"
]
# Verify if directories exists
diff --git a/modules/software/software.py b/modules/software/software.py
index b93a6faee0b..eb4d623b794 100644
--- a/modules/software/software.py
+++ b/modules/software/software.py
@@ -129,6 +129,7 @@ def generate_software_md(software, side_menu_data, notes):
data["name"],
data["attack_id"],
"software",
+ "used by",
data["version"] if "version" in data else None,
data["technique_table_data"],
)
diff --git a/modules/techniques/techniques.py b/modules/techniques/techniques.py
index e5ab5e051c0..1c5980aaab8 100644
--- a/modules/techniques/techniques.py
+++ b/modules/techniques/techniques.py
@@ -55,9 +55,8 @@ def generate_techniques():
check_if_generated = generate_domain_markdown(
domain["name"], techniques_no_sub, tactics, side_nav_data, notes, deprecated
)
- if not technique_generated:
- if check_if_generated:
- technique_generated = True
+ if not technique_generated and check_if_generated:
+ technique_generated = True
if not technique_generated:
util.buildhelpers.remove_module_from_menu(techniques_config.module_name)
@@ -220,6 +219,9 @@ def generate_data_for_md(technique_dict, technique, tactic_list, is_sub_techniqu
# Get examples
technique_dict["examples_table"] = get_examples_table_data(technique, reference_list)
+ # Get asset table
+ technique_dict["assets_table"] = get_assets_table_data(technique, reference_list)
+
# Get technique version
if technique.get("x_mitre_version"):
technique_dict["version"] = technique["x_mitre_version"]
@@ -357,26 +359,60 @@ def get_mitigations_table_data(technique, reference_list):
# Do not add deprecated mitigation to table
if not mitigation["object"].get("x_mitre_deprecated"):
attack_id = util.buildhelpers.get_attack_id(mitigation["object"])
-
# Only add if mitigation attack id is found
- if attack_id:
- row = {}
- row["mid"] = attack_id
- row["name"] = mitigation["object"]["name"]
- if mitigation["relationship"].get("description"):
- # Get filtered description
- reference_list = util.buildhelpers.update_reference_list(
- reference_list, mitigation["relationship"]
- )
- row["descr"] = mitigation["relationship"]["description"]
+ if not attack_id: continue
+ row = {}
+ row["mid"] = attack_id
+ row["name"] = mitigation["object"]["name"]
+ if mitigation["relationship"].get("description"):
+ # Get filtered description
+ reference_list = util.buildhelpers.update_reference_list(
+ reference_list, mitigation["relationship"]
+ )
+ row["descr"] = mitigation["relationship"]["description"]
- mitigation_data.append(row)
+ mitigation_data.append(row)
if mitigation_data:
mitigation_data = sorted(mitigation_data, key=lambda k: k["name"].lower())
return mitigation_data
+def get_assets_table_data(technique, reference_list):
+ """Given a technique a reference list, find assets that are targeted by the
+ technique and return list with asset data. Also modifies the
+ reference list if it finds a reference that is not on the list
+ """
+ asset_data = []
+
+ # Check if technique has assets
+ assets_targeted_by_techniques = util.relationshipgetters.get_assets_targeted_by_techniques().get(technique["id"])
+ if assets_targeted_by_techniques:
+ # Iterate through technique assets
+ for asset in assets_targeted_by_techniques:
+ # Do not add deprecated assets to table
+ if not asset["object"].get("x_mitre_deprecated"):
+ attack_id = util.buildhelpers.get_attack_id(asset["object"])
+
+ # Only add if attack id is found
+ if not attack_id: continue
+ row = {}
+ row["id"] = attack_id
+ row["name"] = asset["object"]["name"]
+ if asset["relationship"].get("description"):
+ # Get filtered description
+ reference_list = util.buildhelpers.update_reference_list(
+ reference_list, asset["relationship"]
+ )
+ row["descr"] = asset["relationship"]["description"]
+
+ asset_data.append(row)
+
+ if asset_data:
+ asset_data = sorted(asset_data, key=lambda k: k["name"].lower())
+ return asset_data
+
+
def get_examples_table_data(technique, reference_list):
"""Given a technique object, find examples in malware using technique,
tools using technique and groups using technique. Return list with
@@ -399,23 +435,23 @@ def get_examples_table_data(technique, reference_list):
attack_id = util.buildhelpers.get_attack_id(example["object"])
# Only add example data if the attack id is found
- if attack_id:
- row = {}
+ if not attack_id: continue
+ row = {}
- row["id"] = attack_id
+ row["id"] = attack_id
- row["path"] = get_path_from_type(example["object"])
+ row["path"] = get_path_from_type(example["object"])
- row["name"] = example["object"]["name"]
+ row["name"] = example["object"]["name"]
- if example["relationship"].get("description"):
- # Get filtered description
- reference_list = util.buildhelpers.update_reference_list(
- reference_list, example["relationship"]
- )
- row["descr"] = example["relationship"]["description"]
+ if example["relationship"].get("description"):
+ # Get filtered description
+ reference_list = util.buildhelpers.update_reference_list(
+ reference_list, example["relationship"]
+ )
+ row["descr"] = example["relationship"]["description"]
- example_data.append(row)
+ example_data.append(row)
if example_data:
example_data = sorted(example_data, key=lambda k: k["name"].lower())
@@ -507,20 +543,21 @@ def get_techniques_list(techniques):
if not technique.get("revoked") and not technique.get("x_mitre_deprecated"):
attack_id = util.buildhelpers.get_attack_id(technique)
- if attack_id:
- technique_dict = {}
- technique_dict["id"] = attack_id
- technique_dict["stix_id"] = technique["id"]
- technique_dict["name"] = technique["name"]
- technique_dict["description"] = technique["description"]
+ if not attack_id: continue
- if technique.get("kill_chain_phases"):
- for elem in technique["kill_chain_phases"]:
- # Fill dict
- if elem["phase_name"] not in technique_list:
- technique_list[elem["phase_name"]] = []
+ technique_dict = {}
+ technique_dict["id"] = attack_id
+ technique_dict["stix_id"] = technique["id"]
+ technique_dict["name"] = technique["name"]
+ technique_dict["description"] = technique["description"]
- technique_list[elem["phase_name"]].append(technique_dict)
+ if technique.get("kill_chain_phases"):
+ for elem in technique["kill_chain_phases"]:
+ # Fill dict
+ if elem["phase_name"] not in technique_list:
+ technique_list[elem["phase_name"]] = []
+
+ technique_list[elem["phase_name"]].append(technique_dict)
for key, __ in technique_list.items():
technique_list[key] = sorted(technique_list[key], key=lambda k: k["name"].lower())
@@ -531,8 +568,6 @@ def get_techniques_list(techniques):
def get_subtechniques(technique):
"""Given a technique, return the ID and name of the subtechnique."""
subtechs = []
- attack_id = util.buildhelpers.get_attack_id(technique)
-
subtechniques_of = util.relationshipgetters.get_subtechniques_of()
if technique["id"] in subtechniques_of:
diff --git a/modules/techniques/templates/technique.html b/modules/techniques/templates/technique.html
index 12faafebbbf..79e8f44ace3 100644
--- a/modules/techniques/templates/technique.html
+++ b/modules/techniques/templates/technique.html
@@ -369,6 +369,29 @@ Procedure Examples
{% endif %}
+ {% if parsed.assets_table %}
+ Targeted Assets
+
+ {% endif %}
{% if parsed.domain != 'pre' %}
Mitigations
{% if parsed.mitigation_table %}
diff --git a/modules/util/buildhelpers.py b/modules/util/buildhelpers.py
index ea0c6eb92ab..20b82e56d26 100644
--- a/modules/util/buildhelpers.py
+++ b/modules/util/buildhelpers.py
@@ -507,17 +507,17 @@ def replace_html_chars(to_be_replaced):
}
-def get_navigator_layers(name, attack_id, obj_type, version, techniques_used, inheritance=False):
+def get_navigator_layers(name, attack_id, obj_type, rel_type, version, techniques_used, inheritance=False):
"""Generate the Enterprise, Mobile, and ICS Navigator JSON layers for the given object."""
# Generate Enterprise base layer
- enterprise_layer = build_base_layer("enterprise-attack", name, obj_type, attack_id, version, inheritance)
+ enterprise_layer = build_base_layer("enterprise-attack", name, obj_type, rel_type, attack_id, version, inheritance)
# Generate Mobile base layer
- mobile_layer = build_base_layer("mobile-attack", name, obj_type, attack_id, version, inheritance)
+ mobile_layer = build_base_layer("mobile-attack", name, obj_type, rel_type, attack_id, version, inheritance)
# Generate ICS base layer
- ics_layer = build_base_layer("ics-attack", name, obj_type, attack_id, version, inheritance)
+ ics_layer = build_base_layer("ics-attack", name, obj_type, rel_type, attack_id, version, inheritance)
# Add technique data to layer
for technique in techniques_used:
@@ -584,12 +584,12 @@ def get_navigator_layers(name, attack_id, obj_type, version, techniques_used, in
return layers
-def build_base_layer(domain, object_name, object_type, attack_id, version, inheritance=False):
+def build_base_layer(domain, object_name, object_type, rel_type, attack_id, version, inheritance=False):
"""Build the base Navigator layer for the given object."""
layer = {}
# Layer description
- layer["description"] = f"{domain_name_map[domain]} techniques used by {object_name}, ATT&CK {object_type} {attack_id}"
+ layer["description"] = f"{domain_name_map[domain]} techniques {rel_type} {object_name}, ATT&CK {object_type} {attack_id}"
if version:
# Add object version number if it exists
layer["description"] += f" (v{version})"
@@ -616,14 +616,14 @@ def build_base_layer(domain, object_name, object_type, attack_id, version, inher
# Layer legend
layer["legendItems"] = [
- {"label": f"used by {object_name}", "color": colorMap[1]}
+ {"label": f"{rel_type} {object_name}", "color": colorMap[1]}
]
# Add campaign inheritance to legend, if applicable
if inheritance:
layer["legendItems"].extend([
- {"label": f"used by a campaign attributed to {object_name}", "color": colorMap[2]},
- {"label": f"used by {object_name} and used by a campaign attributed to {object_name}", "color": colorMap[3]}
+ {"label": f"{rel_type} a campaign attributed to {object_name}", "color": colorMap[2]},
+ {"label": f"{rel_type} {object_name} and {rel_type} a campaign attributed to {object_name}", "color": colorMap[3]}
])
return layer
diff --git a/modules/util/relationshipgetters.py b/modules/util/relationshipgetters.py
index 8cd981f9a56..ff92451a1a0 100644
--- a/modules/util/relationshipgetters.py
+++ b/modules/util/relationshipgetters.py
@@ -11,6 +11,7 @@
techniques_used_by_tools = {}
techniques_used_by_groups = {}
techniques_used_by_campaigns = {}
+techniques_targeting_assets = {}
techniques_detected_by_datacomponent = {}
groups_using_tool = {}
groups_using_malware = {}
@@ -20,6 +21,7 @@
tools_using_technique = {}
malware_using_technique = {}
groups_using_technique = {}
+assets_targeted_by_techniques = {}
campaigns_using_technique = {}
campaigns_using_tool = {}
campaigns_using_malware = {}
@@ -44,6 +46,7 @@
datacomponent_list = []
mitigation_list = []
campaign_list = []
+asset_list = []
technique_to_domain = {}
@@ -130,6 +133,25 @@ def get_techniques_used_by_campaigns():
return techniques_used_by_campaigns
+def get_techniques_targeting_assets():
+ """techniques targeting assets getter"""
+ global techniques_targeting_assets
+
+ if not techniques_targeting_assets:
+ techniques_targeting_assets = rsh.techniques_targeting_assets(get_srcs())
+
+ return techniques_targeting_assets
+
+
+def get_assets_targeted_by_techniques():
+ """assets targeted by techniques getter"""
+ global assets_targeted_by_techniques
+
+ if not assets_targeted_by_techniques:
+ assets_targeted_by_techniques = rsh.assets_targeted_by_techniques(get_srcs())
+
+ return assets_targeted_by_techniques
+
def get_techniques_detected_by_datacomponent():
global techniques_detected_by_datacomponent
@@ -431,6 +453,15 @@ def get_campaign_list():
return campaign_list
+def get_asset_list():
+ """asset list getter"""
+ global asset_list
+
+ if not asset_list:
+ asset_list = get_resources()["assets"]
+
+ return asset_list
+
def get_technique_to_domain():
"""technique to domain getter"""
diff --git a/modules/util/relationshiphelpers.py b/modules/util/relationshiphelpers.py
index 13f6ab734e1..3a8d42db620 100644
--- a/modules/util/relationshiphelpers.py
+++ b/modules/util/relationshiphelpers.py
@@ -247,6 +247,21 @@ def campaigns_attributed_to_group(srcs):
return get_related(srcs, "campaign", "attributed-to", "intrusion-set", reverse=True)
+# technique:asset
+def techniques_targeting_assets(srcs):
+ """Return asset_id => {technique, relationship} for each technique targeting the asset.
+
+ srcs should be an array of memorystores for enterprise, mobile, and pre
+ """
+ return get_related(srcs, "attack-pattern", "targets", "x-mitre-asset", reverse=True)
+
+def assets_targeted_by_techniques(srcs):
+ """Return technique_id => {asset, relationship} for each asset targeted by the technique.
+
+ srcs should be an array of memorystores for enterprise, mobile, and pre
+ """
+ return get_related(srcs, "attack-pattern", "targets", "x-mitre-asset")
+
# technique:malware
def techniques_used_by_malware(srcs):
"""Return malware => {technique, relationship} for each technique used by the malware.
diff --git a/modules/util/stixhelpers.py b/modules/util/stixhelpers.py
index 3925b04fdfa..165b4ac6388 100644
--- a/modules/util/stixhelpers.py
+++ b/modules/util/stixhelpers.py
@@ -359,6 +359,9 @@ def get_domain_resources(types):
# Generates list of campaigns
campaign_list = get_domain_resources(["campaign"])
+ # Generates list of assets
+ asset_list = get_domain_resources(["x-mitre-asset"])
+
# Generates list of relationships
rel_list = []
for domain in site_config.domains:
@@ -377,6 +380,7 @@ def get_domain_resources(types):
"techniques": tech_list,
"mitigations": coa_list,
"campaigns": campaign_list,
+ "assets": asset_list
}
return resources
@@ -430,6 +434,7 @@ def get_contributors(ms):
"x-mitre-data-component",
"x-mitre-data-source",
"x-mitre-tactic",
+ "x-mitre-asset",
]
src = ms[domain["name"]]
obj_list = []
diff --git a/modules/website_build/website_build.py b/modules/website_build/website_build.py
index 1270d672d3c..a4ef579bfe4 100644
--- a/modules/website_build/website_build.py
+++ b/modules/website_build/website_build.py
@@ -168,6 +168,7 @@ def generate_index_page():
"groups": "Group",
"software": "Software",
"campaigns": "Campaign",
+ "assets": "Asset",
}
routes = {}
diff --git a/update-attack.py b/update-attack.py
index 6bf29a89952..4b4fc048bdb 100644
--- a/update-attack.py
+++ b/update-attack.py
@@ -22,6 +22,7 @@
"tactics",
"techniques",
"campaigns",
+ "assets",
"tour",
"website_build",
"random_page",