diff --git a/CLI.md b/CLI.md index fbfa52c6c4f..19cbb0e2b98 100644 --- a/CLI.md +++ b/CLI.md @@ -642,6 +642,27 @@ rules. This is based on the hash of the rule in the following format: As a result, all cases where rules are shown or converted to JSON are not just simple conversions from TOML. + +## Multi-version threat mappings (MITRE ATT&CK v18 / v19) + +Rules can carry more than one ATT&CK mapping at once. The `threat` field holds the baseline mapping +(MITRE ATT&CK v18) that ships to Kibana, while an optional `threat_mappings` field holds additional +version-tagged mappings (e.g. v19). At build time exactly one mapping is emitted as the API `threat` +(default: the baseline), selected via the `threat_mapping_framework` / `threat_mapping_version` config +keys or the `DR_THREAT_MAPPING_FRAMEWORK` / `DR_THREAT_MAPPING_VERSION` environment variables; +`threat_mappings` is always stripped from the shipped artifact. + +Generate a target-version mapping from a rule's existing mapping with +`dev attack convert-threat-mappings` (accuracy-first: anything not present in the mapping config is +dropped, never guessed), and scaffold a mapping config with `dev attack scaffold-version-map`. The +feature is DaC-aware. See [docs-dev/multi-version-threat-mappings.md](docs-dev/multi-version-threat-mappings.md) +for the full guide. + +```bash +# preview v18 -> v19 conversion without writing +python -m detection_rules dev attack convert-threat-mappings -t 19 --dry-run +``` + ## Debugging Most of the CLI errors will print a concise, user friendly error. To enable debug mode and see full error stacktraces, diff --git a/detection_rules/attack.py b/detection_rules/attack.py index 71725ffe503..0a7761a6599 100644 --- a/detection_rules/attack.py +++ b/detection_rules/attack.py @@ -6,13 +6,16 @@ """Mitre attack info.""" import json +import os import re import time from collections import OrderedDict +from dataclasses import dataclass, field from pathlib import Path from typing import Any import requests +import yaml from semver import Version from .utils import cached, clear_caches, get_etc_glob_path, get_etc_path, gzip_compress, read_gzip @@ -20,6 +23,7 @@ PLATFORMS = ["Windows", "macOS", "Linux"] CROSSWALK_FILE = get_etc_path(["attack-crosswalk.json"]) TECHNIQUES_REDIRECT_FILE = get_etc_path(["attack-technique-redirects.json"]) +ATTACK_VERSION_MAPS_DIRNAME = "attack-version-maps" tactics_map: dict[str, Any] = {} @@ -29,14 +33,30 @@ def load_techniques_redirect() -> dict[str, Any]: return json.loads(TECHNIQUES_REDIRECT_FILE.read_text())["mapping"] +def _attack_file_version(path: Path) -> Version: + """Extract the semver from an ATT&CK gz filename for sorting.""" + ver = path.name.split("-v", 1)[1][: -len(".json.gz")] + return Version.parse(ver, optional_minor_and_patch=True) + + def get_attack_file_path() -> Path: + """Return the baseline ATT&CK data file (lowest available version).""" pattern = "attack-v*.json.gz" - attack_file = get_etc_glob_path([pattern]) - if len(attack_file) < 1: + attack_files = get_etc_glob_path([pattern]) + if not attack_files: raise FileNotFoundError(f"Missing required {pattern} file") - if len(attack_file) != 1: - raise FileExistsError(f"Multiple files found with {pattern} pattern. Only one is allowed") - return Path(attack_file[0]) + return min(attack_files, key=_attack_file_version) + + +def get_attack_file_path_for_version(version: str) -> Path: + """Return the ATT&CK data file whose major version matches ``version``.""" + major = version.split(".", maxsplit=1)[0] + attack_files = get_etc_glob_path(["attack-v*.json.gz"]) + for path in sorted(attack_files, key=_attack_file_version, reverse=True): + if path.name.split("-v", 1)[1][: -len(".json.gz")].split(".")[0] == major: + return path + available = [p.name.split("-v", 1)[1][: -len(".json.gz")] for p in attack_files] + raise FileNotFoundError(f"No ATT&CK data file found for version {version!r}. Available: {available}") _, _attack_path_base = str(get_attack_file_path()).split("-v") @@ -98,6 +118,76 @@ def load_attack_gz() -> dict[str, Any]: technique_id_list = [t for t in technique_lookup if "." not in t] sub_technique_id_list = [t for t in technique_lookup if "." in t] +# Index of all ATT&CK gz files present in etc/, keyed by full version string (e.g. "18.1.0", "19.1"). +AVAILABLE_ATTACK_VERSIONS: dict[str, Path] = { + p.name.split("-v", 1)[1][: -len(".json.gz")]: p for p in get_etc_glob_path(["attack-v*.json.gz"]) +} + + +@dataclass +class AttackLookups: + """Pre-built ATT&CK lookup structures for a specific version.""" + + version: str + tactics_map: dict[str, str] + technique_lookup: OrderedDict # type: ignore[type-arg] + revoked: dict[str, Any] + deprecated: dict[str, Any] + matrix: dict[str, list[str]] + + +def _build_lookups(version: str, raw: dict[str, Any]) -> AttackLookups: + """Build ATT&CK lookup structures from raw STIX data.""" + _tactics_map: dict[str, str] = {} + _technique_lookup: dict[str, Any] = {} + _revoked: dict[str, Any] = {} + _deprecated: dict[str, Any] = {} + + for item in raw["objects"]: + if item["type"] == "x-mitre-tactic": + _tactics_map[item["name"]] = item["external_references"][0]["external_id"] + if item["type"] == "attack-pattern" and item["external_references"][0]["source_name"] == "mitre-attack": + tid = item["external_references"][0]["external_id"] + _technique_lookup[tid] = item + if item.get("revoked"): + _revoked[tid] = item + if item.get("x_mitre_deprecated"): + _deprecated[tid] = item + + _tactics_list = list(_tactics_map) + _matrix: dict[str, list[str]] = {t: [] for t in _tactics_list} + for tid, technique in sorted(_technique_lookup.items(), key=lambda kv: kv[1]["name"].lower()): + kill_chain = technique.get("kill_chain_phases") + if kill_chain: + for phase in kill_chain: + if phase["kill_chain_name"] != "mitre-attack": + continue + tactic_name = next( + (t for t in _tactics_list if t.lower() == phase["phase_name"].replace("-", " ")), + None, + ) + if tactic_name: + _matrix[tactic_name].append(tid) + for val in _matrix.values(): + val.sort(key=lambda t: _technique_lookup[t]["name"].lower()) + + return AttackLookups( + version=version, + tactics_map=_tactics_map, + technique_lookup=OrderedDict(sorted(_technique_lookup.items())), + revoked=dict(sorted(_revoked.items())), + deprecated=dict(sorted(_deprecated.items())), + matrix=_matrix, + ) + + +@cached +def build_attack_lookups_for_version(version: str) -> AttackLookups: + """Load and cache ATT&CK lookup structures for a specific version.""" + path = get_attack_file_path_for_version(version) + raw = json.loads(read_gzip(path)) + return _build_lookups(version, raw) + def refresh_attack_data(save: bool = True) -> tuple[dict[str, Any] | None, bytes | None]: """Refresh ATT&CK data from Mitre.""" @@ -248,3 +338,202 @@ def refresh_redirected_techniques_map(threads: int = 50) -> None: def load_crosswalk_map() -> dict[str, Any]: """Retrieve the replacement mapping.""" return json.loads(CROSSWALK_FILE.read_text())["mapping"] + + +# Multi-version threat mapping support. Generation of a target-version mapping from a source mapping +# is driven by a curated, self-contained config (see etc/attack-version-maps/); ids absent from the +# config (or mapped to null) are dropped, so generation is accuracy-first and never invents a mapping. + +# kinds of threat entry that can be remapped, with the corresponding config table attribute +_MAP_KIND_ATTRS: dict[str, str] = { + "tactic": "tactics", + "technique": "techniques", + "subtechnique": "subtechniques", +} +_REQUIRED_MAP_KEYS = ("framework", "source_version", "target_version") + + +@dataclass +class AttackVersionMap: + """A curated source->target ATT&CK (or other framework) mapping config.""" + + framework: str + source_version: str + target_version: str + path: Path + tactics: dict[str, dict[str, str] | None] = field(default_factory=dict) # type: ignore[reportUnknownVariableType] + techniques: dict[str, dict[str, str] | None] = field(default_factory=dict) # type: ignore[reportUnknownVariableType] + subtechniques: dict[str, dict[str, str] | None] = field(default_factory=dict) # type: ignore[reportUnknownVariableType] + + @property + def key(self) -> tuple[str, str, str]: + return (self.framework, str(self.source_version), str(self.target_version)) + + def _table(self, kind: str) -> dict[str, dict[str, str] | None]: + attr = _MAP_KIND_ATTRS.get(kind) + if attr is None: + raise ValueError(f"Unknown threat entry kind: {kind}") + return getattr(self, attr) + + def is_mapped(self, kind: str, source_id: str) -> bool: + """Whether the source id has an explicit, non-null destination in the config.""" + return self._table(kind).get(source_id) is not None + + def lookup(self, kind: str, source_id: str) -> dict[str, str] | None: + """Return the destination entry for a source id, or ``None`` if dropped/absent.""" + return self._table(kind).get(source_id) + + +def parse_attack_version_map(path: Path) -> AttackVersionMap: + """Parse a single attack version mapping config file.""" + raw: dict[str, Any] = yaml.safe_load(path.read_text()) or {} + missing = [k for k in _REQUIRED_MAP_KEYS if not raw.get(k)] + if missing: + raise ValueError(f"Attack version map {path} is missing required keys: {missing}") + + def _validate_table(table: dict[str, Any], kind: str) -> dict[str, dict[str, str] | None]: + for source_id, dest in table.items(): + if dest is None: + continue + if not isinstance(dest, dict) or not all(k in dest for k in ("id", "name", "reference")): + raise ValueError( + f"Attack version map {path}: {kind} entry '{source_id}' must map to null or an " + f"object with 'id', 'name', and 'reference' (got: {dest!r})" + ) + return table + + return AttackVersionMap( + framework=str(raw["framework"]), + source_version=str(raw["source_version"]), + target_version=str(raw["target_version"]), + path=path, + tactics=_validate_table(raw.get("tactics") or {}, "tactics"), + techniques=_validate_table(raw.get("techniques") or {}, "techniques"), + subtechniques=_validate_table(raw.get("subtechniques") or {}, "subtechniques"), + ) + + +def load_attack_version_maps(paths: list[Path]) -> dict[tuple[str, str, str], AttackVersionMap]: + """Load mapping configs from explicit files and/or directories, indexed by (framework, src, tgt).""" + files: list[Path] = [] + for p in paths: + if p.is_dir(): + files.extend(sorted(set(p.glob("*.yaml")) | set(p.glob("*.yml")))) + elif p.exists(): + files.append(p) + else: + raise FileNotFoundError(f"Attack version map path not found: {p}") + + maps: dict[tuple[str, str, str], AttackVersionMap] = {} + for f in files: + parsed = parse_attack_version_map(f) + if parsed.key in maps: + raise ValueError(f"Duplicate attack version map for {parsed.key}: {f} and {maps[parsed.key].path}") + maps[parsed.key] = parsed + return maps + + +def get_attack_version_map( + framework: str, + source_version: str, + target_version: str, + paths: list[Path] | None = None, +) -> AttackVersionMap: + """Return the mapping config matching the (framework, source, target) triple.""" + if paths is None: + from .config import parse_rules_config + + cfg_dir = parse_rules_config().attack_version_maps_dir + if not cfg_dir: + raise FileNotFoundError( + "No attack version maps directory configured (set `attack_version_maps_dir` in " + "_config.yaml) and no explicit config path was provided." + ) + paths = [cfg_dir] + + maps = load_attack_version_maps(paths) + key = (framework, str(source_version), str(target_version)) + if key not in maps: + available = sorted(maps) + raise ValueError( + f"No attack version map found for framework={framework!r} {source_version}->{target_version}. " + f"Available: {available}" + ) + return maps[key] + + +def build_identity_version_map(framework: str, source_version: str, target_version: str) -> dict[str, Any]: + """Build a cross-version identity skeleton using source IDs and target-version names.""" + # Source data: determines which IDs appear as source keys. + try: + src = build_attack_lookups_for_version(source_version) + _src_tactics_map = src.tactics_map + _src_technique_lookup = src.technique_lookup + _src_revoked = src.revoked + _src_deprecated = src.deprecated + except FileNotFoundError: + _src_tactics_map = tactics_map + _src_technique_lookup = technique_lookup + _src_revoked = revoked + _src_deprecated = deprecated + + # Target data: resolves destination names/references where the same ID exists. + try: + tgt = build_attack_lookups_for_version(target_version) + _tgt_tactic_id_to_name: dict[str, str] = {v: k for k, v in tgt.tactics_map.items()} + _tgt_technique_lookup: dict[str, Any] = dict(tgt.technique_lookup) + except FileNotFoundError: + _tgt_tactic_id_to_name = {} + _tgt_technique_lookup = {} + + url_base = "https://attack.mitre.org/{type}/{id}/" + + # Tactics: source IDs as keys, target name if the ID still exists in the target version. + out_tactics: dict[str, dict[str, str]] = { + tactic_id: { + "id": tactic_id, + "name": _tgt_tactic_id_to_name.get(tactic_id, name), + "reference": url_base.format(type="tactics", id=tactic_id), + } + for name, tactic_id in _src_tactics_map.items() + } + + # Techniques and subtechniques: source IDs as keys, target name if available. + out_techniques: dict[str, dict[str, str]] = {} + out_subtechniques: dict[str, dict[str, str]] = {} + for technique_id, item in _src_technique_lookup.items(): + if technique_id in _src_revoked or technique_id in _src_deprecated: + continue + tgt_item = _tgt_technique_lookup.get(technique_id, item) + entry: dict[str, str] = { + "id": technique_id, + "name": tgt_item["name"], + "reference": url_base.format(type="techniques", id=technique_id.replace(".", "/")), + } + if "." in technique_id: + out_subtechniques[technique_id] = entry + else: + out_techniques[technique_id] = entry + + return { + "framework": framework, + "source_version": source_version, + "target_version": target_version, + "tactics": dict(sorted(out_tactics.items())), + "techniques": dict(sorted(out_techniques.items())), + "subtechniques": dict(sorted(out_subtechniques.items())), + } + + +def resolve_output_threat_version() -> tuple[str, str]: + """Resolve which (framework, version) threat mapping should be emitted as the API `threat`.""" + from .config import ( + THREAT_MAPPING_FRAMEWORK_ENV, + THREAT_MAPPING_VERSION_ENV, + parse_rules_config, + ) + + cfg = parse_rules_config() + framework = os.getenv(THREAT_MAPPING_FRAMEWORK_ENV, cfg.threat_mapping_framework) + version = str(os.getenv(THREAT_MAPPING_VERSION_ENV, cfg.threat_mapping_version)) + return framework, version diff --git a/detection_rules/cli_utils.py b/detection_rules/cli_utils.py index eeadcf158a3..7248237638a 100644 --- a/detection_rules/cli_utils.py +++ b/detection_rules/cli_utils.py @@ -215,15 +215,18 @@ def get_collection(*args: Any, **kwargs: Any) -> Any: # Warn that if the path does not match the expected path, it will be saved to the expected path for rule in rules: - threat = rule.contents.data.get("threat") - first_tactic = threat[0].tactic.name if threat else "" # Check if flag or config is set to not include tactic in the filename no_tactic_filename = no_tactic_filename or RULES_CONFIG.no_tactic_filename - tactic_name = None if no_tactic_filename else first_tactic - rule_name = rulename_to_filename(rule.contents.data.name, tactic_name=tactic_name) + # Accept a filename built from the baseline tactic or any versioned (e.g. v19) threat_mappings + # tactic, since a rule may still use its older tactic name in the filename until it's renamed. + tactic_names = [] if no_tactic_filename else rule.contents.data.get_primary_tactic_names() + candidate_names = [rulename_to_filename(rule.contents.data.name, tactic_name=t) for t in tactic_names] + if not candidate_names: + candidate_names = [rulename_to_filename(rule.contents.data.name, tactic_name=None)] + rule_name = candidate_names[0] if not rule.path: click.secho(f"WARNING: Rule path for rule not found: {rule_name}", fg="yellow") - elif rule.path.name != rule_name: + elif rule.path.name not in candidate_names: click.secho( f"WARNING: Rule path does not match required path: {rule.path.name} != {rule_name}", fg="yellow" ) diff --git a/detection_rules/config.py b/detection_rules/config.py index c03d2837041..3493283981b 100644 --- a/detection_rules/config.py +++ b/detection_rules/config.py @@ -27,6 +27,15 @@ ROOT_DIR = Path(__file__).parent.parent CUSTOM_RULES_DIR = os.getenv("CUSTOM_RULES_DIR", None) +# Output threat-mapping selection (which framework/version is emitted as the API `threat`). +# Defaults to MITRE ATT&CK v18 (the value stored in the rule `threat` field). Either may be +# overridden via _config.yaml (`threat_mapping_framework` / `threat_mapping_version`) or the +# environment variables below, with the environment taking precedence. +DEFAULT_THREAT_MAPPING_FRAMEWORK = "MITRE ATT&CK" +DEFAULT_THREAT_MAPPING_VERSION = "18" +THREAT_MAPPING_FRAMEWORK_ENV = "DR_THREAT_MAPPING_FRAMEWORK" +THREAT_MAPPING_VERSION_ENV = "DR_THREAT_MAPPING_VERSION" + @dataclass class UnitTest: @@ -208,7 +217,10 @@ class RulesConfig: action_dir: Path | None = None action_connector_dir: Path | None = None + attack_version_maps_dir: Path | None = None auto_gen_schema_file: Path | None = None + threat_mapping_framework: str = DEFAULT_THREAT_MAPPING_FRAMEWORK + threat_mapping_version: str = DEFAULT_THREAT_MAPPING_VERSION bbr_rules_dirs: list[Path] = field(default_factory=list) # type: ignore[reportUnknownVariableType] bypass_version_lock: bool = False exception_dir: Path | None = None @@ -355,6 +367,29 @@ def parse_rules_config(path: Path | None = None) -> RulesConfig: # noqa: PLR091 # no_tactic_filename contents["no_tactic_filename"] = loaded.get("no_tactic_filename", False) + # attack version mapping configs directory (used by `dev attack convert-threat-mappings`). + # Defaults to the stock etc/attack-version-maps directory; custom rules repos may point this at + # their own directory of per-pair mapping configs. + attack_maps_dir = loaded.get("attack_version_maps_dir") + if attack_maps_dir: + contents["attack_version_maps_dir"] = base_dir.joinpath(attack_maps_dir).resolve() + else: + default_maps_dir = Path(get_etc_path(["attack-version-maps"])) + contents["attack_version_maps_dir"] = default_maps_dir if default_maps_dir.exists() else None + + # threat-mapping output selection (which framework/version is emitted as the API `threat`). + # Environment variables take precedence over the config file values. + contents["threat_mapping_framework"] = os.getenv( + THREAT_MAPPING_FRAMEWORK_ENV, + loaded.get("threat_mapping_framework", DEFAULT_THREAT_MAPPING_FRAMEWORK), + ) + contents["threat_mapping_version"] = str( + os.getenv( + THREAT_MAPPING_VERSION_ENV, + loaded.get("threat_mapping_version", DEFAULT_THREAT_MAPPING_VERSION), + ) + ) + # return the config try: rules_config = RulesConfig(test_config=test_config, **contents) # type: ignore[reportArgumentType] diff --git a/detection_rules/devtools.py b/detection_rules/devtools.py index 26b6fdba4e2..1305147427b 100644 --- a/detection_rules/devtools.py +++ b/detection_rules/devtools.py @@ -35,8 +35,12 @@ from . import attack, rule_loader, utils from .beats import download_beats_schema, download_latest_beats_schema, refresh_main_schema -from .cli_utils import single_collection -from .config import parse_rules_config +from .cli_utils import multi_collection, single_collection +from .config import ( + DEFAULT_THREAT_MAPPING_FRAMEWORK, + DEFAULT_THREAT_MAPPING_VERSION, + parse_rules_config, +) from .docs import REPO_DOCS_DIR, IntegrationSecurityDocs, IntegrationSecurityDocsMDX from .ecs import download_endpoint_schemas, download_schemas from .endgame import EndgameSchemaManager @@ -71,6 +75,7 @@ ThreatMapping, TOMLRule, TOMLRuleContents, + VersionedThreatMapping, ) from .rule_loader import RuleCollection, production_filter from .rule_validators import ESQLValidator @@ -1814,6 +1819,214 @@ def update_attack_in_rules() -> list[TOMLRule]: return new_rules +def _remap_threat_entry( + entry: ThreatMapping, + vmap: "attack.AttackVersionMap", + framework: str, + dropped: list[str], +) -> dict[str, Any] | None: + """Build a target-version threat entry from a source entry using the mapping config.""" + tactic_dest = vmap.lookup("tactic", entry.tactic.id) + if tactic_dest is None: + dropped.append(f"tactic {entry.tactic.id} ({entry.tactic.name})") + return None + + techniques: list[dict[str, Any]] = [] + for technique in entry.technique or []: + tech_dest = vmap.lookup("technique", technique.id) + if tech_dest is None: + dropped.append(f"technique {technique.id} ({technique.name})") + continue + + new_technique: dict[str, Any] = { + "id": tech_dest["id"], + "name": tech_dest["name"], + "reference": tech_dest["reference"], + } + subtechniques: list[dict[str, Any]] = [] + for sub in technique.subtechnique or []: + sub_dest = vmap.lookup("subtechnique", sub.id) + if sub_dest is None: + dropped.append(f"subtechnique {sub.id} ({sub.name})") + continue + subtechniques.append({"id": sub_dest["id"], "name": sub_dest["name"], "reference": sub_dest["reference"]}) + if subtechniques: + new_technique["subtechnique"] = subtechniques + techniques.append(new_technique) + + new_entry: dict[str, Any] = { + "framework": framework, + "tactic": {"id": tactic_dest["id"], "name": tactic_dest["name"], "reference": tactic_dest["reference"]}, + } + if techniques: + new_entry["technique"] = techniques + return new_entry + + +def _get_source_threat(rule: TOMLRule, framework: str, source_version: str) -> list[ThreatMapping] | None: + """Get the source threat mapping for the given framework/version.""" + if framework == DEFAULT_THREAT_MAPPING_FRAMEWORK and str(source_version) == str(DEFAULT_THREAT_MAPPING_VERSION): + return rule.contents.data.threat + for block in rule.contents.data.threat_mappings or []: + if block.framework == framework and str(block.version) == str(source_version): + return block.threat + return None + + +@attack_group.command("convert-threat-mappings") +@click.option("--target-version", "-t", required=True, help="Target framework version to generate (e.g. 19)") +@click.option( + "--source-version", + "-s", + default=DEFAULT_THREAT_MAPPING_VERSION, + show_default=True, + help="Source version to convert from (the version stored in the rule `threat` field by default)", +) +@click.option( + "--framework", + default=DEFAULT_THREAT_MAPPING_FRAMEWORK, + show_default=True, + help="Threat framework to convert", +) +@click.option( + "--config", + "config_paths", + multiple=True, + type=Path, + help="Explicit mapping config file(s) or directory(ies). Defaults to the configured `attack_version_maps_dir`.", +) +@click.option("--dry-run", is_flag=True, help="Report what would change without writing any files") +@click.option( + "--overwrite/--no-overwrite", + default=True, + show_default=True, + help="Replace an existing target-version block on a rule (otherwise the rule is skipped)", +) +@multi_collection +def convert_threat_mappings( # noqa: PLR0913 + rules: RuleCollection, + target_version: str, + source_version: str, + framework: str, + config_paths: tuple[Path, ...], + dry_run: bool, + overwrite: bool, +) -> list[TOMLRule]: + """Generate a target-version threat mapping from a rule's source mapping (accuracy-first).""" + if framework == DEFAULT_THREAT_MAPPING_FRAMEWORK and str(target_version) == str(DEFAULT_THREAT_MAPPING_VERSION): + raise click.BadParameter( + f"--target-version {target_version} is the baseline stored in the `threat` field; " + "generating a versioned block for it would duplicate `threat`." + ) + + try: + vmap = attack.get_attack_version_map(framework, source_version, target_version, list(config_paths) or None) + except (FileNotFoundError, ValueError) as exc: + raise click.ClickException(str(exc)) from exc + + click.echo( + f"Converting {framework} {source_version} -> {target_version} using config: {vmap.path}\n" + f"({len(rules)} rules in scope; dry-run={dry_run})\n" + ) + + today = time.strftime("%Y/%m/%d") + updated_rules: list[TOMLRule] = [] + skipped_no_source = 0 + skipped_existing = 0 + skipped_empty = 0 + + for rule in rules.rules: + source_threat = _get_source_threat(rule, framework, source_version) + if not source_threat: + skipped_no_source += 1 + continue + + existing_blocks = list(rule.contents.data.threat_mappings or []) + has_target = any(b.framework == framework and str(b.version) == str(target_version) for b in existing_blocks) + if has_target and not overwrite: + skipped_existing += 1 + continue + + dropped: list[str] = [] + threat_dicts = [ + converted + for converted in (_remap_threat_entry(entry, vmap, framework, dropped) for entry in source_threat) + if converted + ] + + if not threat_dicts: + skipped_empty += 1 + click.secho( + f" [skip] {rule.name}: no confident {target_version} mapping (dropped: {', '.join(dropped)})", + fg="yellow", + ) + continue + + block = VersionedThreatMapping.from_dict( + {"framework": framework, "version": str(target_version), "threat": threat_dicts} + ) + remaining = [ + b for b in existing_blocks if not (b.framework == framework and str(b.version) == str(target_version)) + ] + new_blocks = sorted([*remaining, block], key=lambda b: (b.framework, b.version)) + + click.echo(f" [ok] {rule.name}: generated {target_version} mapping ({len(threat_dicts)} tactic(s))") + if dropped: + click.secho(f" dropped (not in config): {', '.join(dropped)}", fg="yellow") + + if not dry_run: + new_meta = dataclasses.replace(rule.contents.metadata, updated_date=today) + new_data = dataclasses.replace(rule.contents.data, threat_mappings=new_blocks) + new_contents = dataclasses.replace(rule.contents, data=new_data, metadata=new_meta) + new_rule = TOMLRule(contents=new_contents, path=rule.path) + new_rule.save_toml() + updated_rules.append(rule) + + click.echo( + f"\nFinished - {len(updated_rules)} rule(s) " + f"{'would be ' if dry_run else ''}updated; " + f"{skipped_no_source} without a {source_version} source mapping, " + f"{skipped_existing} with an existing {target_version} block (use --overwrite), " + f"{skipped_empty} with no confident mapping." + ) + return updated_rules + + +@attack_group.command("scaffold-version-map") +@click.option("--target-version", "-t", required=True, help="Target framework version (e.g. 19)") +@click.option( + "--source-version", + "-s", + default=DEFAULT_THREAT_MAPPING_VERSION, + show_default=True, + help="Source version (the baseline stored in the rule `threat` field)", +) +@click.option("--framework", default=DEFAULT_THREAT_MAPPING_FRAMEWORK, show_default=True, help="Threat framework") +@click.option("--output", "-o", type=Path, required=True, help="Path to write the mapping config YAML to") +def scaffold_version_map(target_version: str, source_version: str, framework: str, output: Path) -> Path: + """Generate an identity mapping-config skeleton from the loaded ATT&CK data for human curation.""" + skeleton = attack.build_identity_version_map(framework, source_version, target_version) + header_lines = [ + f"# Auto-generated identity mapping skeleton ({framework} {source_version} to {target_version}).", + "# REVIEW REQUIRED: every id currently maps to itself with its source-version name/reference.", + "# Curate against the target version's real changes before relying on this config:", + "# renamed -> change the entry 'name'", + "# deprecated -> map the entry to a null value, which drops it during conversion", + "# split/merged -> point the source id at the chosen target id/name/reference", + "# Entries absent from this file are dropped during conversion (accuracy-first).", + "", + ] + header = "\n".join(header_lines) + output.parent.mkdir(parents=True, exist_ok=True) + _ = output.write_text(header + yaml.safe_dump(skeleton, sort_keys=False, default_flow_style=False)) + click.echo( + f"Wrote {framework} {source_version}->{target_version} skeleton to {output} " + f"({len(skeleton['tactics'])} tactics, {len(skeleton['techniques'])} techniques, " + f"{len(skeleton['subtechniques'])} subtechniques)." + ) + return output + + @dev_group.group("transforms") def transforms_group() -> None: """Commands for managing TOML [transform].""" diff --git a/detection_rules/etc/_config.yaml b/detection_rules/etc/_config.yaml index 2cb9c1441a2..de43c7edbf0 100644 --- a/detection_rules/etc/_config.yaml +++ b/detection_rules/etc/_config.yaml @@ -88,4 +88,19 @@ normalize_kql_keywords: False # To prevent the tactic prefix from being added to the rule filename, set the line below to True # This config line can be used instead of specifying the `--no-tactic-filename` flag in the CLI # Mind that for unit tests, you also want to disable the filename test in the test_config.yaml -# no_tactic_filename: True \ No newline at end of file +# no_tactic_filename: True + +# Multi-version threat mappings (e.g. MITRE ATT&CK v18/v19) +# --------------------------------------------------------- +# Rules may carry additional framework/version-tagged threat mappings in the `threat_mappings` field +# alongside the default `threat` field. At build time exactly one mapping is emitted as the API +# `threat`; by default that is the baseline stored in `threat` (MITRE ATT&CK v18). Override which +# framework/version is emitted with the keys below (or the DR_THREAT_MAPPING_FRAMEWORK / +# DR_THREAT_MAPPING_VERSION environment variables, which take precedence): +# threat_mapping_framework: "MITRE ATT&CK" +# threat_mapping_version: "18" +# +# Directory of per-pair mapping configs used by `dev attack convert-threat-mappings`. Defaults to the +# stock detection_rules/etc/attack-version-maps directory; point this at your own directory to ship +# custom source->target mappings with a custom rules repo (paths are relative to this config file). +# attack_version_maps_dir: etc/attack-version-maps \ No newline at end of file diff --git a/detection_rules/etc/attack-v19.1.json.gz b/detection_rules/etc/attack-v19.1.json.gz new file mode 100644 index 00000000000..37b2632aa99 Binary files /dev/null and b/detection_rules/etc/attack-v19.1.json.gz differ diff --git a/detection_rules/etc/attack-version-maps/attack_v18_to_v19.yaml b/detection_rules/etc/attack-version-maps/attack_v18_to_v19.yaml new file mode 100644 index 00000000000..c2a482c6624 --- /dev/null +++ b/detection_rules/etc/attack-version-maps/attack_v18_to_v19.yaml @@ -0,0 +1,2833 @@ +# Auto-generated identity mapping skeleton (MITRE ATT&CK 18 to 19). +# REVIEW REQUIRED: every id currently maps to itself with its source-version name/reference. +# Curate against the target version's real changes before relying on this config: +# renamed -> change the entry 'name' +# deprecated -> map the entry to a null value, which drops it during conversion +# split/merged -> point the source id at the chosen target id/name/reference +# Entries absent from this file are dropped during conversion (accuracy-first). +framework: MITRE ATT&CK +source_version: '18' +target_version: '19' +tactics: + TA0001: + id: TA0001 + name: Initial Access + reference: https://attack.mitre.org/tactics/TA0001/ + TA0002: + id: TA0002 + name: Execution + reference: https://attack.mitre.org/tactics/TA0002/ + TA0003: + id: TA0003 + name: Persistence + reference: https://attack.mitre.org/tactics/TA0003/ + TA0004: + id: TA0004 + name: Privilege Escalation + reference: https://attack.mitre.org/tactics/TA0004/ + TA0005: + id: TA0005 + name: Stealth + reference: https://attack.mitre.org/tactics/TA0005/ + TA0006: + id: TA0006 + name: Credential Access + reference: https://attack.mitre.org/tactics/TA0006/ + TA0007: + id: TA0007 + name: Discovery + reference: https://attack.mitre.org/tactics/TA0007/ + TA0008: + id: TA0008 + name: Lateral Movement + reference: https://attack.mitre.org/tactics/TA0008/ + TA0009: + id: TA0009 + name: Collection + reference: https://attack.mitre.org/tactics/TA0009/ + TA0010: + id: TA0010 + name: Exfiltration + reference: https://attack.mitre.org/tactics/TA0010/ + TA0011: + id: TA0011 + name: Command and Control + reference: https://attack.mitre.org/tactics/TA0011/ + TA0040: + id: TA0040 + name: Impact + reference: https://attack.mitre.org/tactics/TA0040/ + TA0042: + id: TA0042 + name: Resource Development + reference: https://attack.mitre.org/tactics/TA0042/ + TA0043: + id: TA0043 + name: Reconnaissance + reference: https://attack.mitre.org/tactics/TA0043/ +techniques: + T1001: + id: T1001 + name: Data Obfuscation + reference: https://attack.mitre.org/techniques/T1001/ + T1003: + id: T1003 + name: OS Credential Dumping + reference: https://attack.mitre.org/techniques/T1003/ + T1005: + id: T1005 + name: Data from Local System + reference: https://attack.mitre.org/techniques/T1005/ + T1006: + id: T1006 + name: Direct Volume Access + reference: https://attack.mitre.org/techniques/T1006/ + T1007: + id: T1007 + name: System Service Discovery + reference: https://attack.mitre.org/techniques/T1007/ + T1008: + id: T1008 + name: Fallback Channels + reference: https://attack.mitre.org/techniques/T1008/ + T1010: + id: T1010 + name: Application Window Discovery + reference: https://attack.mitre.org/techniques/T1010/ + T1011: + id: T1011 + name: Exfiltration Over Other Network Medium + reference: https://attack.mitre.org/techniques/T1011/ + T1012: + id: T1012 + name: Query Registry + reference: https://attack.mitre.org/techniques/T1012/ + T1014: + id: T1014 + name: Rootkit + reference: https://attack.mitre.org/techniques/T1014/ + T1016: + id: T1016 + name: System Network Configuration Discovery + reference: https://attack.mitre.org/techniques/T1016/ + T1018: + id: T1018 + name: Remote System Discovery + reference: https://attack.mitre.org/techniques/T1018/ + T1020: + id: T1020 + name: Automated Exfiltration + reference: https://attack.mitre.org/techniques/T1020/ + T1021: + id: T1021 + name: Remote Services + reference: https://attack.mitre.org/techniques/T1021/ + T1025: + id: T1025 + name: Data from Removable Media + reference: https://attack.mitre.org/techniques/T1025/ + T1027: + id: T1027 + name: Obfuscated Files or Information + reference: https://attack.mitre.org/techniques/T1027/ + T1029: + id: T1029 + name: Scheduled Transfer + reference: https://attack.mitre.org/techniques/T1029/ + T1030: + id: T1030 + name: Data Transfer Size Limits + reference: https://attack.mitre.org/techniques/T1030/ + T1033: + id: T1033 + name: System Owner/User Discovery + reference: https://attack.mitre.org/techniques/T1033/ + T1036: + id: T1036 + name: Masquerading + reference: https://attack.mitre.org/techniques/T1036/ + T1037: + id: T1037 + name: Boot or Logon Initialization Scripts + reference: https://attack.mitre.org/techniques/T1037/ + T1039: + id: T1039 + name: Data from Network Shared Drive + reference: https://attack.mitre.org/techniques/T1039/ + T1040: + id: T1040 + name: Network Sniffing + reference: https://attack.mitre.org/techniques/T1040/ + T1041: + id: T1041 + name: Exfiltration Over C2 Channel + reference: https://attack.mitre.org/techniques/T1041/ + T1046: + id: T1046 + name: Network Service Discovery + reference: https://attack.mitre.org/techniques/T1046/ + T1047: + id: T1047 + name: Windows Management Instrumentation + reference: https://attack.mitre.org/techniques/T1047/ + T1048: + id: T1048 + name: Exfiltration Over Alternative Protocol + reference: https://attack.mitre.org/techniques/T1048/ + T1049: + id: T1049 + name: System Network Connections Discovery + reference: https://attack.mitre.org/techniques/T1049/ + T1052: + id: T1052 + name: Exfiltration Over Physical Medium + reference: https://attack.mitre.org/techniques/T1052/ + T1053: + id: T1053 + name: Scheduled Task/Job + reference: https://attack.mitre.org/techniques/T1053/ + T1055: + id: T1055 + name: Process Injection + reference: https://attack.mitre.org/techniques/T1055/ + T1056: + id: T1056 + name: Input Capture + reference: https://attack.mitre.org/techniques/T1056/ + T1057: + id: T1057 + name: Process Discovery + reference: https://attack.mitre.org/techniques/T1057/ + T1059: + id: T1059 + name: Command and Scripting Interpreter + reference: https://attack.mitre.org/techniques/T1059/ + T1068: + id: T1068 + name: Exploitation for Privilege Escalation + reference: https://attack.mitre.org/techniques/T1068/ + T1069: + id: T1069 + name: Permission Groups Discovery + reference: https://attack.mitre.org/techniques/T1069/ + T1070: + id: T1070 + name: Indicator Removal + reference: https://attack.mitre.org/techniques/T1070/ + T1071: + id: T1071 + name: Application Layer Protocol + reference: https://attack.mitre.org/techniques/T1071/ + T1072: + id: T1072 + name: Software Deployment Tools + reference: https://attack.mitre.org/techniques/T1072/ + T1074: + id: T1074 + name: Data Staged + reference: https://attack.mitre.org/techniques/T1074/ + T1078: + id: T1078 + name: Valid Accounts + reference: https://attack.mitre.org/techniques/T1078/ + T1080: + id: T1080 + name: Taint Shared Content + reference: https://attack.mitre.org/techniques/T1080/ + T1082: + id: T1082 + name: System Information Discovery + reference: https://attack.mitre.org/techniques/T1082/ + T1083: + id: T1083 + name: File and Directory Discovery + reference: https://attack.mitre.org/techniques/T1083/ + T1087: + id: T1087 + name: Account Discovery + reference: https://attack.mitre.org/techniques/T1087/ + T1090: + id: T1090 + name: Proxy + reference: https://attack.mitre.org/techniques/T1090/ + T1091: + id: T1091 + name: Replication Through Removable Media + reference: https://attack.mitre.org/techniques/T1091/ + T1092: + id: T1092 + name: Communication Through Removable Media + reference: https://attack.mitre.org/techniques/T1092/ + T1095: + id: T1095 + name: Non-Application Layer Protocol + reference: https://attack.mitre.org/techniques/T1095/ + T1098: + id: T1098 + name: Account Manipulation + reference: https://attack.mitre.org/techniques/T1098/ + T1102: + id: T1102 + name: Web Service + reference: https://attack.mitre.org/techniques/T1102/ + T1104: + id: T1104 + name: Multi-Stage Channels + reference: https://attack.mitre.org/techniques/T1104/ + T1105: + id: T1105 + name: Ingress Tool Transfer + reference: https://attack.mitre.org/techniques/T1105/ + T1106: + id: T1106 + name: Native API + reference: https://attack.mitre.org/techniques/T1106/ + T1110: + id: T1110 + name: Brute Force + reference: https://attack.mitre.org/techniques/T1110/ + T1111: + id: T1111 + name: Multi-Factor Authentication Interception + reference: https://attack.mitre.org/techniques/T1111/ + T1112: + id: T1112 + name: Modify Registry + reference: https://attack.mitre.org/techniques/T1112/ + T1113: + id: T1113 + name: Screen Capture + reference: https://attack.mitre.org/techniques/T1113/ + T1114: + id: T1114 + name: Email Collection + reference: https://attack.mitre.org/techniques/T1114/ + T1115: + id: T1115 + name: Clipboard Data + reference: https://attack.mitre.org/techniques/T1115/ + T1119: + id: T1119 + name: Automated Collection + reference: https://attack.mitre.org/techniques/T1119/ + T1120: + id: T1120 + name: Peripheral Device Discovery + reference: https://attack.mitre.org/techniques/T1120/ + T1123: + id: T1123 + name: Audio Capture + reference: https://attack.mitre.org/techniques/T1123/ + T1124: + id: T1124 + name: System Time Discovery + reference: https://attack.mitre.org/techniques/T1124/ + T1125: + id: T1125 + name: Video Capture + reference: https://attack.mitre.org/techniques/T1125/ + T1127: + id: T1127 + name: Trusted Developer Utilities Proxy Execution + reference: https://attack.mitre.org/techniques/T1127/ + T1129: + id: T1129 + name: Shared Modules + reference: https://attack.mitre.org/techniques/T1129/ + T1132: + id: T1132 + name: Data Encoding + reference: https://attack.mitre.org/techniques/T1132/ + T1133: + id: T1133 + name: External Remote Services + reference: https://attack.mitre.org/techniques/T1133/ + T1134: + id: T1134 + name: Access Token Manipulation + reference: https://attack.mitre.org/techniques/T1134/ + T1135: + id: T1135 + name: Network Share Discovery + reference: https://attack.mitre.org/techniques/T1135/ + T1136: + id: T1136 + name: Create Account + reference: https://attack.mitre.org/techniques/T1136/ + T1137: + id: T1137 + name: Office Application Startup + reference: https://attack.mitre.org/techniques/T1137/ + T1140: + id: T1140 + name: Deobfuscate/Decode Files or Information + reference: https://attack.mitre.org/techniques/T1140/ + T1176: + id: T1176 + name: Software Extensions + reference: https://attack.mitre.org/techniques/T1176/ + T1185: + id: T1185 + name: Browser Session Hijacking + reference: https://attack.mitre.org/techniques/T1185/ + T1187: + id: T1187 + name: Forced Authentication + reference: https://attack.mitre.org/techniques/T1187/ + T1189: + id: T1189 + name: Drive-by Compromise + reference: https://attack.mitre.org/techniques/T1189/ + T1190: + id: T1190 + name: Exploit Public-Facing Application + reference: https://attack.mitre.org/techniques/T1190/ + T1195: + id: T1195 + name: Supply Chain Compromise + reference: https://attack.mitre.org/techniques/T1195/ + T1197: + id: T1197 + name: BITS Jobs + reference: https://attack.mitre.org/techniques/T1197/ + T1199: + id: T1199 + name: Trusted Relationship + reference: https://attack.mitre.org/techniques/T1199/ + T1200: + id: T1200 + name: Hardware Additions + reference: https://attack.mitre.org/techniques/T1200/ + T1201: + id: T1201 + name: Password Policy Discovery + reference: https://attack.mitre.org/techniques/T1201/ + T1202: + id: T1202 + name: Indirect Command Execution + reference: https://attack.mitre.org/techniques/T1202/ + T1203: + id: T1203 + name: Exploitation for Client Execution + reference: https://attack.mitre.org/techniques/T1203/ + T1204: + id: T1204 + name: User Execution + reference: https://attack.mitre.org/techniques/T1204/ + T1205: + id: T1205 + name: Traffic Signaling + reference: https://attack.mitre.org/techniques/T1205/ + T1207: + id: T1207 + name: Rogue Domain Controller + reference: https://attack.mitre.org/techniques/T1207/ + T1210: + id: T1210 + name: Exploitation of Remote Services + reference: https://attack.mitre.org/techniques/T1210/ + T1211: + id: T1211 + name: Exploitation for Stealth + reference: https://attack.mitre.org/techniques/T1211/ + T1212: + id: T1212 + name: Exploitation for Credential Access + reference: https://attack.mitre.org/techniques/T1212/ + T1213: + id: T1213 + name: Data from Information Repositories + reference: https://attack.mitre.org/techniques/T1213/ + T1216: + id: T1216 + name: System Script Proxy Execution + reference: https://attack.mitre.org/techniques/T1216/ + T1217: + id: T1217 + name: Browser Information Discovery + reference: https://attack.mitre.org/techniques/T1217/ + T1218: + id: T1218 + name: System Binary Proxy Execution + reference: https://attack.mitre.org/techniques/T1218/ + T1219: + id: T1219 + name: Remote Access Tools + reference: https://attack.mitre.org/techniques/T1219/ + T1220: + id: T1220 + name: XSL Script Processing + reference: https://attack.mitre.org/techniques/T1220/ + T1221: + id: T1221 + name: Template Injection + reference: https://attack.mitre.org/techniques/T1221/ + T1222: + id: T1222 + name: File and Directory Permissions Modification + reference: https://attack.mitre.org/techniques/T1222/ + T1480: + id: T1480 + name: Execution Guardrails + reference: https://attack.mitre.org/techniques/T1480/ + T1482: + id: T1482 + name: Domain Trust Discovery + reference: https://attack.mitre.org/techniques/T1482/ + T1484: + id: T1484 + name: Domain or Tenant Policy Modification + reference: https://attack.mitre.org/techniques/T1484/ + T1485: + id: T1485 + name: Data Destruction + reference: https://attack.mitre.org/techniques/T1485/ + T1486: + id: T1486 + name: Data Encrypted for Impact + reference: https://attack.mitre.org/techniques/T1486/ + T1489: + id: T1489 + name: Service Stop + reference: https://attack.mitre.org/techniques/T1489/ + T1490: + id: T1490 + name: Inhibit System Recovery + reference: https://attack.mitre.org/techniques/T1490/ + T1491: + id: T1491 + name: Defacement + reference: https://attack.mitre.org/techniques/T1491/ + T1495: + id: T1495 + name: Firmware Corruption + reference: https://attack.mitre.org/techniques/T1495/ + T1496: + id: T1496 + name: Resource Hijacking + reference: https://attack.mitre.org/techniques/T1496/ + T1497: + id: T1497 + name: Virtualization/Sandbox Evasion + reference: https://attack.mitre.org/techniques/T1497/ + T1498: + id: T1498 + name: Network Denial of Service + reference: https://attack.mitre.org/techniques/T1498/ + T1499: + id: T1499 + name: Endpoint Denial of Service + reference: https://attack.mitre.org/techniques/T1499/ + T1505: + id: T1505 + name: Server Software Component + reference: https://attack.mitre.org/techniques/T1505/ + T1518: + id: T1518 + name: Software Discovery + reference: https://attack.mitre.org/techniques/T1518/ + T1525: + id: T1525 + name: Implant Internal Image + reference: https://attack.mitre.org/techniques/T1525/ + T1526: + id: T1526 + name: Cloud Service Discovery + reference: https://attack.mitre.org/techniques/T1526/ + T1528: + id: T1528 + name: Steal Application Access Token + reference: https://attack.mitre.org/techniques/T1528/ + T1529: + id: T1529 + name: System Shutdown/Reboot + reference: https://attack.mitre.org/techniques/T1529/ + T1530: + id: T1530 + name: Data from Cloud Storage + reference: https://attack.mitre.org/techniques/T1530/ + T1531: + id: T1531 + name: Account Access Removal + reference: https://attack.mitre.org/techniques/T1531/ + T1534: + id: T1534 + name: Internal Spearphishing + reference: https://attack.mitre.org/techniques/T1534/ + T1535: + id: T1535 + name: Unused/Unsupported Cloud Regions + reference: https://attack.mitre.org/techniques/T1535/ + T1537: + id: T1537 + name: Transfer Data to Cloud Account + reference: https://attack.mitre.org/techniques/T1537/ + T1538: + id: T1538 + name: Cloud Service Dashboard + reference: https://attack.mitre.org/techniques/T1538/ + T1539: + id: T1539 + name: Steal Web Session Cookie + reference: https://attack.mitre.org/techniques/T1539/ + T1542: + id: T1542 + name: Pre-OS Boot + reference: https://attack.mitre.org/techniques/T1542/ + T1543: + id: T1543 + name: Create or Modify System Process + reference: https://attack.mitre.org/techniques/T1543/ + T1546: + id: T1546 + name: Event Triggered Execution + reference: https://attack.mitre.org/techniques/T1546/ + T1547: + id: T1547 + name: Boot or Logon Autostart Execution + reference: https://attack.mitre.org/techniques/T1547/ + T1548: + id: T1548 + name: Abuse Elevation Control Mechanism + reference: https://attack.mitre.org/techniques/T1548/ + T1550: + id: T1550 + name: Use Alternate Authentication Material + reference: https://attack.mitre.org/techniques/T1550/ + T1552: + id: T1552 + name: Unsecured Credentials + reference: https://attack.mitre.org/techniques/T1552/ + T1553: + id: T1553 + name: Subvert Trust Controls + reference: https://attack.mitre.org/techniques/T1553/ + T1554: + id: T1554 + name: Compromise Host Software Binary + reference: https://attack.mitre.org/techniques/T1554/ + T1555: + id: T1555 + name: Credentials from Password Stores + reference: https://attack.mitre.org/techniques/T1555/ + T1556: + id: T1556 + name: Modify Authentication Process + reference: https://attack.mitre.org/techniques/T1556/ + T1557: + id: T1557 + name: Adversary-in-the-Middle + reference: https://attack.mitre.org/techniques/T1557/ + T1558: + id: T1558 + name: Steal or Forge Kerberos Tickets + reference: https://attack.mitre.org/techniques/T1558/ + T1559: + id: T1559 + name: Inter-Process Communication + reference: https://attack.mitre.org/techniques/T1559/ + T1560: + id: T1560 + name: Archive Collected Data + reference: https://attack.mitre.org/techniques/T1560/ + T1561: + id: T1561 + name: Disk Wipe + reference: https://attack.mitre.org/techniques/T1561/ + T1562: + id: T1562 + name: Impair Defenses + reference: https://attack.mitre.org/techniques/T1562/ + T1563: + id: T1563 + name: Remote Service Session Hijacking + reference: https://attack.mitre.org/techniques/T1563/ + T1564: + id: T1564 + name: Hide Artifacts + reference: https://attack.mitre.org/techniques/T1564/ + T1565: + id: T1565 + name: Data Manipulation + reference: https://attack.mitre.org/techniques/T1565/ + T1566: + id: T1566 + name: Phishing + reference: https://attack.mitre.org/techniques/T1566/ + T1567: + id: T1567 + name: Exfiltration Over Web Service + reference: https://attack.mitre.org/techniques/T1567/ + T1568: + id: T1568 + name: Dynamic Resolution + reference: https://attack.mitre.org/techniques/T1568/ + T1569: + id: T1569 + name: System Services + reference: https://attack.mitre.org/techniques/T1569/ + T1570: + id: T1570 + name: Lateral Tool Transfer + reference: https://attack.mitre.org/techniques/T1570/ + T1571: + id: T1571 + name: Non-Standard Port + reference: https://attack.mitre.org/techniques/T1571/ + T1572: + id: T1572 + name: Protocol Tunneling + reference: https://attack.mitre.org/techniques/T1572/ + T1573: + id: T1573 + name: Encrypted Channel + reference: https://attack.mitre.org/techniques/T1573/ + T1574: + id: T1574 + name: Hijack Execution Flow + reference: https://attack.mitre.org/techniques/T1574/ + T1578: + id: T1578 + name: Modify Cloud Compute Infrastructure + reference: https://attack.mitre.org/techniques/T1578/ + T1580: + id: T1580 + name: Cloud Infrastructure Discovery + reference: https://attack.mitre.org/techniques/T1580/ + T1583: + id: T1583 + name: Acquire Infrastructure + reference: https://attack.mitre.org/techniques/T1583/ + T1584: + id: T1584 + name: Compromise Infrastructure + reference: https://attack.mitre.org/techniques/T1584/ + T1585: + id: T1585 + name: Establish Accounts + reference: https://attack.mitre.org/techniques/T1585/ + T1586: + id: T1586 + name: Compromise Accounts + reference: https://attack.mitre.org/techniques/T1586/ + T1587: + id: T1587 + name: Develop Capabilities + reference: https://attack.mitre.org/techniques/T1587/ + T1588: + id: T1588 + name: Obtain Capabilities + reference: https://attack.mitre.org/techniques/T1588/ + T1589: + id: T1589 + name: Gather Victim Identity Information + reference: https://attack.mitre.org/techniques/T1589/ + T1590: + id: T1590 + name: Gather Victim Network Information + reference: https://attack.mitre.org/techniques/T1590/ + T1591: + id: T1591 + name: Gather Victim Org Information + reference: https://attack.mitre.org/techniques/T1591/ + T1592: + id: T1592 + name: Gather Victim Host Information + reference: https://attack.mitre.org/techniques/T1592/ + T1593: + id: T1593 + name: Search Open Websites/Domains + reference: https://attack.mitre.org/techniques/T1593/ + T1594: + id: T1594 + name: Search Victim-Owned Websites + reference: https://attack.mitre.org/techniques/T1594/ + T1595: + id: T1595 + name: Active Scanning + reference: https://attack.mitre.org/techniques/T1595/ + T1596: + id: T1596 + name: Search Open Technical Databases + reference: https://attack.mitre.org/techniques/T1596/ + T1597: + id: T1597 + name: Search Closed Sources + reference: https://attack.mitre.org/techniques/T1597/ + T1598: + id: T1598 + name: Phishing for Information + reference: https://attack.mitre.org/techniques/T1598/ + T1599: + id: T1599 + name: Network Boundary Bridging + reference: https://attack.mitre.org/techniques/T1599/ + T1600: + id: T1600 + name: Weaken Encryption + reference: https://attack.mitre.org/techniques/T1600/ + T1601: + id: T1601 + name: Modify System Image + reference: https://attack.mitre.org/techniques/T1601/ + T1602: + id: T1602 + name: Data from Configuration Repository + reference: https://attack.mitre.org/techniques/T1602/ + T1606: + id: T1606 + name: Forge Web Credentials + reference: https://attack.mitre.org/techniques/T1606/ + T1608: + id: T1608 + name: Stage Capabilities + reference: https://attack.mitre.org/techniques/T1608/ + T1609: + id: T1609 + name: Container Administration Command + reference: https://attack.mitre.org/techniques/T1609/ + T1610: + id: T1610 + name: Deploy Container + reference: https://attack.mitre.org/techniques/T1610/ + T1611: + id: T1611 + name: Escape to Host + reference: https://attack.mitre.org/techniques/T1611/ + T1612: + id: T1612 + name: Build Image on Host + reference: https://attack.mitre.org/techniques/T1612/ + T1613: + id: T1613 + name: Container and Resource Discovery + reference: https://attack.mitre.org/techniques/T1613/ + T1614: + id: T1614 + name: System Location Discovery + reference: https://attack.mitre.org/techniques/T1614/ + T1615: + id: T1615 + name: Group Policy Discovery + reference: https://attack.mitre.org/techniques/T1615/ + T1619: + id: T1619 + name: Cloud Storage Object Discovery + reference: https://attack.mitre.org/techniques/T1619/ + T1620: + id: T1620 + name: Reflective Code Loading + reference: https://attack.mitre.org/techniques/T1620/ + T1621: + id: T1621 + name: Multi-Factor Authentication Request Generation + reference: https://attack.mitre.org/techniques/T1621/ + T1622: + id: T1622 + name: Debugger Evasion + reference: https://attack.mitre.org/techniques/T1622/ + T1647: + id: T1647 + name: Plist File Modification + reference: https://attack.mitre.org/techniques/T1647/ + T1648: + id: T1648 + name: Serverless Execution + reference: https://attack.mitre.org/techniques/T1648/ + T1649: + id: T1649 + name: Steal or Forge Authentication Certificates + reference: https://attack.mitre.org/techniques/T1649/ + T1650: + id: T1650 + name: Acquire Access + reference: https://attack.mitre.org/techniques/T1650/ + T1651: + id: T1651 + name: Cloud Administration Command + reference: https://attack.mitre.org/techniques/T1651/ + T1652: + id: T1652 + name: Device Driver Discovery + reference: https://attack.mitre.org/techniques/T1652/ + T1653: + id: T1653 + name: Power Settings + reference: https://attack.mitre.org/techniques/T1653/ + T1654: + id: T1654 + name: Log Enumeration + reference: https://attack.mitre.org/techniques/T1654/ + T1656: + id: T1656 + name: Impersonation + reference: https://attack.mitre.org/techniques/T1656/ + T1657: + id: T1657 + name: Financial Theft + reference: https://attack.mitre.org/techniques/T1657/ + T1659: + id: T1659 + name: Content Injection + reference: https://attack.mitre.org/techniques/T1659/ + T1665: + id: T1665 + name: Hide Infrastructure + reference: https://attack.mitre.org/techniques/T1665/ + T1666: + id: T1666 + name: Modify Cloud Resource Hierarchy + reference: https://attack.mitre.org/techniques/T1666/ + T1667: + id: T1667 + name: Email Bombing + reference: https://attack.mitre.org/techniques/T1667/ + T1668: + id: T1668 + name: Exclusive Control + reference: https://attack.mitre.org/techniques/T1668/ + T1669: + id: T1669 + name: Wi-Fi Networks + reference: https://attack.mitre.org/techniques/T1669/ + T1671: + id: T1671 + name: Cloud Application Integration + reference: https://attack.mitre.org/techniques/T1671/ + T1672: + id: T1672 + name: Email Spoofing + reference: https://attack.mitre.org/techniques/T1672/ + T1673: + id: T1673 + name: Virtual Machine Discovery + reference: https://attack.mitre.org/techniques/T1673/ + T1674: + id: T1674 + name: Input Injection + reference: https://attack.mitre.org/techniques/T1674/ + T1675: + id: T1675 + name: ESXi Administration Command + reference: https://attack.mitre.org/techniques/T1675/ + T1677: + id: T1677 + name: Poisoned Pipeline Execution + reference: https://attack.mitre.org/techniques/T1677/ + T1678: + id: T1678 + name: Delay Execution + reference: https://attack.mitre.org/techniques/T1678/ + T1679: + id: T1679 + name: Selective Exclusion + reference: https://attack.mitre.org/techniques/T1679/ + T1680: + id: T1680 + name: Local Storage Discovery + reference: https://attack.mitre.org/techniques/T1680/ + T1681: + id: T1681 + name: Search Threat Vendor Data + reference: https://attack.mitre.org/techniques/T1681/ +subtechniques: + T1001.001: + id: T1001.001 + name: Junk Data + reference: https://attack.mitre.org/techniques/T1001/001/ + T1001.002: + id: T1001.002 + name: Steganography + reference: https://attack.mitre.org/techniques/T1001/002/ + T1001.003: + id: T1001.003 + name: Protocol or Service Impersonation + reference: https://attack.mitre.org/techniques/T1001/003/ + T1003.001: + id: T1003.001 + name: LSASS Memory + reference: https://attack.mitre.org/techniques/T1003/001/ + T1003.002: + id: T1003.002 + name: Security Account Manager + reference: https://attack.mitre.org/techniques/T1003/002/ + T1003.003: + id: T1003.003 + name: NTDS + reference: https://attack.mitre.org/techniques/T1003/003/ + T1003.004: + id: T1003.004 + name: LSA Secrets + reference: https://attack.mitre.org/techniques/T1003/004/ + T1003.005: + id: T1003.005 + name: Cached Domain Credentials + reference: https://attack.mitre.org/techniques/T1003/005/ + T1003.006: + id: T1003.006 + name: DCSync + reference: https://attack.mitre.org/techniques/T1003/006/ + T1003.007: + id: T1003.007 + name: Proc Filesystem + reference: https://attack.mitre.org/techniques/T1003/007/ + T1003.008: + id: T1003.008 + name: /etc/passwd and /etc/shadow + reference: https://attack.mitre.org/techniques/T1003/008/ + T1011.001: + id: T1011.001 + name: Exfiltration Over Bluetooth + reference: https://attack.mitre.org/techniques/T1011/001/ + T1016.001: + id: T1016.001 + name: Internet Connection Discovery + reference: https://attack.mitre.org/techniques/T1016/001/ + T1016.002: + id: T1016.002 + name: Wi-Fi Discovery + reference: https://attack.mitre.org/techniques/T1016/002/ + T1020.001: + id: T1020.001 + name: Traffic Duplication + reference: https://attack.mitre.org/techniques/T1020/001/ + T1021.001: + id: T1021.001 + name: Remote Desktop Protocol + reference: https://attack.mitre.org/techniques/T1021/001/ + T1021.002: + id: T1021.002 + name: SMB/Windows Admin Shares + reference: https://attack.mitre.org/techniques/T1021/002/ + T1021.003: + id: T1021.003 + name: Distributed Component Object Model + reference: https://attack.mitre.org/techniques/T1021/003/ + T1021.004: + id: T1021.004 + name: SSH + reference: https://attack.mitre.org/techniques/T1021/004/ + T1021.005: + id: T1021.005 + name: VNC + reference: https://attack.mitre.org/techniques/T1021/005/ + T1021.006: + id: T1021.006 + name: Windows Remote Management + reference: https://attack.mitre.org/techniques/T1021/006/ + T1021.007: + id: T1021.007 + name: Cloud Services + reference: https://attack.mitre.org/techniques/T1021/007/ + T1021.008: + id: T1021.008 + name: Direct Cloud VM Connections + reference: https://attack.mitre.org/techniques/T1021/008/ + T1027.001: + id: T1027.001 + name: Binary Padding + reference: https://attack.mitre.org/techniques/T1027/001/ + T1027.002: + id: T1027.002 + name: Software Packing + reference: https://attack.mitre.org/techniques/T1027/002/ + T1027.003: + id: T1027.003 + name: Steganography + reference: https://attack.mitre.org/techniques/T1027/003/ + T1027.004: + id: T1027.004 + name: Compile After Delivery + reference: https://attack.mitre.org/techniques/T1027/004/ + T1027.005: + id: T1027.005 + name: Indicator Removal from Tools + reference: https://attack.mitre.org/techniques/T1027/005/ + T1027.006: + id: T1027.006 + name: HTML Smuggling + reference: https://attack.mitre.org/techniques/T1027/006/ + T1027.007: + id: T1027.007 + name: Dynamic API Resolution + reference: https://attack.mitre.org/techniques/T1027/007/ + T1027.008: + id: T1027.008 + name: Stripped Payloads + reference: https://attack.mitre.org/techniques/T1027/008/ + T1027.009: + id: T1027.009 + name: Embedded Payloads + reference: https://attack.mitre.org/techniques/T1027/009/ + T1027.010: + id: T1027.010 + name: Command Obfuscation + reference: https://attack.mitre.org/techniques/T1027/010/ + T1027.011: + id: T1027.011 + name: Fileless Storage + reference: https://attack.mitre.org/techniques/T1027/011/ + T1027.012: + id: T1027.012 + name: LNK Icon Smuggling + reference: https://attack.mitre.org/techniques/T1027/012/ + T1027.013: + id: T1027.013 + name: Encrypted/Encoded File + reference: https://attack.mitre.org/techniques/T1027/013/ + T1027.014: + id: T1027.014 + name: Polymorphic Code + reference: https://attack.mitre.org/techniques/T1027/014/ + T1027.015: + id: T1027.015 + name: Compression + reference: https://attack.mitre.org/techniques/T1027/015/ + T1027.016: + id: T1027.016 + name: Junk Code Insertion + reference: https://attack.mitre.org/techniques/T1027/016/ + T1027.017: + id: T1027.017 + name: SVG Smuggling + reference: https://attack.mitre.org/techniques/T1027/017/ + T1036.001: + id: T1036.001 + name: Invalid Code Signature + reference: https://attack.mitre.org/techniques/T1036/001/ + T1036.002: + id: T1036.002 + name: Right-to-Left Override + reference: https://attack.mitre.org/techniques/T1036/002/ + T1036.003: + id: T1036.003 + name: Rename Legitimate Utilities + reference: https://attack.mitre.org/techniques/T1036/003/ + T1036.004: + id: T1036.004 + name: Masquerade Task or Service + reference: https://attack.mitre.org/techniques/T1036/004/ + T1036.005: + id: T1036.005 + name: Match Legitimate Resource Name or Location + reference: https://attack.mitre.org/techniques/T1036/005/ + T1036.006: + id: T1036.006 + name: Space after Filename + reference: https://attack.mitre.org/techniques/T1036/006/ + T1036.007: + id: T1036.007 + name: Double File Extension + reference: https://attack.mitre.org/techniques/T1036/007/ + T1036.008: + id: T1036.008 + name: Masquerade File Type + reference: https://attack.mitre.org/techniques/T1036/008/ + T1036.009: + id: T1036.009 + name: Break Process Trees + reference: https://attack.mitre.org/techniques/T1036/009/ + T1036.010: + id: T1036.010 + name: Masquerade Account Name + reference: https://attack.mitre.org/techniques/T1036/010/ + T1036.011: + id: T1036.011 + name: Overwrite Process Arguments + reference: https://attack.mitre.org/techniques/T1036/011/ + T1036.012: + id: T1036.012 + name: Browser Fingerprint + reference: https://attack.mitre.org/techniques/T1036/012/ + T1037.001: + id: T1037.001 + name: Logon Script (Windows) + reference: https://attack.mitre.org/techniques/T1037/001/ + T1037.002: + id: T1037.002 + name: Login Hook + reference: https://attack.mitre.org/techniques/T1037/002/ + T1037.003: + id: T1037.003 + name: Network Logon Script + reference: https://attack.mitre.org/techniques/T1037/003/ + T1037.004: + id: T1037.004 + name: RC Scripts + reference: https://attack.mitre.org/techniques/T1037/004/ + T1037.005: + id: T1037.005 + name: Startup Items + reference: https://attack.mitre.org/techniques/T1037/005/ + T1048.001: + id: T1048.001 + name: Exfiltration Over Symmetric Encrypted Non-C2 Protocol + reference: https://attack.mitre.org/techniques/T1048/001/ + T1048.002: + id: T1048.002 + name: Exfiltration Over Asymmetric Encrypted Non-C2 Protocol + reference: https://attack.mitre.org/techniques/T1048/002/ + T1048.003: + id: T1048.003 + name: Exfiltration Over Unencrypted Non-C2 Protocol + reference: https://attack.mitre.org/techniques/T1048/003/ + T1052.001: + id: T1052.001 + name: Exfiltration over USB + reference: https://attack.mitre.org/techniques/T1052/001/ + T1053.002: + id: T1053.002 + name: At + reference: https://attack.mitre.org/techniques/T1053/002/ + T1053.003: + id: T1053.003 + name: Cron + reference: https://attack.mitre.org/techniques/T1053/003/ + T1053.005: + id: T1053.005 + name: Scheduled Task + reference: https://attack.mitre.org/techniques/T1053/005/ + T1053.006: + id: T1053.006 + name: Systemd Timers + reference: https://attack.mitre.org/techniques/T1053/006/ + T1053.007: + id: T1053.007 + name: Container Orchestration Job + reference: https://attack.mitre.org/techniques/T1053/007/ + T1055.001: + id: T1055.001 + name: Dynamic-link Library Injection + reference: https://attack.mitre.org/techniques/T1055/001/ + T1055.002: + id: T1055.002 + name: Portable Executable Injection + reference: https://attack.mitre.org/techniques/T1055/002/ + T1055.003: + id: T1055.003 + name: Thread Execution Hijacking + reference: https://attack.mitre.org/techniques/T1055/003/ + T1055.004: + id: T1055.004 + name: Asynchronous Procedure Call + reference: https://attack.mitre.org/techniques/T1055/004/ + T1055.005: + id: T1055.005 + name: Thread Local Storage + reference: https://attack.mitre.org/techniques/T1055/005/ + T1055.008: + id: T1055.008 + name: Ptrace System Calls + reference: https://attack.mitre.org/techniques/T1055/008/ + T1055.009: + id: T1055.009 + name: Proc Memory + reference: https://attack.mitre.org/techniques/T1055/009/ + T1055.011: + id: T1055.011 + name: Extra Window Memory Injection + reference: https://attack.mitre.org/techniques/T1055/011/ + T1055.012: + id: T1055.012 + name: Process Hollowing + reference: https://attack.mitre.org/techniques/T1055/012/ + T1055.013: + id: T1055.013 + name: "Process Doppelg\xE4nging" + reference: https://attack.mitre.org/techniques/T1055/013/ + T1055.014: + id: T1055.014 + name: VDSO Hijacking + reference: https://attack.mitre.org/techniques/T1055/014/ + T1055.015: + id: T1055.015 + name: ListPlanting + reference: https://attack.mitre.org/techniques/T1055/015/ + T1056.001: + id: T1056.001 + name: Keylogging + reference: https://attack.mitre.org/techniques/T1056/001/ + T1056.002: + id: T1056.002 + name: GUI Input Capture + reference: https://attack.mitre.org/techniques/T1056/002/ + T1056.003: + id: T1056.003 + name: Web Portal Capture + reference: https://attack.mitre.org/techniques/T1056/003/ + T1056.004: + id: T1056.004 + name: Credential API Hooking + reference: https://attack.mitre.org/techniques/T1056/004/ + T1059.001: + id: T1059.001 + name: PowerShell + reference: https://attack.mitre.org/techniques/T1059/001/ + T1059.002: + id: T1059.002 + name: AppleScript + reference: https://attack.mitre.org/techniques/T1059/002/ + T1059.003: + id: T1059.003 + name: Windows Command Shell + reference: https://attack.mitre.org/techniques/T1059/003/ + T1059.004: + id: T1059.004 + name: Unix Shell + reference: https://attack.mitre.org/techniques/T1059/004/ + T1059.005: + id: T1059.005 + name: Visual Basic + reference: https://attack.mitre.org/techniques/T1059/005/ + T1059.006: + id: T1059.006 + name: Python + reference: https://attack.mitre.org/techniques/T1059/006/ + T1059.007: + id: T1059.007 + name: JavaScript + reference: https://attack.mitre.org/techniques/T1059/007/ + T1059.008: + id: T1059.008 + name: Network Device CLI + reference: https://attack.mitre.org/techniques/T1059/008/ + T1059.009: + id: T1059.009 + name: Cloud API + reference: https://attack.mitre.org/techniques/T1059/009/ + T1059.010: + id: T1059.010 + name: AutoHotKey & AutoIT + reference: https://attack.mitre.org/techniques/T1059/010/ + T1059.011: + id: T1059.011 + name: Lua + reference: https://attack.mitre.org/techniques/T1059/011/ + T1059.012: + id: T1059.012 + name: Hypervisor CLI + reference: https://attack.mitre.org/techniques/T1059/012/ + T1059.013: + id: T1059.013 + name: Container CLI/API + reference: https://attack.mitre.org/techniques/T1059/013/ + T1069.001: + id: T1069.001 + name: Local Groups + reference: https://attack.mitre.org/techniques/T1069/001/ + T1069.002: + id: T1069.002 + name: Domain Groups + reference: https://attack.mitre.org/techniques/T1069/002/ + T1069.003: + id: T1069.003 + name: Cloud Groups + reference: https://attack.mitre.org/techniques/T1069/003/ + T1070.001: + id: T1070.001 + name: Clear Windows Event Logs + reference: https://attack.mitre.org/techniques/T1070/001/ + T1070.002: + id: T1070.002 + name: Clear Linux or Mac System Logs + reference: https://attack.mitre.org/techniques/T1070/002/ + T1070.003: + id: T1070.003 + name: Clear Command History + reference: https://attack.mitre.org/techniques/T1070/003/ + T1070.004: + id: T1070.004 + name: File Deletion + reference: https://attack.mitre.org/techniques/T1070/004/ + T1070.005: + id: T1070.005 + name: Network Share Connection Removal + reference: https://attack.mitre.org/techniques/T1070/005/ + T1070.006: + id: T1070.006 + name: Timestomp + reference: https://attack.mitre.org/techniques/T1070/006/ + T1070.007: + id: T1070.007 + name: Clear Network Connection History and Configurations + reference: https://attack.mitre.org/techniques/T1070/007/ + T1070.008: + id: T1070.008 + name: Clear Mailbox Data + reference: https://attack.mitre.org/techniques/T1070/008/ + T1070.009: + id: T1070.009 + name: Clear Persistence + reference: https://attack.mitre.org/techniques/T1070/009/ + T1070.010: + id: T1070.010 + name: Relocate Malware + reference: https://attack.mitre.org/techniques/T1070/010/ + T1071.001: + id: T1071.001 + name: Web Protocols + reference: https://attack.mitre.org/techniques/T1071/001/ + T1071.002: + id: T1071.002 + name: File Transfer Protocols + reference: https://attack.mitre.org/techniques/T1071/002/ + T1071.003: + id: T1071.003 + name: Mail Protocols + reference: https://attack.mitre.org/techniques/T1071/003/ + T1071.004: + id: T1071.004 + name: DNS + reference: https://attack.mitre.org/techniques/T1071/004/ + T1071.005: + id: T1071.005 + name: Publish/Subscribe Protocols + reference: https://attack.mitre.org/techniques/T1071/005/ + T1074.001: + id: T1074.001 + name: Local Data Staging + reference: https://attack.mitre.org/techniques/T1074/001/ + T1074.002: + id: T1074.002 + name: Remote Data Staging + reference: https://attack.mitre.org/techniques/T1074/002/ + T1078.001: + id: T1078.001 + name: Default Accounts + reference: https://attack.mitre.org/techniques/T1078/001/ + T1078.002: + id: T1078.002 + name: Domain Accounts + reference: https://attack.mitre.org/techniques/T1078/002/ + T1078.003: + id: T1078.003 + name: Local Accounts + reference: https://attack.mitre.org/techniques/T1078/003/ + T1078.004: + id: T1078.004 + name: Cloud Accounts + reference: https://attack.mitre.org/techniques/T1078/004/ + T1087.001: + id: T1087.001 + name: Local Account + reference: https://attack.mitre.org/techniques/T1087/001/ + T1087.002: + id: T1087.002 + name: Domain Account + reference: https://attack.mitre.org/techniques/T1087/002/ + T1087.003: + id: T1087.003 + name: Email Account + reference: https://attack.mitre.org/techniques/T1087/003/ + T1087.004: + id: T1087.004 + name: Cloud Account + reference: https://attack.mitre.org/techniques/T1087/004/ + T1090.001: + id: T1090.001 + name: Internal Proxy + reference: https://attack.mitre.org/techniques/T1090/001/ + T1090.002: + id: T1090.002 + name: External Proxy + reference: https://attack.mitre.org/techniques/T1090/002/ + T1090.003: + id: T1090.003 + name: Multi-hop Proxy + reference: https://attack.mitre.org/techniques/T1090/003/ + T1090.004: + id: T1090.004 + name: Domain Fronting + reference: https://attack.mitre.org/techniques/T1090/004/ + T1098.001: + id: T1098.001 + name: Additional Cloud Credentials + reference: https://attack.mitre.org/techniques/T1098/001/ + T1098.002: + id: T1098.002 + name: Additional Email Delegate Permissions + reference: https://attack.mitre.org/techniques/T1098/002/ + T1098.003: + id: T1098.003 + name: Additional Cloud Roles + reference: https://attack.mitre.org/techniques/T1098/003/ + T1098.004: + id: T1098.004 + name: SSH Authorized Keys + reference: https://attack.mitre.org/techniques/T1098/004/ + T1098.005: + id: T1098.005 + name: Device Registration + reference: https://attack.mitre.org/techniques/T1098/005/ + T1098.006: + id: T1098.006 + name: Additional Container Cluster Roles + reference: https://attack.mitre.org/techniques/T1098/006/ + T1098.007: + id: T1098.007 + name: Additional Local or Domain Groups + reference: https://attack.mitre.org/techniques/T1098/007/ + T1102.001: + id: T1102.001 + name: Dead Drop Resolver + reference: https://attack.mitre.org/techniques/T1102/001/ + T1102.002: + id: T1102.002 + name: Bidirectional Communication + reference: https://attack.mitre.org/techniques/T1102/002/ + T1102.003: + id: T1102.003 + name: One-Way Communication + reference: https://attack.mitre.org/techniques/T1102/003/ + T1110.001: + id: T1110.001 + name: Password Guessing + reference: https://attack.mitre.org/techniques/T1110/001/ + T1110.002: + id: T1110.002 + name: Password Cracking + reference: https://attack.mitre.org/techniques/T1110/002/ + T1110.003: + id: T1110.003 + name: Password Spraying + reference: https://attack.mitre.org/techniques/T1110/003/ + T1110.004: + id: T1110.004 + name: Credential Stuffing + reference: https://attack.mitre.org/techniques/T1110/004/ + T1114.001: + id: T1114.001 + name: Local Email Collection + reference: https://attack.mitre.org/techniques/T1114/001/ + T1114.002: + id: T1114.002 + name: Remote Email Collection + reference: https://attack.mitre.org/techniques/T1114/002/ + T1114.003: + id: T1114.003 + name: Email Forwarding Rule + reference: https://attack.mitre.org/techniques/T1114/003/ + T1127.001: + id: T1127.001 + name: MSBuild + reference: https://attack.mitre.org/techniques/T1127/001/ + T1127.002: + id: T1127.002 + name: ClickOnce + reference: https://attack.mitre.org/techniques/T1127/002/ + T1127.003: + id: T1127.003 + name: JamPlus + reference: https://attack.mitre.org/techniques/T1127/003/ + T1132.001: + id: T1132.001 + name: Standard Encoding + reference: https://attack.mitre.org/techniques/T1132/001/ + T1132.002: + id: T1132.002 + name: Non-Standard Encoding + reference: https://attack.mitre.org/techniques/T1132/002/ + T1134.001: + id: T1134.001 + name: Token Impersonation/Theft + reference: https://attack.mitre.org/techniques/T1134/001/ + T1134.002: + id: T1134.002 + name: Create Process with Token + reference: https://attack.mitre.org/techniques/T1134/002/ + T1134.003: + id: T1134.003 + name: Make and Impersonate Token + reference: https://attack.mitre.org/techniques/T1134/003/ + T1134.004: + id: T1134.004 + name: Parent PID Spoofing + reference: https://attack.mitre.org/techniques/T1134/004/ + T1134.005: + id: T1134.005 + name: SID-History Injection + reference: https://attack.mitre.org/techniques/T1134/005/ + T1136.001: + id: T1136.001 + name: Local Account + reference: https://attack.mitre.org/techniques/T1136/001/ + T1136.002: + id: T1136.002 + name: Domain Account + reference: https://attack.mitre.org/techniques/T1136/002/ + T1136.003: + id: T1136.003 + name: Cloud Account + reference: https://attack.mitre.org/techniques/T1136/003/ + T1137.001: + id: T1137.001 + name: Office Template Macros + reference: https://attack.mitre.org/techniques/T1137/001/ + T1137.002: + id: T1137.002 + name: Office Test + reference: https://attack.mitre.org/techniques/T1137/002/ + T1137.003: + id: T1137.003 + name: Outlook Forms + reference: https://attack.mitre.org/techniques/T1137/003/ + T1137.004: + id: T1137.004 + name: Outlook Home Page + reference: https://attack.mitre.org/techniques/T1137/004/ + T1137.005: + id: T1137.005 + name: Outlook Rules + reference: https://attack.mitre.org/techniques/T1137/005/ + T1137.006: + id: T1137.006 + name: Add-ins + reference: https://attack.mitre.org/techniques/T1137/006/ + T1176.001: + id: T1176.001 + name: Browser Extensions + reference: https://attack.mitre.org/techniques/T1176/001/ + T1176.002: + id: T1176.002 + name: IDE Extensions + reference: https://attack.mitre.org/techniques/T1176/002/ + T1195.001: + id: T1195.001 + name: Compromise Software Dependencies and Development Tools + reference: https://attack.mitre.org/techniques/T1195/001/ + T1195.002: + id: T1195.002 + name: Compromise Software Supply Chain + reference: https://attack.mitre.org/techniques/T1195/002/ + T1195.003: + id: T1195.003 + name: Compromise Hardware Supply Chain + reference: https://attack.mitre.org/techniques/T1195/003/ + T1204.001: + id: T1204.001 + name: Malicious Link + reference: https://attack.mitre.org/techniques/T1204/001/ + T1204.002: + id: T1204.002 + name: Malicious File + reference: https://attack.mitre.org/techniques/T1204/002/ + T1204.003: + id: T1204.003 + name: Malicious Image + reference: https://attack.mitre.org/techniques/T1204/003/ + T1204.004: + id: T1204.004 + name: Malicious Copy and Paste + reference: https://attack.mitre.org/techniques/T1204/004/ + T1204.005: + id: T1204.005 + name: Malicious Library + reference: https://attack.mitre.org/techniques/T1204/005/ + T1205.001: + id: T1205.001 + name: Port Knocking + reference: https://attack.mitre.org/techniques/T1205/001/ + T1205.002: + id: T1205.002 + name: Socket Filters + reference: https://attack.mitre.org/techniques/T1205/002/ + T1213.001: + id: T1213.001 + name: Confluence + reference: https://attack.mitre.org/techniques/T1213/001/ + T1213.002: + id: T1213.002 + name: Sharepoint + reference: https://attack.mitre.org/techniques/T1213/002/ + T1213.003: + id: T1213.003 + name: Code Repositories + reference: https://attack.mitre.org/techniques/T1213/003/ + T1213.004: + id: T1213.004 + name: Customer Relationship Management Software + reference: https://attack.mitre.org/techniques/T1213/004/ + T1213.005: + id: T1213.005 + name: Messaging Applications + reference: https://attack.mitre.org/techniques/T1213/005/ + T1213.006: + id: T1213.006 + name: Databases + reference: https://attack.mitre.org/techniques/T1213/006/ + T1216.001: + id: T1216.001 + name: PubPrn + reference: https://attack.mitre.org/techniques/T1216/001/ + T1216.002: + id: T1216.002 + name: SyncAppvPublishingServer + reference: https://attack.mitre.org/techniques/T1216/002/ + T1218.001: + id: T1218.001 + name: Compiled HTML File + reference: https://attack.mitre.org/techniques/T1218/001/ + T1218.002: + id: T1218.002 + name: Control Panel + reference: https://attack.mitre.org/techniques/T1218/002/ + T1218.003: + id: T1218.003 + name: CMSTP + reference: https://attack.mitre.org/techniques/T1218/003/ + T1218.004: + id: T1218.004 + name: InstallUtil + reference: https://attack.mitre.org/techniques/T1218/004/ + T1218.005: + id: T1218.005 + name: Mshta + reference: https://attack.mitre.org/techniques/T1218/005/ + T1218.007: + id: T1218.007 + name: Msiexec + reference: https://attack.mitre.org/techniques/T1218/007/ + T1218.008: + id: T1218.008 + name: Odbcconf + reference: https://attack.mitre.org/techniques/T1218/008/ + T1218.009: + id: T1218.009 + name: Regsvcs/Regasm + reference: https://attack.mitre.org/techniques/T1218/009/ + T1218.010: + id: T1218.010 + name: Regsvr32 + reference: https://attack.mitre.org/techniques/T1218/010/ + T1218.011: + id: T1218.011 + name: Rundll32 + reference: https://attack.mitre.org/techniques/T1218/011/ + T1218.012: + id: T1218.012 + name: Verclsid + reference: https://attack.mitre.org/techniques/T1218/012/ + T1218.013: + id: T1218.013 + name: Mavinject + reference: https://attack.mitre.org/techniques/T1218/013/ + T1218.014: + id: T1218.014 + name: MMC + reference: https://attack.mitre.org/techniques/T1218/014/ + T1218.015: + id: T1218.015 + name: Electron Applications + reference: https://attack.mitre.org/techniques/T1218/015/ + T1219.001: + id: T1219.001 + name: IDE Tunneling + reference: https://attack.mitre.org/techniques/T1219/001/ + T1219.002: + id: T1219.002 + name: Remote Desktop Software + reference: https://attack.mitre.org/techniques/T1219/002/ + T1219.003: + id: T1219.003 + name: Remote Access Hardware + reference: https://attack.mitre.org/techniques/T1219/003/ + T1222.001: + id: T1222.001 + name: Windows Permissions + reference: https://attack.mitre.org/techniques/T1222/001/ + T1222.002: + id: T1222.002 + name: Linux and Mac Permissions + reference: https://attack.mitre.org/techniques/T1222/002/ + T1480.001: + id: T1480.001 + name: Environmental Keying + reference: https://attack.mitre.org/techniques/T1480/001/ + T1480.002: + id: T1480.002 + name: Mutual Exclusion + reference: https://attack.mitre.org/techniques/T1480/002/ + T1484.001: + id: T1484.001 + name: Group Policy Modification + reference: https://attack.mitre.org/techniques/T1484/001/ + T1484.002: + id: T1484.002 + name: Trust Modification + reference: https://attack.mitre.org/techniques/T1484/002/ + T1485.001: + id: T1485.001 + name: Lifecycle-Triggered Deletion + reference: https://attack.mitre.org/techniques/T1485/001/ + T1491.001: + id: T1491.001 + name: Internal Defacement + reference: https://attack.mitre.org/techniques/T1491/001/ + T1491.002: + id: T1491.002 + name: External Defacement + reference: https://attack.mitre.org/techniques/T1491/002/ + T1496.001: + id: T1496.001 + name: Compute Hijacking + reference: https://attack.mitre.org/techniques/T1496/001/ + T1496.002: + id: T1496.002 + name: Bandwidth Hijacking + reference: https://attack.mitre.org/techniques/T1496/002/ + T1496.003: + id: T1496.003 + name: SMS Pumping + reference: https://attack.mitre.org/techniques/T1496/003/ + T1496.004: + id: T1496.004 + name: Cloud Service Hijacking + reference: https://attack.mitre.org/techniques/T1496/004/ + T1497.001: + id: T1497.001 + name: System Checks + reference: https://attack.mitre.org/techniques/T1497/001/ + T1497.002: + id: T1497.002 + name: User Activity Based Checks + reference: https://attack.mitre.org/techniques/T1497/002/ + T1497.003: + id: T1497.003 + name: Time Based Checks + reference: https://attack.mitre.org/techniques/T1497/003/ + T1498.001: + id: T1498.001 + name: Direct Network Flood + reference: https://attack.mitre.org/techniques/T1498/001/ + T1498.002: + id: T1498.002 + name: Reflection Amplification + reference: https://attack.mitre.org/techniques/T1498/002/ + T1499.001: + id: T1499.001 + name: OS Exhaustion Flood + reference: https://attack.mitre.org/techniques/T1499/001/ + T1499.002: + id: T1499.002 + name: Service Exhaustion Flood + reference: https://attack.mitre.org/techniques/T1499/002/ + T1499.003: + id: T1499.003 + name: Application Exhaustion Flood + reference: https://attack.mitre.org/techniques/T1499/003/ + T1499.004: + id: T1499.004 + name: Application or System Exploitation + reference: https://attack.mitre.org/techniques/T1499/004/ + T1505.001: + id: T1505.001 + name: SQL Stored Procedures + reference: https://attack.mitre.org/techniques/T1505/001/ + T1505.002: + id: T1505.002 + name: Transport Agent + reference: https://attack.mitre.org/techniques/T1505/002/ + T1505.003: + id: T1505.003 + name: Web Shell + reference: https://attack.mitre.org/techniques/T1505/003/ + T1505.004: + id: T1505.004 + name: IIS Components + reference: https://attack.mitre.org/techniques/T1505/004/ + T1505.005: + id: T1505.005 + name: Terminal Services DLL + reference: https://attack.mitre.org/techniques/T1505/005/ + T1505.006: + id: T1505.006 + name: vSphere Installation Bundles + reference: https://attack.mitre.org/techniques/T1505/006/ + T1518.001: + id: T1518.001 + name: Security Software Discovery + reference: https://attack.mitre.org/techniques/T1518/001/ + T1518.002: + id: T1518.002 + name: Backup Software Discovery + reference: https://attack.mitre.org/techniques/T1518/002/ + T1542.001: + id: T1542.001 + name: System Firmware + reference: https://attack.mitre.org/techniques/T1542/001/ + T1542.002: + id: T1542.002 + name: Component Firmware + reference: https://attack.mitre.org/techniques/T1542/002/ + T1542.003: + id: T1542.003 + name: Bootkit + reference: https://attack.mitre.org/techniques/T1542/003/ + T1542.004: + id: T1542.004 + name: ROMMONkit + reference: https://attack.mitre.org/techniques/T1542/004/ + T1542.005: + id: T1542.005 + name: TFTP Boot + reference: https://attack.mitre.org/techniques/T1542/005/ + T1543.001: + id: T1543.001 + name: Launch Agent + reference: https://attack.mitre.org/techniques/T1543/001/ + T1543.002: + id: T1543.002 + name: Systemd Service + reference: https://attack.mitre.org/techniques/T1543/002/ + T1543.003: + id: T1543.003 + name: Windows Service + reference: https://attack.mitre.org/techniques/T1543/003/ + T1543.004: + id: T1543.004 + name: Launch Daemon + reference: https://attack.mitre.org/techniques/T1543/004/ + T1543.005: + id: T1543.005 + name: Container Service + reference: https://attack.mitre.org/techniques/T1543/005/ + T1546.001: + id: T1546.001 + name: Change Default File Association + reference: https://attack.mitre.org/techniques/T1546/001/ + T1546.002: + id: T1546.002 + name: Screensaver + reference: https://attack.mitre.org/techniques/T1546/002/ + T1546.003: + id: T1546.003 + name: Windows Management Instrumentation Event Subscription + reference: https://attack.mitre.org/techniques/T1546/003/ + T1546.004: + id: T1546.004 + name: Unix Shell Configuration Modification + reference: https://attack.mitre.org/techniques/T1546/004/ + T1546.005: + id: T1546.005 + name: Trap + reference: https://attack.mitre.org/techniques/T1546/005/ + T1546.006: + id: T1546.006 + name: LC_LOAD_DYLIB Addition + reference: https://attack.mitre.org/techniques/T1546/006/ + T1546.007: + id: T1546.007 + name: Netsh Helper DLL + reference: https://attack.mitre.org/techniques/T1546/007/ + T1546.008: + id: T1546.008 + name: Accessibility Features + reference: https://attack.mitre.org/techniques/T1546/008/ + T1546.009: + id: T1546.009 + name: AppCert DLLs + reference: https://attack.mitre.org/techniques/T1546/009/ + T1546.010: + id: T1546.010 + name: AppInit DLLs + reference: https://attack.mitre.org/techniques/T1546/010/ + T1546.011: + id: T1546.011 + name: Application Shimming + reference: https://attack.mitre.org/techniques/T1546/011/ + T1546.012: + id: T1546.012 + name: Image File Execution Options Injection + reference: https://attack.mitre.org/techniques/T1546/012/ + T1546.013: + id: T1546.013 + name: PowerShell Profile + reference: https://attack.mitre.org/techniques/T1546/013/ + T1546.014: + id: T1546.014 + name: Emond + reference: https://attack.mitre.org/techniques/T1546/014/ + T1546.015: + id: T1546.015 + name: Component Object Model Hijacking + reference: https://attack.mitre.org/techniques/T1546/015/ + T1546.016: + id: T1546.016 + name: Installer Packages + reference: https://attack.mitre.org/techniques/T1546/016/ + T1546.017: + id: T1546.017 + name: Udev Rules + reference: https://attack.mitre.org/techniques/T1546/017/ + T1546.018: + id: T1546.018 + name: Python Startup Hooks + reference: https://attack.mitre.org/techniques/T1546/018/ + T1547.001: + id: T1547.001 + name: Registry Run Keys / Startup Folder + reference: https://attack.mitre.org/techniques/T1547/001/ + T1547.002: + id: T1547.002 + name: Authentication Package + reference: https://attack.mitre.org/techniques/T1547/002/ + T1547.003: + id: T1547.003 + name: Time Providers + reference: https://attack.mitre.org/techniques/T1547/003/ + T1547.004: + id: T1547.004 + name: Winlogon Helper DLL + reference: https://attack.mitre.org/techniques/T1547/004/ + T1547.005: + id: T1547.005 + name: Security Support Provider + reference: https://attack.mitre.org/techniques/T1547/005/ + T1547.006: + id: T1547.006 + name: Kernel Modules and Extensions + reference: https://attack.mitre.org/techniques/T1547/006/ + T1547.007: + id: T1547.007 + name: Re-opened Applications + reference: https://attack.mitre.org/techniques/T1547/007/ + T1547.008: + id: T1547.008 + name: LSASS Driver + reference: https://attack.mitre.org/techniques/T1547/008/ + T1547.009: + id: T1547.009 + name: Shortcut Modification + reference: https://attack.mitre.org/techniques/T1547/009/ + T1547.010: + id: T1547.010 + name: Port Monitors + reference: https://attack.mitre.org/techniques/T1547/010/ + T1547.012: + id: T1547.012 + name: Print Processors + reference: https://attack.mitre.org/techniques/T1547/012/ + T1547.013: + id: T1547.013 + name: XDG Autostart Entries + reference: https://attack.mitre.org/techniques/T1547/013/ + T1547.014: + id: T1547.014 + name: Active Setup + reference: https://attack.mitre.org/techniques/T1547/014/ + T1547.015: + id: T1547.015 + name: Login Items + reference: https://attack.mitre.org/techniques/T1547/015/ + T1548.001: + id: T1548.001 + name: Setuid and Setgid + reference: https://attack.mitre.org/techniques/T1548/001/ + T1548.002: + id: T1548.002 + name: Bypass User Account Control + reference: https://attack.mitre.org/techniques/T1548/002/ + T1548.003: + id: T1548.003 + name: Sudo and Sudo Caching + reference: https://attack.mitre.org/techniques/T1548/003/ + T1548.004: + id: T1548.004 + name: Elevated Execution with Prompt + reference: https://attack.mitre.org/techniques/T1548/004/ + T1548.005: + id: T1548.005 + name: Temporary Elevated Cloud Access + reference: https://attack.mitre.org/techniques/T1548/005/ + T1548.006: + id: T1548.006 + name: TCC Manipulation + reference: https://attack.mitre.org/techniques/T1548/006/ + T1550.001: + id: T1550.001 + name: Application Access Token + reference: https://attack.mitre.org/techniques/T1550/001/ + T1550.002: + id: T1550.002 + name: Pass the Hash + reference: https://attack.mitre.org/techniques/T1550/002/ + T1550.003: + id: T1550.003 + name: Pass the Ticket + reference: https://attack.mitre.org/techniques/T1550/003/ + T1550.004: + id: T1550.004 + name: Web Session Cookie + reference: https://attack.mitre.org/techniques/T1550/004/ + T1552.001: + id: T1552.001 + name: Credentials In Files + reference: https://attack.mitre.org/techniques/T1552/001/ + T1552.002: + id: T1552.002 + name: Credentials in Registry + reference: https://attack.mitre.org/techniques/T1552/002/ + T1552.003: + id: T1552.003 + name: Shell History + reference: https://attack.mitre.org/techniques/T1552/003/ + T1552.004: + id: T1552.004 + name: Private Keys + reference: https://attack.mitre.org/techniques/T1552/004/ + T1552.005: + id: T1552.005 + name: Cloud Instance Metadata API + reference: https://attack.mitre.org/techniques/T1552/005/ + T1552.006: + id: T1552.006 + name: Group Policy Preferences + reference: https://attack.mitre.org/techniques/T1552/006/ + T1552.007: + id: T1552.007 + name: Container API + reference: https://attack.mitre.org/techniques/T1552/007/ + T1552.008: + id: T1552.008 + name: Chat Messages + reference: https://attack.mitre.org/techniques/T1552/008/ + T1553.001: + id: T1553.001 + name: Gatekeeper Bypass + reference: https://attack.mitre.org/techniques/T1553/001/ + T1553.002: + id: T1553.002 + name: Code Signing + reference: https://attack.mitre.org/techniques/T1553/002/ + T1553.003: + id: T1553.003 + name: SIP and Trust Provider Hijacking + reference: https://attack.mitre.org/techniques/T1553/003/ + T1553.004: + id: T1553.004 + name: Install Root Certificate + reference: https://attack.mitre.org/techniques/T1553/004/ + T1553.005: + id: T1553.005 + name: Mark-of-the-Web Bypass + reference: https://attack.mitre.org/techniques/T1553/005/ + T1553.006: + id: T1553.006 + name: Code Signing Policy Modification + reference: https://attack.mitre.org/techniques/T1553/006/ + T1555.001: + id: T1555.001 + name: Keychain + reference: https://attack.mitre.org/techniques/T1555/001/ + T1555.002: + id: T1555.002 + name: Securityd Memory + reference: https://attack.mitre.org/techniques/T1555/002/ + T1555.003: + id: T1555.003 + name: Credentials from Web Browsers + reference: https://attack.mitre.org/techniques/T1555/003/ + T1555.004: + id: T1555.004 + name: Windows Credential Manager + reference: https://attack.mitre.org/techniques/T1555/004/ + T1555.005: + id: T1555.005 + name: Password Managers + reference: https://attack.mitre.org/techniques/T1555/005/ + T1555.006: + id: T1555.006 + name: Cloud Secrets Management Stores + reference: https://attack.mitre.org/techniques/T1555/006/ + T1556.001: + id: T1556.001 + name: Domain Controller Authentication + reference: https://attack.mitre.org/techniques/T1556/001/ + T1556.002: + id: T1556.002 + name: Password Filter DLL + reference: https://attack.mitre.org/techniques/T1556/002/ + T1556.003: + id: T1556.003 + name: Pluggable Authentication Modules + reference: https://attack.mitre.org/techniques/T1556/003/ + T1556.004: + id: T1556.004 + name: Network Device Authentication + reference: https://attack.mitre.org/techniques/T1556/004/ + T1556.005: + id: T1556.005 + name: Reversible Encryption + reference: https://attack.mitre.org/techniques/T1556/005/ + T1556.006: + id: T1556.006 + name: Multi-Factor Authentication + reference: https://attack.mitre.org/techniques/T1556/006/ + T1556.007: + id: T1556.007 + name: Hybrid Identity + reference: https://attack.mitre.org/techniques/T1556/007/ + T1556.008: + id: T1556.008 + name: Network Provider DLL + reference: https://attack.mitre.org/techniques/T1556/008/ + T1556.009: + id: T1556.009 + name: Conditional Access Policies + reference: https://attack.mitre.org/techniques/T1556/009/ + T1557.001: + id: T1557.001 + name: Name Resolution Poisoning and SMB Relay + reference: https://attack.mitre.org/techniques/T1557/001/ + T1557.002: + id: T1557.002 + name: ARP Cache Poisoning + reference: https://attack.mitre.org/techniques/T1557/002/ + T1557.003: + id: T1557.003 + name: DHCP Spoofing + reference: https://attack.mitre.org/techniques/T1557/003/ + T1557.004: + id: T1557.004 + name: Evil Twin + reference: https://attack.mitre.org/techniques/T1557/004/ + T1558.001: + id: T1558.001 + name: Golden Ticket + reference: https://attack.mitre.org/techniques/T1558/001/ + T1558.002: + id: T1558.002 + name: Silver Ticket + reference: https://attack.mitre.org/techniques/T1558/002/ + T1558.003: + id: T1558.003 + name: Kerberoasting + reference: https://attack.mitre.org/techniques/T1558/003/ + T1558.004: + id: T1558.004 + name: AS-REP Roasting + reference: https://attack.mitre.org/techniques/T1558/004/ + T1558.005: + id: T1558.005 + name: Ccache Files + reference: https://attack.mitre.org/techniques/T1558/005/ + T1559.001: + id: T1559.001 + name: Component Object Model + reference: https://attack.mitre.org/techniques/T1559/001/ + T1559.002: + id: T1559.002 + name: Dynamic Data Exchange + reference: https://attack.mitre.org/techniques/T1559/002/ + T1559.003: + id: T1559.003 + name: XPC Services + reference: https://attack.mitre.org/techniques/T1559/003/ + T1560.001: + id: T1560.001 + name: Archive via Utility + reference: https://attack.mitre.org/techniques/T1560/001/ + T1560.002: + id: T1560.002 + name: Archive via Library + reference: https://attack.mitre.org/techniques/T1560/002/ + T1560.003: + id: T1560.003 + name: Archive via Custom Method + reference: https://attack.mitre.org/techniques/T1560/003/ + T1561.001: + id: T1561.001 + name: Disk Content Wipe + reference: https://attack.mitre.org/techniques/T1561/001/ + T1561.002: + id: T1561.002 + name: Disk Structure Wipe + reference: https://attack.mitre.org/techniques/T1561/002/ + T1562.001: + id: T1562.001 + name: Disable or Modify Tools + reference: https://attack.mitre.org/techniques/T1562/001/ + T1562.002: + id: T1562.002 + name: Disable Windows Event Logging + reference: https://attack.mitre.org/techniques/T1562/002/ + T1562.003: + id: T1562.003 + name: Impair Command History Logging + reference: https://attack.mitre.org/techniques/T1562/003/ + T1562.004: + id: T1562.004 + name: Disable or Modify System Firewall + reference: https://attack.mitre.org/techniques/T1562/004/ + T1562.006: + id: T1562.006 + name: Indicator Blocking + reference: https://attack.mitre.org/techniques/T1562/006/ + T1562.007: + id: T1562.007 + name: Disable or Modify Cloud Firewall + reference: https://attack.mitre.org/techniques/T1562/007/ + T1562.008: + id: T1562.008 + name: Disable or Modify Cloud Logs + reference: https://attack.mitre.org/techniques/T1562/008/ + T1562.009: + id: T1562.009 + name: Safe Mode Boot + reference: https://attack.mitre.org/techniques/T1562/009/ + T1562.010: + id: T1562.010 + name: Downgrade Attack + reference: https://attack.mitre.org/techniques/T1562/010/ + T1562.011: + id: T1562.011 + name: Spoof Security Alerting + reference: https://attack.mitre.org/techniques/T1562/011/ + T1562.012: + id: T1562.012 + name: Disable or Modify Linux Audit System + reference: https://attack.mitre.org/techniques/T1562/012/ + T1562.013: + id: T1562.013 + name: Disable or Modify Network Device Firewall + reference: https://attack.mitre.org/techniques/T1562/013/ + T1563.001: + id: T1563.001 + name: SSH Hijacking + reference: https://attack.mitre.org/techniques/T1563/001/ + T1563.002: + id: T1563.002 + name: RDP Hijacking + reference: https://attack.mitre.org/techniques/T1563/002/ + T1564.001: + id: T1564.001 + name: Hidden Files and Directories + reference: https://attack.mitre.org/techniques/T1564/001/ + T1564.002: + id: T1564.002 + name: Hidden Users + reference: https://attack.mitre.org/techniques/T1564/002/ + T1564.003: + id: T1564.003 + name: Hidden Window + reference: https://attack.mitre.org/techniques/T1564/003/ + T1564.004: + id: T1564.004 + name: NTFS File Attributes + reference: https://attack.mitre.org/techniques/T1564/004/ + T1564.005: + id: T1564.005 + name: Hidden File System + reference: https://attack.mitre.org/techniques/T1564/005/ + T1564.006: + id: T1564.006 + name: Run Virtual Instance + reference: https://attack.mitre.org/techniques/T1564/006/ + T1564.007: + id: T1564.007 + name: VBA Stomping + reference: https://attack.mitre.org/techniques/T1564/007/ + T1564.008: + id: T1564.008 + name: Email Hiding Rules + reference: https://attack.mitre.org/techniques/T1564/008/ + T1564.009: + id: T1564.009 + name: Resource Forking + reference: https://attack.mitre.org/techniques/T1564/009/ + T1564.010: + id: T1564.010 + name: Process Argument Spoofing + reference: https://attack.mitre.org/techniques/T1564/010/ + T1564.011: + id: T1564.011 + name: Ignore Process Interrupts + reference: https://attack.mitre.org/techniques/T1564/011/ + T1564.012: + id: T1564.012 + name: File/Path Exclusions + reference: https://attack.mitre.org/techniques/T1564/012/ + T1564.013: + id: T1564.013 + name: Bind Mounts + reference: https://attack.mitre.org/techniques/T1564/013/ + T1564.014: + id: T1564.014 + name: Extended Attributes + reference: https://attack.mitre.org/techniques/T1564/014/ + T1565.001: + id: T1565.001 + name: Stored Data Manipulation + reference: https://attack.mitre.org/techniques/T1565/001/ + T1565.002: + id: T1565.002 + name: Transmitted Data Manipulation + reference: https://attack.mitre.org/techniques/T1565/002/ + T1565.003: + id: T1565.003 + name: Runtime Data Manipulation + reference: https://attack.mitre.org/techniques/T1565/003/ + T1566.001: + id: T1566.001 + name: Spearphishing Attachment + reference: https://attack.mitre.org/techniques/T1566/001/ + T1566.002: + id: T1566.002 + name: Spearphishing Link + reference: https://attack.mitre.org/techniques/T1566/002/ + T1566.003: + id: T1566.003 + name: Spearphishing via Service + reference: https://attack.mitre.org/techniques/T1566/003/ + T1566.004: + id: T1566.004 + name: Spearphishing Voice + reference: https://attack.mitre.org/techniques/T1566/004/ + T1567.001: + id: T1567.001 + name: Exfiltration to Code Repository + reference: https://attack.mitre.org/techniques/T1567/001/ + T1567.002: + id: T1567.002 + name: Exfiltration to Cloud Storage + reference: https://attack.mitre.org/techniques/T1567/002/ + T1567.003: + id: T1567.003 + name: Exfiltration to Text Storage Sites + reference: https://attack.mitre.org/techniques/T1567/003/ + T1567.004: + id: T1567.004 + name: Exfiltration Over Webhook + reference: https://attack.mitre.org/techniques/T1567/004/ + T1568.001: + id: T1568.001 + name: Fast Flux DNS + reference: https://attack.mitre.org/techniques/T1568/001/ + T1568.002: + id: T1568.002 + name: Domain Generation Algorithms + reference: https://attack.mitre.org/techniques/T1568/002/ + T1568.003: + id: T1568.003 + name: DNS Calculation + reference: https://attack.mitre.org/techniques/T1568/003/ + T1569.001: + id: T1569.001 + name: Launchctl + reference: https://attack.mitre.org/techniques/T1569/001/ + T1569.002: + id: T1569.002 + name: Service Execution + reference: https://attack.mitre.org/techniques/T1569/002/ + T1569.003: + id: T1569.003 + name: Systemctl + reference: https://attack.mitre.org/techniques/T1569/003/ + T1573.001: + id: T1573.001 + name: Symmetric Cryptography + reference: https://attack.mitre.org/techniques/T1573/001/ + T1573.002: + id: T1573.002 + name: Asymmetric Cryptography + reference: https://attack.mitre.org/techniques/T1573/002/ + T1574.001: + id: T1574.001 + name: DLL + reference: https://attack.mitre.org/techniques/T1574/001/ + T1574.004: + id: T1574.004 + name: Dylib Hijacking + reference: https://attack.mitre.org/techniques/T1574/004/ + T1574.005: + id: T1574.005 + name: Executable Installer File Permissions Weakness + reference: https://attack.mitre.org/techniques/T1574/005/ + T1574.006: + id: T1574.006 + name: Dynamic Linker Hijacking + reference: https://attack.mitre.org/techniques/T1574/006/ + T1574.007: + id: T1574.007 + name: Path Interception by PATH Environment Variable + reference: https://attack.mitre.org/techniques/T1574/007/ + T1574.008: + id: T1574.008 + name: Path Interception by Search Order Hijacking + reference: https://attack.mitre.org/techniques/T1574/008/ + T1574.009: + id: T1574.009 + name: Path Interception by Unquoted Path + reference: https://attack.mitre.org/techniques/T1574/009/ + T1574.010: + id: T1574.010 + name: Services File Permissions Weakness + reference: https://attack.mitre.org/techniques/T1574/010/ + T1574.011: + id: T1574.011 + name: Services Registry Permissions Weakness + reference: https://attack.mitre.org/techniques/T1574/011/ + T1574.012: + id: T1574.012 + name: COR_PROFILER + reference: https://attack.mitre.org/techniques/T1574/012/ + T1574.013: + id: T1574.013 + name: KernelCallbackTable + reference: https://attack.mitre.org/techniques/T1574/013/ + T1574.014: + id: T1574.014 + name: AppDomainManager + reference: https://attack.mitre.org/techniques/T1574/014/ + T1578.001: + id: T1578.001 + name: Create Snapshot + reference: https://attack.mitre.org/techniques/T1578/001/ + T1578.002: + id: T1578.002 + name: Create Cloud Instance + reference: https://attack.mitre.org/techniques/T1578/002/ + T1578.003: + id: T1578.003 + name: Delete Cloud Instance + reference: https://attack.mitre.org/techniques/T1578/003/ + T1578.004: + id: T1578.004 + name: Revert Cloud Instance + reference: https://attack.mitre.org/techniques/T1578/004/ + T1578.005: + id: T1578.005 + name: Modify Cloud Compute Configurations + reference: https://attack.mitre.org/techniques/T1578/005/ + T1583.001: + id: T1583.001 + name: Domains + reference: https://attack.mitre.org/techniques/T1583/001/ + T1583.002: + id: T1583.002 + name: DNS Server + reference: https://attack.mitre.org/techniques/T1583/002/ + T1583.003: + id: T1583.003 + name: Virtual Private Server + reference: https://attack.mitre.org/techniques/T1583/003/ + T1583.004: + id: T1583.004 + name: Server + reference: https://attack.mitre.org/techniques/T1583/004/ + T1583.005: + id: T1583.005 + name: Botnet + reference: https://attack.mitre.org/techniques/T1583/005/ + T1583.006: + id: T1583.006 + name: Web Services + reference: https://attack.mitre.org/techniques/T1583/006/ + T1583.007: + id: T1583.007 + name: Serverless + reference: https://attack.mitre.org/techniques/T1583/007/ + T1583.008: + id: T1583.008 + name: Malvertising + reference: https://attack.mitre.org/techniques/T1583/008/ + T1584.001: + id: T1584.001 + name: Domains + reference: https://attack.mitre.org/techniques/T1584/001/ + T1584.002: + id: T1584.002 + name: DNS Server + reference: https://attack.mitre.org/techniques/T1584/002/ + T1584.003: + id: T1584.003 + name: Virtual Private Server + reference: https://attack.mitre.org/techniques/T1584/003/ + T1584.004: + id: T1584.004 + name: Server + reference: https://attack.mitre.org/techniques/T1584/004/ + T1584.005: + id: T1584.005 + name: Botnet + reference: https://attack.mitre.org/techniques/T1584/005/ + T1584.006: + id: T1584.006 + name: Web Services + reference: https://attack.mitre.org/techniques/T1584/006/ + T1584.007: + id: T1584.007 + name: Serverless + reference: https://attack.mitre.org/techniques/T1584/007/ + T1584.008: + id: T1584.008 + name: Network Devices + reference: https://attack.mitre.org/techniques/T1584/008/ + T1585.001: + id: T1585.001 + name: Social Media Accounts + reference: https://attack.mitre.org/techniques/T1585/001/ + T1585.002: + id: T1585.002 + name: Email Accounts + reference: https://attack.mitre.org/techniques/T1585/002/ + T1585.003: + id: T1585.003 + name: Cloud Accounts + reference: https://attack.mitre.org/techniques/T1585/003/ + T1586.001: + id: T1586.001 + name: Social Media Accounts + reference: https://attack.mitre.org/techniques/T1586/001/ + T1586.002: + id: T1586.002 + name: Email Accounts + reference: https://attack.mitre.org/techniques/T1586/002/ + T1586.003: + id: T1586.003 + name: Cloud Accounts + reference: https://attack.mitre.org/techniques/T1586/003/ + T1587.001: + id: T1587.001 + name: Malware + reference: https://attack.mitre.org/techniques/T1587/001/ + T1587.002: + id: T1587.002 + name: Code Signing Certificates + reference: https://attack.mitre.org/techniques/T1587/002/ + T1587.003: + id: T1587.003 + name: Digital Certificates + reference: https://attack.mitre.org/techniques/T1587/003/ + T1587.004: + id: T1587.004 + name: Exploits + reference: https://attack.mitre.org/techniques/T1587/004/ + T1588.001: + id: T1588.001 + name: Malware + reference: https://attack.mitre.org/techniques/T1588/001/ + T1588.002: + id: T1588.002 + name: Tool + reference: https://attack.mitre.org/techniques/T1588/002/ + T1588.003: + id: T1588.003 + name: Code Signing Certificates + reference: https://attack.mitre.org/techniques/T1588/003/ + T1588.004: + id: T1588.004 + name: Digital Certificates + reference: https://attack.mitre.org/techniques/T1588/004/ + T1588.005: + id: T1588.005 + name: Exploits + reference: https://attack.mitre.org/techniques/T1588/005/ + T1588.006: + id: T1588.006 + name: Vulnerabilities + reference: https://attack.mitre.org/techniques/T1588/006/ + T1588.007: + id: T1588.007 + name: Artificial Intelligence + reference: https://attack.mitre.org/techniques/T1588/007/ + T1589.001: + id: T1589.001 + name: Credentials + reference: https://attack.mitre.org/techniques/T1589/001/ + T1589.002: + id: T1589.002 + name: Email Addresses + reference: https://attack.mitre.org/techniques/T1589/002/ + T1589.003: + id: T1589.003 + name: Employee Names + reference: https://attack.mitre.org/techniques/T1589/003/ + T1590.001: + id: T1590.001 + name: Domain Properties + reference: https://attack.mitre.org/techniques/T1590/001/ + T1590.002: + id: T1590.002 + name: DNS + reference: https://attack.mitre.org/techniques/T1590/002/ + T1590.003: + id: T1590.003 + name: Network Trust Dependencies + reference: https://attack.mitre.org/techniques/T1590/003/ + T1590.004: + id: T1590.004 + name: Network Topology + reference: https://attack.mitre.org/techniques/T1590/004/ + T1590.005: + id: T1590.005 + name: IP Addresses + reference: https://attack.mitre.org/techniques/T1590/005/ + T1590.006: + id: T1590.006 + name: Network Security Appliances + reference: https://attack.mitre.org/techniques/T1590/006/ + T1591.001: + id: T1591.001 + name: Determine Physical Locations + reference: https://attack.mitre.org/techniques/T1591/001/ + T1591.002: + id: T1591.002 + name: Business Relationships + reference: https://attack.mitre.org/techniques/T1591/002/ + T1591.003: + id: T1591.003 + name: Identify Business Tempo + reference: https://attack.mitre.org/techniques/T1591/003/ + T1591.004: + id: T1591.004 + name: Identify Roles + reference: https://attack.mitre.org/techniques/T1591/004/ + T1592.001: + id: T1592.001 + name: Hardware + reference: https://attack.mitre.org/techniques/T1592/001/ + T1592.002: + id: T1592.002 + name: Software + reference: https://attack.mitre.org/techniques/T1592/002/ + T1592.003: + id: T1592.003 + name: Firmware + reference: https://attack.mitre.org/techniques/T1592/003/ + T1592.004: + id: T1592.004 + name: Client Configurations + reference: https://attack.mitre.org/techniques/T1592/004/ + T1593.001: + id: T1593.001 + name: Social Media + reference: https://attack.mitre.org/techniques/T1593/001/ + T1593.002: + id: T1593.002 + name: Search Engines + reference: https://attack.mitre.org/techniques/T1593/002/ + T1593.003: + id: T1593.003 + name: Code Repositories + reference: https://attack.mitre.org/techniques/T1593/003/ + T1595.001: + id: T1595.001 + name: Scanning IP Blocks + reference: https://attack.mitre.org/techniques/T1595/001/ + T1595.002: + id: T1595.002 + name: Vulnerability Scanning + reference: https://attack.mitre.org/techniques/T1595/002/ + T1595.003: + id: T1595.003 + name: Wordlist Scanning + reference: https://attack.mitre.org/techniques/T1595/003/ + T1596.001: + id: T1596.001 + name: DNS/Passive DNS + reference: https://attack.mitre.org/techniques/T1596/001/ + T1596.002: + id: T1596.002 + name: WHOIS + reference: https://attack.mitre.org/techniques/T1596/002/ + T1596.003: + id: T1596.003 + name: Digital Certificates + reference: https://attack.mitre.org/techniques/T1596/003/ + T1596.004: + id: T1596.004 + name: CDNs + reference: https://attack.mitre.org/techniques/T1596/004/ + T1596.005: + id: T1596.005 + name: Scan Databases + reference: https://attack.mitre.org/techniques/T1596/005/ + T1597.001: + id: T1597.001 + name: Threat Intel Vendors + reference: https://attack.mitre.org/techniques/T1597/001/ + T1597.002: + id: T1597.002 + name: Purchase Technical Data + reference: https://attack.mitre.org/techniques/T1597/002/ + T1598.001: + id: T1598.001 + name: Spearphishing Service + reference: https://attack.mitre.org/techniques/T1598/001/ + T1598.002: + id: T1598.002 + name: Spearphishing Attachment + reference: https://attack.mitre.org/techniques/T1598/002/ + T1598.003: + id: T1598.003 + name: Spearphishing Link + reference: https://attack.mitre.org/techniques/T1598/003/ + T1598.004: + id: T1598.004 + name: Spearphishing Voice + reference: https://attack.mitre.org/techniques/T1598/004/ + T1599.001: + id: T1599.001 + name: Network Address Translation Traversal + reference: https://attack.mitre.org/techniques/T1599/001/ + T1600.001: + id: T1600.001 + name: Reduce Key Space + reference: https://attack.mitre.org/techniques/T1600/001/ + T1600.002: + id: T1600.002 + name: Disable Crypto Hardware + reference: https://attack.mitre.org/techniques/T1600/002/ + T1601.001: + id: T1601.001 + name: Patch System Image + reference: https://attack.mitre.org/techniques/T1601/001/ + T1601.002: + id: T1601.002 + name: Downgrade System Image + reference: https://attack.mitre.org/techniques/T1601/002/ + T1602.001: + id: T1602.001 + name: SNMP (MIB Dump) + reference: https://attack.mitre.org/techniques/T1602/001/ + T1602.002: + id: T1602.002 + name: Network Device Configuration Dump + reference: https://attack.mitre.org/techniques/T1602/002/ + T1606.001: + id: T1606.001 + name: Web Cookies + reference: https://attack.mitre.org/techniques/T1606/001/ + T1606.002: + id: T1606.002 + name: SAML Tokens + reference: https://attack.mitre.org/techniques/T1606/002/ + T1608.001: + id: T1608.001 + name: Upload Malware + reference: https://attack.mitre.org/techniques/T1608/001/ + T1608.002: + id: T1608.002 + name: Upload Tool + reference: https://attack.mitre.org/techniques/T1608/002/ + T1608.003: + id: T1608.003 + name: Install Digital Certificate + reference: https://attack.mitre.org/techniques/T1608/003/ + T1608.004: + id: T1608.004 + name: Drive-by Target + reference: https://attack.mitre.org/techniques/T1608/004/ + T1608.005: + id: T1608.005 + name: Link Target + reference: https://attack.mitre.org/techniques/T1608/005/ + T1608.006: + id: T1608.006 + name: SEO Poisoning + reference: https://attack.mitre.org/techniques/T1608/006/ + T1614.001: + id: T1614.001 + name: System Language Discovery + reference: https://attack.mitre.org/techniques/T1614/001/ diff --git a/detection_rules/rule.py b/detection_rules/rule.py index 944db5ec5d5..e075fef281c 100644 --- a/detection_rules/rule.py +++ b/detection_rules/rule.py @@ -7,6 +7,7 @@ import copy import dataclasses import json +import logging import os import re import time @@ -15,7 +16,7 @@ from dataclasses import dataclass, field from functools import cached_property from pathlib import Path -from typing import Any, Literal +from typing import Any, Literal, cast from urllib.parse import urlparse from uuid import uuid4 @@ -58,6 +59,8 @@ from .remote_validation import RemoteValidator +logger = logging.getLogger(__name__) + MIN_FLEET_PACKAGE_VERSION = "7.13.0" TIME_NOW = time.strftime("%Y/%m/%d") RULES_CONFIG = parse_rules_config() @@ -292,6 +295,27 @@ class FlatThreatMapping(MarshmallowDataclassMixin): sub_technique_ids: list[str] +@dataclass(frozen=True, kw_only=True) +class VersionedThreatMapping(MarshmallowDataclassMixin): + """Mapping to a threat framework tagged with the framework version it targets.""" + + framework: Literal["MITRE ATT&CK", "MITRE ATLAS"] + version: str + threat: list[ThreatMapping] + + @validates_schema + def validate_framework_agreement(self, data: dict[str, Any], **_: Any) -> None: + """Ensure inner threat entries use the same framework as the versioned wrapper.""" + framework = cast("str | None", data.get("framework")) + for entry in cast("list[dict[str, Any] | ThreatMapping]", data.get("threat") or []): + entry_framework = cast("str", entry["framework"] if isinstance(entry, dict) else entry.framework) + if entry_framework != framework: + raise ValidationError( + f"threat_mappings entry for {framework} version {data.get('version')} contains a " + f"threat with mismatched framework '{entry_framework}'" + ) + + @dataclass(frozen=True) class AlertSuppressionDuration: """Mapping to alert suppression duration.""" @@ -412,6 +436,9 @@ class RelatedIntegrations: severity_mapping: list[SeverityMapping] | None = None tags: list[str] | None = None threat: list[ThreatMapping] | None = None + # additional framework/version-tagged mappings; the build emits one as the API `threat` and strips + # this field (see `_select_output_threat_mapping`) + threat_mappings: list[VersionedThreatMapping] | None = None throttle: str | None = None timeline_id: definitions.TimelineTemplateId | None = None timeline_title: definitions.TimelineTemplateTitle | None = None @@ -505,6 +532,34 @@ def validates_data(self, data: dict[str, Any], **_: Any) -> None: ) raise ValidationError(msg) + @validates_schema + def validates_threat_mappings(self, data: dict[str, Any], **_: Any) -> None: + """Ensure versioned threat mappings have unique (framework, version) keys.""" + threat_mappings = data.get("threat_mappings") + if not threat_mappings: + return + + seen: set[tuple[str, str]] = set() + for entry in cast("list[dict[str, Any] | VersionedThreatMapping]", threat_mappings): + framework = cast("str", entry["framework"] if isinstance(entry, dict) else entry.framework) + version = cast("str", entry["version"] if isinstance(entry, dict) else entry.version) + key: tuple[str, str] = (framework, version) + if key in seen: + raise ValidationError( + f"Duplicate threat_mappings entry for framework '{framework}' version '{version}'" + ) + seen.add(key) + + def get_primary_tactic_names(self) -> list[str]: + """Return the primary tactic name from the baseline threat and every threat_mappings block.""" + names: list[str] = [] + if self.threat: + names.append(self.threat[0].tactic.name) + for versioned_block in self.threat_mappings or []: + if versioned_block.threat and versioned_block.threat[0].tactic.name not in names: + names.append(versioned_block.threat[0].tactic.name) + return names + class DataValidator: """Additional validation beyond base marshmallow schema validation.""" @@ -1142,6 +1197,43 @@ def validate(self, meta: RuleMeta) -> None: # noqa: ARG002 ) +def _normalize_threat_for_hash(threat: list[dict[str, Any]]) -> list[dict[str, Any]]: + """Strip threat entry names and sort by ID so the hash reflects mapping content only.""" + # ID and reference are kept, so adding, removing, or changing an ID still triggers a version bump. + # Sorting by ID (rather than authored order) means reordering `[[rule.threat]]` blocks - which + # happens naturally when generating a versioned mapping - doesn't itself trigger a version bump. + result: list[dict[str, Any]] = [] + for entry in threat: + normalized: dict[str, Any] = {"framework": cast("str | None", entry.get("framework"))} + tactic = cast("dict[str, Any]", entry.get("tactic") or {}) + normalized["tactic"] = { + "id": cast("str | None", tactic.get("id")), + "reference": cast("str | None", tactic.get("reference")), + } + techniques: list[dict[str, Any]] = [] + for tech in cast("list[dict[str, Any]]", entry.get("technique") or []): + t: dict[str, Any] = { + "id": cast("str | None", tech.get("id")), + "reference": cast("str | None", tech.get("reference")), + } + subs: list[dict[str, Any]] = sorted( + ( + {"id": cast("str | None", sub.get("id")), "reference": cast("str | None", sub.get("reference"))} + for sub in cast("list[dict[str, Any]]", tech.get("subtechnique") or []) + ), + key=lambda s: s["id"] or "", + ) + if subs: + t["subtechnique"] = subs + techniques.append(t) + techniques.sort(key=lambda t: t["id"] or "") + if techniques: + normalized["technique"] = techniques + result.append(normalized) + result.sort(key=lambda e: (e["tactic"]["id"] or "", e["framework"] or "")) + return result + + class BaseRuleContents(ABC): """Base contents object for shared methods between active and deprecated rules.""" @@ -1284,12 +1376,61 @@ def _post_dict_conversion(self, obj: dict[str, Any]) -> dict[str, Any]: # cleanup the whitespace in the rule obj = nested_normalize(obj) + # select which framework/version threat mapping is emitted as the API `threat`, then strip + # the repo-side `threat_mappings` field so it is never shipped to Kibana + self._select_output_threat_mapping(obj) + # fill in threat.technique so it's never missing for threat_entry in obj.get("threat", []): threat_entry.setdefault("technique", []) return obj + @staticmethod + def _select_output_threat_mapping(obj: dict[str, Any]) -> None: + """Emit the configured framework/version mapping as `threat` and drop `threat_mappings`.""" + from . import attack + from .config import DEFAULT_THREAT_MAPPING_FRAMEWORK, DEFAULT_THREAT_MAPPING_VERSION + + threat_mappings = obj.pop("threat_mappings", None) + + framework, version = attack.resolve_output_threat_version() + + # The baseline framework/version lives in the `threat` field itself, so there is nothing to + # select or warn about when it is requested (the common case). + if framework == DEFAULT_THREAT_MAPPING_FRAMEWORK and str(version) == str(DEFAULT_THREAT_MAPPING_VERSION): + return + + if not threat_mappings: + if obj.get("threat"): + logger.warning( + "No threat mapping for framework '%s' version '%s' on rule '%s'; " + "emitting the default `threat` mapping instead.", + framework, + version, + obj.get("name", obj.get("rule_id", "")), + ) + return + + selected = next( + ( + block + for block in threat_mappings + if block.get("framework") == framework and str(block.get("version")) == str(version) + ), + None, + ) + if selected is not None: + obj["threat"] = selected.get("threat", []) + elif obj.get("threat"): + logger.warning( + "No threat mapping for framework '%s' version '%s' on rule '%s'; " + "emitting the default `threat` mapping instead.", + framework, + version, + obj.get("name", obj.get("rule_id", "")), + ) + def _uses_keep_star(self, hashable_dict: dict[str, Any]) -> bool: """Check if this is an ES|QL rule that uses `| keep *` or fields ending with '*'.""" if hashable_dict.get("language") != "esql": @@ -1325,6 +1466,12 @@ def get_hashable_content(self, include_version: bool = False, include_integratio if self._uses_keep_star(hashable_dict): hashable_dict.pop("required_fields", None) + # Strip tactic/technique names so ATT&CK renames between versions (e.g. "Defense Evasion" + # -> "Stealth") don't trigger a version bump. ID and reference are preserved, so genuine + # mapping changes (added/removed entries, ID changes) still produce a new hash. + if hashable_dict.get("threat"): + hashable_dict["threat"] = _normalize_threat_for_hash(hashable_dict["threat"]) + return hashable_dict @cached diff --git a/detection_rules/schemas/definitions.py b/detection_rules/schemas/definitions.py index 65e99fa5b71..27376159cd4 100644 --- a/detection_rules/schemas/definitions.py +++ b/detection_rules/schemas/definitions.py @@ -178,6 +178,7 @@ def validator_wrapper(value: Any) -> Any: "Tactic: Command and Control", "Tactic: Credential Access", "Tactic: Defense Evasion", + "Tactic: Defense Impairment", "Tactic: Discovery", "Tactic: Execution", "Tactic: Exfiltration", @@ -188,6 +189,7 @@ def validator_wrapper(value: Any) -> Any: "Tactic: Privilege Escalation", "Tactic: Reconnaissance", "Tactic: Resource Development", + "Tactic: Stealth", "Threat: BPFDoor", "Threat: Cobalt Strike", "Threat: Lightning Framework", diff --git a/docs-dev/multi-version-threat-mappings.md b/docs-dev/multi-version-threat-mappings.md new file mode 100644 index 00000000000..e212bf8bd47 --- /dev/null +++ b/docs-dev/multi-version-threat-mappings.md @@ -0,0 +1,129 @@ +# Multi-version threat mappings (MITRE ATT&CK v18 / v19 / …) + +A rule's MITRE ATT&CK mapping lives in its `threat` field, which is shipped verbatim to Kibana. To +support more than one ATT&CK version (or, in future, other frameworks) at once, a rule can also carry +additional, version-tagged mappings in a `threat_mappings` field. These are **repo-side only** — the +build selects exactly one mapping to emit as the API `threat` and strips `threat_mappings` from the +shipped artifact. + +This makes it possible to author and review a v19 mapping today, while continuing to ship v18, and to +flip the shipped version later with a single config/env change. + +## Rule schema + +`threat` continues to hold the **baseline** mapping (MITRE ATT&CK v18). Additional versions are added +as `threat_mappings` entries, each keeping the same structure as `threat` plus a `framework` and a +`version`: + +```toml +[[rule.threat]] # baseline (v18) — shipped by default, consumed by Kibana +framework = "MITRE ATT&CK" + [[rule.threat.technique]] + id = "T1078" + name = "Valid Accounts" + reference = "https://attack.mitre.org/techniques/T1078/" + [rule.threat.tactic] + id = "TA0001" + name = "Initial Access" + reference = "https://attack.mitre.org/tactics/TA0001/" + +[[rule.threat_mappings]] # additional, version-tagged mapping (repo-side only) +framework = "MITRE ATT&CK" +version = "19" + [[rule.threat_mappings.threat]] + framework = "MITRE ATT&CK" + # ...same tactic/technique/subtechnique structure as `threat`... +``` + +Validation enforces unique `(framework, version)` blocks and that each inner threat entry's framework +matches its wrapper. + +## Selecting which version is shipped + +At build time (`TOMLRuleContents.to_api_format`), the configured framework/version is emitted as +`threat`: + +- Default is the baseline `(MITRE ATT&CK, 18)` — `threat` is emitted unchanged. +- When another version is configured, the matching `threat_mappings` block is emitted as `threat`. +- If a rule has no block for the requested version, its baseline `threat` is emitted (with a warning), + so packaging never breaks. +- `threat_mappings` is always stripped from the API output. + +Set the output version with either config keys or environment variables (env takes precedence): + +```yaml +# _config.yaml +threat_mapping_framework: "MITRE ATT&CK" +threat_mapping_version: "19" +``` + +```bash +DR_THREAT_MAPPING_FRAMEWORK="MITRE ATT&CK" DR_THREAT_MAPPING_VERSION=19 python -m detection_rules ... +``` + +Because the field is stripped from the API format, adding `threat_mappings` to rules does **not** +change rule hashes or trigger version bumps until the output version is actually flipped. + +## Generating a target-version mapping + +`dev attack convert-threat-mappings` does a first-pass generation of a target-version mapping from a +rule's source mapping. It is **accuracy-first**: any source tactic/technique/subtechnique that is not +explicitly present in the mapping config is **dropped rather than guessed**, and every dropped item is +reported for review. + +```bash +# preview the v18 -> v19 conversion for all rules +python -m detection_rules dev attack convert-threat-mappings -t 19 --dry-run + +# write the v19 blocks (scope with -d/--directory, -f/--rule-file, -id/--rule-id) +python -m detection_rules dev attack convert-threat-mappings -t 19 -d rules/ + +# use an explicit mapping config instead of the configured directory +python -m detection_rules dev attack convert-threat-mappings -t 19 --config path/to/map.yaml +``` + +## Mapping config format + +Conversion is driven by a **directory of per-pair config files** (default +`detection_rules/etc/attack-version-maps/`). Each file declares one `(framework, source_version -> +target_version)` triple and is **self-contained** — it carries the destination `id`, `name`, and +`reference` for each source id. A source id mapped to `null`, or absent entirely, is dropped. + +```yaml +framework: "MITRE ATT&CK" +source_version: "18" +target_version: "19" +tactics: + TA0001: { id: TA0001, name: Initial Access, reference: "https://attack.mitre.org/tactics/TA0001/" } +techniques: + T1078: { id: T1078, name: "Valid Accounts", reference: "https://attack.mitre.org/techniques/T1078/" } + T1100: null # explicitly dropped (deprecated / no confident target) +subtechniques: + T1078.004: { id: T1078.004, name: "Cloud Accounts", reference: "https://attack.mitre.org/techniques/T1078/004/" } +``` + +Adding a new destination (e.g. v19 -> v20, or another framework) is just a new file declaring its own +triple. Multiple destinations coexist as separate `threat_mappings` blocks per rule. + +### Scaffolding a config + +Because the config is self-contained, `dev attack scaffold-version-map` generates an **identity** +baseline from the currently loaded ATT&CK data (every non-revoked/non-deprecated id maps to itself). +Review and curate it against the target version's real changes (renames, deprecations, splits) before +relying on it: + +```bash +python -m detection_rules dev attack scaffold-version-map -t 19 -o detection_rules/etc/attack-version-maps/attack_v18_to_v19.yaml +``` + +## Detections-as-Code (custom rules) + +All of the above is DaC-aware. With `CUSTOM_RULES_DIR` set, the commands operate on the custom rule +directories automatically. Point a custom rules repo at its own mapping configs via the custom +`_config.yaml`: + +```yaml +attack_version_maps_dir: etc/attack-version-maps # relative to the custom _config.yaml +``` + +or pass `--config` explicitly. The output-selection keys/env vars work the same way for custom rules. diff --git a/pyproject.toml b/pyproject.toml index 1404a2d4cae..16844e966b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "detection_rules" -version = "1.7.5" +version = "1.7.6" description = "Detection Rules is the home for rules used by Elastic Security. This repository is used for the development, maintenance, testing, validation, and release of rules for Elastic Security’s Detection Engine." readme = "README.md" requires-python = ">=3.12" diff --git a/tests/test_all_rules.py b/tests/test_all_rules.py index 0dd2723faa5..b419640f663 100644 --- a/tests/test_all_rules.py +++ b/tests/test_all_rules.py @@ -376,6 +376,104 @@ def test_duplicated_tactics(self): f"Flatten to a single entry per tactic" ) + def test_versioned_threat_mappings_tactic_technique_correlations(self): + """Validate threat_mappings entries against their declared version's ATT&CK data.""" + for rule in self.all_rules: + threat_mappings = rule.contents.data.threat_mappings + if not threat_mappings: + continue + for versioned_block in threat_mappings: + if versioned_block.framework != "MITRE ATT&CK": + continue + try: + lookups = attack.build_attack_lookups_for_version(versioned_block.version) + except FileNotFoundError: + continue # no local data for this version; validation skipped + + for entry in versioned_block.threat: + tactic = entry.tactic + techniques = entry.technique or [] + version_label = f"v{versioned_block.version}" + + if tactic.name not in lookups.tactics_map: + self.fail( + f"{self.rule_str(rule)} threat_mappings {version_label}: " + f"unknown ATT&CK tactic '{tactic.name}'" + ) + + expected_tactic_id = lookups.tactics_map[tactic.name] + self.assertEqual( + expected_tactic_id, + tactic.id, + f"{self.rule_str(rule)} threat_mappings {version_label}: " + f"tactic ID mismatch for '{tactic.name}': " + f"expected {expected_tactic_id}, got {tactic.id}", + ) + + mismatched = [t.id for t in techniques if t.id not in lookups.matrix.get(tactic.name, [])] + if mismatched: + self.fail( + f"{self.rule_str(rule)} threat_mappings {version_label}: " + f"techniques {mismatched} not under tactic '{tactic.name}' " + f"in ATT&CK {version_label}" + ) + + for technique in techniques: + if technique.id not in lookups.technique_lookup: + self.fail( + f"{self.rule_str(rule)} threat_mappings {version_label}: " + f"unknown ATT&CK technique ID '{technique.id}'" + ) + expected_name = lookups.technique_lookup[technique.id]["name"] + self.assertEqual( + expected_name, + technique.name, + f"{self.rule_str(rule)} threat_mappings {version_label}: " + f"technique name mismatch for {technique.id}: " + f"expected '{expected_name}', got '{technique.name}'", + ) + + for sub in technique.subtechnique or []: + if sub.id not in lookups.technique_lookup: + self.fail( + f"{self.rule_str(rule)} threat_mappings {version_label}: " + f"unknown ATT&CK subtechnique ID '{sub.id}'" + ) + expected_sub_name = lookups.technique_lookup[sub.id]["name"] + self.assertEqual( + expected_sub_name, + sub.name, + f"{self.rule_str(rule)} threat_mappings {version_label}: " + f"subtechnique name mismatch for {sub.id}: " + f"expected '{expected_sub_name}', got '{sub.name}'", + ) + + def test_versioned_threat_mappings_deprecations(self): + """Check that threat_mappings entries don't use techniques deprecated in their declared version.""" + for rule in self.all_rules: + threat_mappings = rule.contents.data.threat_mappings + if not threat_mappings: + continue + for versioned_block in threat_mappings: + if versioned_block.framework != "MITRE ATT&CK": + continue + try: + lookups = attack.build_attack_lookups_for_version(versioned_block.version) + except FileNotFoundError: + continue + + bad: dict[str, str] = {} + for entry in versioned_block.threat: + for technique in entry.technique or []: + if technique.id in lookups.revoked or technique.id in lookups.deprecated: + bad[technique.id] = f"revoked/deprecated in ATT&CK v{versioned_block.version}" + + if bad: + self.fail( + f"{self.rule_str(rule)} threat_mappings v{versioned_block.version} " + f"uses deprecated/revoked techniques: " + ", ".join(f"{k} ({v})" for k, v in bad.items()) + ) + @unittest.skipIf(os.environ.get("DR_BYPASS_TAGS_VALIDATION") is not None, "Skipping tag validation") class TestRuleTags(BaseRuleTest): @@ -488,11 +586,16 @@ def test_primary_tactic_as_tag(self): if threat: missing = [] threat_tactic_names = [e.tactic.name for e in threat] - primary_tactic = f"Tactic: {threat_tactic_names[0]}" + + # Accept a primary-tactic tag matching the baseline or any versioned (e.g. v19) + # threat_mappings tactic name, since renamed tactics (e.g. Stealth) are still valid. + primary_tactic_candidates = { + f"Tactic: {name}" for name in rule.contents.data.get_primary_tactic_names() + } # missing primary tactic - if primary_tactic not in rule.contents.data.tags: - missing.append(primary_tactic) + if not primary_tactic_candidates.intersection(rule.contents.data.tags): + missing.append(sorted(primary_tactic_candidates)) # listed tactic that is not in threat mapping tag_tactics = set(rule_tags).intersection(tactics) @@ -501,7 +604,7 @@ def test_primary_tactic_as_tag(self): if missing or missing_from_threat: err_msg = self.rule_str(rule) if missing: - err_msg += f"\n expected: {missing}" + err_msg += f"\n expected one of: {missing}" if missing_from_threat: err_msg += f"\n unexpected (or missing from threat mapping): {missing_from_threat}" @@ -641,11 +744,14 @@ def test_rule_file_name_tactic(self): authors = rule.contents.data.author if threat and "Elastic" in authors: - primary_tactic = threat[0].tactic.name - tactic_str = primary_tactic.lower().replace(" ", "_") - - if tactic_str != filename[: len(tactic_str)]: - bad_name_rules.append(f"{rule.id} - {Path(rule.path).name} -> expected: {tactic_str}") + # Accept the baseline tactic name or any versioned (e.g. v19) threat_mappings tactic + # name, so a rule isn't forced to rename its file until it's actually migrated. + primary_tactics = rule.contents.data.get_primary_tactic_names() + tactic_strs = [t.lower().replace(" ", "_") for t in primary_tactics] + + if not any(filename.startswith(tactic_str) for tactic_str in tactic_strs): + expected = " or ".join(tactic_strs) + bad_name_rules.append(f"{rule.id} - {Path(rule.path).name} -> expected: {expected}") if bad_name_rules: error_msg = "filename does not start with the primary tactic - update the tactic or the rule filename" diff --git a/tests/test_threat_mappings.py b/tests/test_threat_mappings.py new file mode 100644 index 00000000000..3ae2ca37556 --- /dev/null +++ b/tests/test_threat_mappings.py @@ -0,0 +1,357 @@ +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License +# 2.0; you may not use this file except in compliance with the Elastic License +# 2.0. + +"""Tests for multi-version threat mappings (e.g. MITRE ATT&CK v18/v19) support.""" + +import os +import unittest +from pathlib import Path +from tempfile import TemporaryDirectory +from typing import Any, ClassVar + +from marshmallow import ValidationError + +from detection_rules import attack +from detection_rules.config import ( + THREAT_MAPPING_VERSION_ENV, +) +from detection_rules.rule_loader import RuleCollection + +TACTIC = { + "id": "TA0001", + "name": "Initial Access", + "reference": "https://attack.mitre.org/tactics/TA0001/", +} +TECH_V18 = {"id": "T1078", "name": "Valid Accounts", "reference": "https://attack.mitre.org/techniques/T1078/"} +TECH_V19 = {"id": "T1078", "name": "Valid Accounts (v19)", "reference": "https://attack.mitre.org/techniques/T1078/"} + + +def _metadata() -> dict[str, Any]: + return { + "creation_date": "2020/12/15", + "integration": ["endpoint"], + "maturity": "production", + "min_stack_comments": "test", + "min_stack_version": "8.3.0", + "updated_date": "2024/08/30", + } + + +def _rule(threat_mappings: list[dict[str, Any]] | None = None) -> dict[str, Any]: + rule: dict[str, Any] = { + "author": ["Elastic"], + "description": "Test rule.", + "language": "eql", + "name": "Threat Mapping Test Rule", + "risk_score": 47, + "rule_id": "5f7d1e2a-1111-2222-3333-444455556666", + "severity": "low", + "type": "eql", + "query": "any where true", + "threat": [{"framework": "MITRE ATT&CK", "tactic": TACTIC, "technique": [TECH_V18]}], + } + if threat_mappings is not None: + rule["threat_mappings"] = threat_mappings + return rule + + +def _v19_block(technique: dict[str, Any] = TECH_V19) -> dict[str, Any]: + return { + "framework": "MITRE ATT&CK", + "version": "19", + "threat": [{"framework": "MITRE ATT&CK", "tactic": TACTIC, "technique": [technique]}], + } + + +class ThreatMappingEnv: + """Context manager to temporarily set the output threat-mapping version env var.""" + + def __init__(self, version: str | None) -> None: + self.version = version + self._prev: str | None = None + + def __enter__(self) -> None: + self._prev = os.environ.get(THREAT_MAPPING_VERSION_ENV) + if self.version is None: + os.environ.pop(THREAT_MAPPING_VERSION_ENV, None) + else: + os.environ[THREAT_MAPPING_VERSION_ENV] = self.version + + def __exit__(self, *_: object) -> None: + if self._prev is None: + os.environ.pop(THREAT_MAPPING_VERSION_ENV, None) + else: + os.environ[THREAT_MAPPING_VERSION_ENV] = self._prev + + +class TestThreatHashStability(unittest.TestCase): + """Hash stability: name changes must not trigger version bumps; ID changes must.""" + + def _load(self, rule_dict: dict[str, Any]): + return RuleCollection().load_dict({"metadata": _metadata(), "rule": rule_dict}) + + def _hash(self, rule_dict: dict[str, Any]) -> str: + return self._load(rule_dict).contents.get_hash() + + def test_name_change_does_not_change_hash(self) -> None: + tactic_v18 = { + "id": "TA0005", + "name": "Defense Evasion", + "reference": "https://attack.mitre.org/tactics/TA0005/", + } + tactic_v19 = {"id": "TA0005", "name": "Stealth", "reference": "https://attack.mitre.org/tactics/TA0005/"} + tech = {"id": "T1036", "name": "Masquerading", "reference": "https://attack.mitre.org/techniques/T1036/"} + rule_v18 = _rule() + rule_v18["threat"] = [{"framework": "MITRE ATT&CK", "tactic": tactic_v18, "technique": [tech]}] + rule_v19 = _rule() + rule_v19["threat"] = [{"framework": "MITRE ATT&CK", "tactic": tactic_v19, "technique": [tech]}] + self.assertEqual(self._hash(rule_v18), self._hash(rule_v19)) + + def test_tactic_id_change_changes_hash(self) -> None: + tactic_a = {"id": "TA0005", "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/"} + tactic_b = {"id": "TA0002", "name": "Execution", "reference": "https://attack.mitre.org/tactics/TA0002/"} + tech = {"id": "T1036", "name": "Masquerading", "reference": "https://attack.mitre.org/techniques/T1036/"} + rule_a = _rule() + rule_a["threat"] = [{"framework": "MITRE ATT&CK", "tactic": tactic_a, "technique": [tech]}] + rule_b = _rule() + rule_b["threat"] = [{"framework": "MITRE ATT&CK", "tactic": tactic_b, "technique": [tech]}] + self.assertNotEqual(self._hash(rule_a), self._hash(rule_b)) + + def test_technique_id_change_changes_hash(self) -> None: + tactic = {"id": "TA0005", "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/"} + tech_a = {"id": "T1036", "name": "Masquerading", "reference": "https://attack.mitre.org/techniques/T1036/"} + tech_b = {"id": "T1027", "name": "Obfuscated Files", "reference": "https://attack.mitre.org/techniques/T1027/"} + rule_a = _rule() + rule_a["threat"] = [{"framework": "MITRE ATT&CK", "tactic": tactic, "technique": [tech_a]}] + rule_b = _rule() + rule_b["threat"] = [{"framework": "MITRE ATT&CK", "tactic": tactic, "technique": [tech_b]}] + self.assertNotEqual(self._hash(rule_a), self._hash(rule_b)) + + def test_technique_name_change_does_not_change_hash(self) -> None: + tactic = {"id": "TA0005", "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/"} + tech_old = {"id": "T1036", "name": "Old Name", "reference": "https://attack.mitre.org/techniques/T1036/"} + tech_new = {"id": "T1036", "name": "New Name", "reference": "https://attack.mitre.org/techniques/T1036/"} + rule_old = _rule() + rule_old["threat"] = [{"framework": "MITRE ATT&CK", "tactic": tactic, "technique": [tech_old]}] + rule_new = _rule() + rule_new["threat"] = [{"framework": "MITRE ATT&CK", "tactic": tactic, "technique": [tech_new]}] + self.assertEqual(self._hash(rule_old), self._hash(rule_new)) + + def test_removing_technique_changes_hash(self) -> None: + tactic = {"id": "TA0005", "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/"} + tech = {"id": "T1036", "name": "Masquerading", "reference": "https://attack.mitre.org/techniques/T1036/"} + with_tech = _rule() + with_tech["threat"] = [{"framework": "MITRE ATT&CK", "tactic": tactic, "technique": [tech]}] + without_tech = _rule() + without_tech["threat"] = [{"framework": "MITRE ATT&CK", "tactic": tactic}] + self.assertNotEqual(self._hash(with_tech), self._hash(without_tech)) + + def test_reordering_threat_blocks_does_not_change_hash(self) -> None: + """A generated mapping may list tactics in a different order than the source; no version bump should occur.""" + tactic_a = { + "id": "TA0011", + "name": "Command and Control", + "reference": "https://attack.mitre.org/tactics/TA0011/", + } + tactic_b = {"id": "TA0005", "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/"} + rule_forward = _rule() + rule_forward["threat"] = [ + {"framework": "MITRE ATT&CK", "tactic": tactic_a}, + {"framework": "MITRE ATT&CK", "tactic": tactic_b}, + ] + rule_reversed = _rule() + rule_reversed["threat"] = [ + {"framework": "MITRE ATT&CK", "tactic": tactic_b}, + {"framework": "MITRE ATT&CK", "tactic": tactic_a}, + ] + self.assertEqual(self._hash(rule_forward), self._hash(rule_reversed)) + + def test_reordering_techniques_does_not_change_hash(self) -> None: + tactic = {"id": "TA0005", "name": "Defense Evasion", "reference": "https://attack.mitre.org/tactics/TA0005/"} + tech_a = {"id": "T1036", "name": "Masquerading", "reference": "https://attack.mitre.org/techniques/T1036/"} + tech_b = {"id": "T1027", "name": "Obfuscated Files", "reference": "https://attack.mitre.org/techniques/T1027/"} + rule_forward = _rule() + rule_forward["threat"] = [{"framework": "MITRE ATT&CK", "tactic": tactic, "technique": [tech_a, tech_b]}] + rule_reversed = _rule() + rule_reversed["threat"] = [{"framework": "MITRE ATT&CK", "tactic": tactic, "technique": [tech_b, tech_a]}] + self.assertEqual(self._hash(rule_forward), self._hash(rule_reversed)) + + +class TestVersionedThreatMappingSchema(unittest.TestCase): + """Schema validation + build-time selection for `threat_mappings`.""" + + def _load(self, rule_dict: dict[str, Any]): + return RuleCollection().load_dict({"metadata": _metadata(), "rule": rule_dict}) + + def test_loads_and_round_trips(self) -> None: + rule = self._load(_rule(threat_mappings=[_v19_block()])) + self.assertEqual(len(rule.contents.data.threat_mappings), 1) + self.assertEqual(rule.contents.data.threat_mappings[0].version, "19") + + def test_default_emits_baseline_and_strips_field(self) -> None: + rule = self._load(_rule(threat_mappings=[_v19_block()])) + with ThreatMappingEnv(None): + api = rule.contents.to_api_format() + self.assertNotIn("threat_mappings", api) + self.assertEqual(api["threat"][0]["technique"][0]["name"], "Valid Accounts") + + def test_selects_v19_when_configured(self) -> None: + rule = self._load(_rule(threat_mappings=[_v19_block()])) + with ThreatMappingEnv("19"): + api = rule.contents.to_api_format() + self.assertNotIn("threat_mappings", api) + self.assertEqual(api["threat"][0]["technique"][0]["name"], "Valid Accounts (v19)") + + def test_missing_version_falls_back_to_default(self) -> None: + rule = self._load(_rule(threat_mappings=[_v19_block()])) + with ThreatMappingEnv("20"): + api = rule.contents.to_api_format() + self.assertEqual(api["threat"][0]["technique"][0]["name"], "Valid Accounts") + + def test_duplicate_framework_version_rejected(self) -> None: + with self.assertRaises(ValidationError): + self._load(_rule(threat_mappings=[_v19_block(), _v19_block()])) + + def test_inner_framework_mismatch_rejected(self) -> None: + block = _v19_block() + block["threat"][0]["framework"] = "MITRE ATLAS" + with self.assertRaises(ValidationError): + self._load(_rule(threat_mappings=[block])) + + def test_get_primary_tactic_names_baseline_only(self) -> None: + rule = self._load(_rule()) + self.assertEqual(rule.contents.data.get_primary_tactic_names(), ["Initial Access"]) + + def test_get_primary_tactic_names_includes_versioned_blocks(self) -> None: + v19_tactic = { + "id": "TA0005", + "name": "Stealth", + "reference": "https://attack.mitre.org/tactics/TA0005/", + } + block = { + "framework": "MITRE ATT&CK", + "version": "19", + "threat": [{"framework": "MITRE ATT&CK", "tactic": v19_tactic, "technique": [TECH_V19]}], + } + rule = self._load(_rule(threat_mappings=[block])) + self.assertEqual(rule.contents.data.get_primary_tactic_names(), ["Initial Access", "Stealth"]) + + +class TestAttackVersionMapLoader(unittest.TestCase): + """Loading and lookups for the mapping config files.""" + + MAP: ClassVar[dict[str, Any]] = { + "framework": "MITRE ATT&CK", + "source_version": "18", + "target_version": "19", + "tactics": {"TA0001": TACTIC}, + "techniques": {"T1078": TECH_V19, "T1100": None}, + "subtechniques": {}, + } + + def _write(self, directory: Path, name: str, data: dict[str, Any]) -> Path: + import yaml + + path = directory / name + path.write_text(yaml.safe_dump(data)) + return path + + def test_parse_and_lookup(self) -> None: + with TemporaryDirectory() as tmp: + path = self._write(Path(tmp), "m.yaml", self.MAP) + vmap = attack.parse_attack_version_map(path) + self.assertEqual(vmap.key, ("MITRE ATT&CK", "18", "19")) + self.assertEqual(vmap.lookup("technique", "T1078"), TECH_V19) + # explicit null and absent both resolve to None (dropped) + self.assertIsNone(vmap.lookup("technique", "T1100")) + self.assertIsNone(vmap.lookup("technique", "T9999")) + self.assertTrue(vmap.is_mapped("technique", "T1078")) + self.assertFalse(vmap.is_mapped("technique", "T1100")) + + def test_get_by_triple(self) -> None: + with TemporaryDirectory() as tmp: + self._write(Path(tmp), "m.yaml", self.MAP) + vmap = attack.get_attack_version_map("MITRE ATT&CK", "18", "19", [Path(tmp)]) + self.assertEqual(vmap.target_version, "19") + with self.assertRaises(ValueError): + attack.get_attack_version_map("MITRE ATT&CK", "18", "99", [Path(tmp)]) + + def test_missing_required_keys_rejected(self) -> None: + with TemporaryDirectory() as tmp: + path = self._write(Path(tmp), "bad.yaml", {"framework": "MITRE ATT&CK"}) + with self.assertRaises(ValueError): + attack.parse_attack_version_map(path) + + def test_malformed_destination_rejected(self) -> None: + bad = dict(self.MAP) + bad["techniques"] = {"T1078": {"id": "T1078"}} # missing name/reference + with TemporaryDirectory() as tmp: + path = self._write(Path(tmp), "bad.yaml", bad) + with self.assertRaises(ValueError): + attack.parse_attack_version_map(path) + + def test_duplicate_triple_rejected(self) -> None: + with TemporaryDirectory() as tmp: + self._write(Path(tmp), "a.yaml", self.MAP) + self._write(Path(tmp), "b.yaml", self.MAP) + with self.assertRaises(ValueError): + attack.load_attack_version_maps([Path(tmp)]) + + +class TestIdentityScaffold(unittest.TestCase): + def test_build_identity_version_map(self) -> None: + skeleton = attack.build_identity_version_map("MITRE ATT&CK", "18", "19") + self.assertEqual(skeleton["source_version"], "18") + self.assertEqual(skeleton["target_version"], "19") + self.assertGreater(len(skeleton["techniques"]), 0) + # identity entries map an id to itself with a name/reference + sample_id, sample = next(iter(skeleton["techniques"].items())) + self.assertEqual(sample_id, sample["id"]) + self.assertIn("reference", sample) + # revoked/deprecated ids are excluded from the identity baseline + for revoked_id in attack.revoked: + self.assertNotIn(revoked_id, skeleton["techniques"]) + self.assertNotIn(revoked_id, skeleton["subtechniques"]) + + def test_scaffold_uses_v18_source_keys(self) -> None: + """Source keys must come from v18; new v19-only IDs must not appear as source keys.""" + skeleton = attack.build_identity_version_map("MITRE ATT&CK", "18", "19") + v18_lookups = attack.build_attack_lookups_for_version("18") + v19_lookups = attack.build_attack_lookups_for_version("19") + # TA0112 is new in v19; it must not appear as a source key in a v18->v19 map + v19_only_tactics = set(v19_lookups.tactics_map.values()) - set(v18_lookups.tactics_map.values()) + for tactic_id in v19_only_tactics: + self.assertNotIn(tactic_id, skeleton["tactics"]) + + def test_scaffold_uses_v19_tactic_names(self) -> None: + """Tactic names in the scaffold must come from v19, not v18.""" + skeleton = attack.build_identity_version_map("MITRE ATT&CK", "18", "19") + v19_lookups = attack.build_attack_lookups_for_version("19") + v19_tactic_id_to_name = {v: k for k, v in v19_lookups.tactics_map.items()} + for tactic_id, entry in skeleton["tactics"].items(): + if tactic_id in v19_tactic_id_to_name: + self.assertEqual( + entry["name"], + v19_tactic_id_to_name[tactic_id], + f"Scaffold tactic {tactic_id} has v18 name '{entry['name']}' " + f"instead of v19 name '{v19_tactic_id_to_name[tactic_id]}'", + ) + + def test_scaffold_uses_v19_technique_names(self) -> None: + """Technique names that exist in v19 must reflect the v19 name.""" + skeleton = attack.build_identity_version_map("MITRE ATT&CK", "18", "19") + v19_lookups = attack.build_attack_lookups_for_version("19") + for technique_id, entry in skeleton["techniques"].items(): + if technique_id in v19_lookups.technique_lookup: + expected = v19_lookups.technique_lookup[technique_id]["name"] + self.assertEqual( + entry["name"], + expected, + f"Scaffold technique {technique_id} has name '{entry['name']}' instead of v19 name '{expected}'", + ) + + +if __name__ == "__main__": + unittest.main()