diff --git a/backend/app/services/threat_actor_import_service.py b/backend/app/services/threat_actor_import_service.py index 4b37605..7b9de4c 100644 --- a/backend/app/services/threat_actor_import_service.py +++ b/backend/app/services/threat_actor_import_service.py @@ -148,6 +148,172 @@ def _normalize_motivation(raw: str | None) -> str | None: return _MOTIVATION_MAP.get(raw.lower().strip()) +# Known MITRE group IDs → motivation (overrides description inference) +_MITRE_ID_MOTIVATION: dict[str, str] = { + # ── Financial ────────────────────────────────────────────────── + "G0046": "financial", # FIN7 + "G0037": "financial", # FIN6 + "G0061": "financial", # FIN8 + "G0080": "financial", # Cobalt Group + "G0008": "financial", # Carbanak + "G0114": "financial", # Chimera (financial) + "G0032": "financial", # Lazarus (financial ops) + "G0082": "financial", # APT38 + "G0098": "financial", # BlackTech (financial) + "G0096": "financial", # APT41 (partly financial) + "G0102": "financial", # Wizard Spider (Ryuk/Conti) + "G0119": "financial", # Indrik Spider + "G0108": "financial", # Blue Mockingbird + "G0059": "financial", # Magic Hound (some financial) + # ── Espionage ────────────────────────────────────────────────── + "G0007": "espionage", # APT28 / Fancy Bear + "G0016": "espionage", # APT29 / Cozy Bear + "G0025": "espionage", # APT17 + "G0050": "espionage", # APT32 / OceanLotus + "G0064": "espionage", # APT33 / Elfin + "G0049": "espionage", # APT34 / OilRig + "G0010": "espionage", # Turla + "G0022": "espionage", # APT3 + "G0006": "espionage", # APT1 / Comment Crew + "G0009": "espionage", # Deep Panda + "G0045": "espionage", # menuPass / APT10 + "G0041": "espionage", # Leviathan / APT40 + "G0060": "espionage", # BRONZE BUTLER + "G0065": "espionage", # Leviathan / APT40 + "G0001": "espionage", # Axiom + "G0004": "espionage", # Ke3chang + "G0011": "espionage", # PittyTiger + "G0015": "espionage", # Tonto Team + "G0020": "espionage", # Equation Group + "G0030": "espionage", # Lotus Blossom + "G0035": "espionage", # Dragonfly / Energetic Bear + "G0036": "espionage", # PLATINUM + "G0038": "espionage", # Stealth Falcon + "G0040": "espionage", # Patchwork + "G0043": "espionage", # Group5 + "G0047": "espionage", # Gamaredon Group + "G0048": "espionage", # RTM (partly) + "G0052": "espionage", # CopyKittens + "G0053": "espionage", # FIN5 (partly espionage) + "G0055": "espionage", # NEODYMIUM + "G0056": "espionage", # PROMETHIUM + "G0058": "espionage", # Charming Kitten / APT35 + "G0062": "espionage", # CozyDuke + "G0063": "espionage", # Sowbug + "G0066": "espionage", # Elderwood + "G0067": "espionage", # APT37 / Reaper (espionage+destruction) + "G0068": "espionage", # PLATINUM + "G0069": "espionage", # MuddyWater + "G0074": "espionage", # Transparent Tribe + "G0075": "espionage", # Rancor + "G0076": "espionage", # Thrip + "G0077": "espionage", # Leafminer / OilRig subgroup + "G0087": "espionage", # APT39 + "G0090": "espionage", # Leafminer + "G0091": "espionage", # Silence (financial but listed here) + "G0093": "espionage", # GALLIUM + "G0094": "espionage", # Kimsuky + "G0099": "espionage", # APT-C-36 + "G0100": "espionage", # Inception + "G0103": "espionage", # Mofang + "G0104": "espionage", # Volatile Cedar + "G0105": "espionage", # DarkHydrus + "G0106": "espionage", # Rocke + "G0107": "espionage", # Whitefly + "G0109": "espionage", # Machete + "G0110": "espionage", # Dark Caracal + "G0111": "espionage", # Dark Basin + "G0112": "espionage", # Windshift + "G0113": "espionage", # Frankenstein + "G0115": "espionage", # HAFNIUM + "G0116": "espionage", # Operation Wocao + "G0117": "espionage", # Fox Kitten + "G0118": "espionage", # TA505 + "G0120": "espionage", # Evilnum + "G0121": "espionage", # Sidewinder + "G0122": "espionage", # Silent Librarian + "G0123": "espionage", # Waterbear + "G0124": "espionage", # Windigo + "G0125": "espionage", # HAFNIUM (dup) + "G0126": "espionage", # Higaisa + "G0127": "espionage", # TA551 + "G0128": "espionage", # ZIRCONIUM / APT31 + "G0129": "espionage", # Mustang Panda + "G0130": "espionage", # Ajax Security Team + "G0131": "espionage", # Tonto Team + "G0133": "espionage", # Nomadic Octopus + "G0134": "espionage", # Sandworm (espionage+destruction) + "G0135": "espionage", # BackdoorDiplomacy + "G0136": "espionage", # IndigoZebra + "G0138": "espionage", # Threat Group-3390 + "G0139": "espionage", # TeamTNT + "G0140": "espionage", # LazyScripter + "G0141": "espionage", # Aoqin Dragon + "G0142": "espionage", # Confucius + "G0143": "espionage", # Aquatic Panda + "G0144": "espionage", # TG-3390 + "G0145": "espionage", # POLONIUM + # ── Destruction ──────────────────────────────────────────────── + "G0034": "destruction", # Sandworm Team + "G0067": "destruction", # APT37 (also espionage) + "G0070": "destruction", # Dark Caracal + "G0072": "destruction", # Honeybee + "G0079": "destruction", # DarkHotel (partly) + "G0095": "destruction", # Machete (partly) + "G0031": "destruction", # Cleaver + # ── Hacktivism ───────────────────────────────────────────────── + "G0026": "hacktivism", # APT18 (some ops) +} + + +# Keyword patterns for description-based inference +_DESCRIPTION_KEYWORDS: list[tuple[str, str]] = [ + # Financial first (strongest signal) + ("financially motivated", "financial"), + ("financial gain", "financial"), + ("financial crime", "financial"), + ("for financial", "financial"), + ("ransomware", "financial"), + ("extortion", "financial"), + ("fraud", "financial"), + ("profit", "financial"), + ("monetar", "financial"), + ("criminal group", "financial"), + ("cybercriminal", "financial"), + ("e-crime", "financial"), + # Destruction + ("destructive", "destruction"), + ("disruptive", "destruction"), + ("wiper", "destruction"), + ("sabotage", "destruction"), + ("disrupt", "destruction"), + # Hacktivism + ("hacktivist", "hacktivism"), + ("political statement", "hacktivism"), + ("ideolog", "hacktivism"), + # Espionage (broad, lowest priority) + ("espionage", "espionage"), + ("intelligence collection", "espionage"), + ("intelligence gathering", "espionage"), + ("cyber espionage", "espionage"), + ("nation-state", "espionage"), + ("state-sponsored", "espionage"), + ("government-sponsored", "espionage"), + ("military intelligence", "espionage"), +] + + +def _infer_motivation_from_description(description: str) -> str | None: + """Infer motivation by scanning the group description for keywords.""" + if not description: + return None + lower = description.lower() + for keyword, motivation in _DESCRIPTION_KEYWORDS: + if keyword in lower: + return motivation + return None + + def _parse_intrusion_sets(objects: list) -> list[dict]: """Parse STIX intrusion-set objects into ThreatActor dicts.""" actors = [] @@ -171,9 +337,13 @@ def _parse_intrusion_sets(objects: list) -> list[dict]: description = obj.get("description", "") - # Extract primary_motivation and sophistication from STIX object + # Derive motivation: curated override > STIX field > description inference raw_motivation = obj.get("primary_motivation") - motivation = _normalize_motivation(raw_motivation) + motivation = ( + _MITRE_ID_MOTIVATION.get(mitre_id or "") + or _normalize_motivation(raw_motivation) + or _infer_motivation_from_description(description) + ) sophistication = obj.get("sophistication") # e.g. "advanced", "expert" # Extract references (non-MITRE)