Que fait Analyse des Relations entre Familles de Malwares avec Malpedia ?
Présentation
Malpedia est une plateforme collaborative maintenue par Fraunhofer FKIE qui catalogue les familles de malwares avec leurs alias, règles YARA, associations à des acteurs de menace et rapports de référence. Avec plus de 2 600 familles documentées, elle constitue la ressource de référence pour comprendre les lignées de malwares, suivre l'évolution des variantes et relier les malwares à des groupes de menace spécifiques. Cette compétence couvre l'interrogation de l'API Malpedia, le mappage des relations entre familles de malwares, l'extraction de règles YARA pour la détection et la construction de renseignements sur les écosystèmes de malwares utilisés par les adversaires.
Quand l'utiliser
- Lors de l'investigation d'incidents de sécurité nécessitant l'analyse des relations entre familles de malwares avec Malpedia
- Lors de la création de règles de détection ou de requêtes de chasse aux menaces pour ce domaine
- Lorsque les analystes SOC ont besoin de procédures structurées pour ce type d'analyse
- Lors de la validation de la couverture de surveillance pour des techniques d'attaque connexes
Prérequis
- Python 3.9+ avec les bibliothèques
requests,yara-python,stix2 - Clé API Malpedia (inscription sur https://malpedia.caad.fkie.fraunhofer.de/)
- Compréhension de la classification des malwares et des conventions de nommage
- Familiarité avec la syntaxe des règles YARA pour la détection
- Accès à des échantillons de malwares pour validation (optionnel)
Concepts clés
Modèle de données Malpedia
Malpedia organise les malwares en Familles (p. ex. "win.cobalt_strike"), chacune contenant : des alias (noms spécifiques aux fournisseurs comme "Beacon", "CobaltStrike"), des règles YARA (communautaires et fournisseurs), des associations d'acteurs (groupes de menace utilisant la famille), des rapports de référence (rapports CTI documentant la famille), et des empreintes d'échantillons (échantillons représentatifs pour chaque variante).
Nommage des familles de malwares
Malpedia utilise le format platform.family_name (p. ex. win.emotet, elf.mirai, apk.flubot). Les plateformes incluent win (Windows), elf (Linux), apk (Android), osx (macOS) et py (Python). Ce nommage standardisé résout le problème des "noms multiples" où différents fournisseurs attribuent des noms différents au même malware.
Relations entre familles
Les familles de malwares entretiennent des relations incluant : parent-enfant (réutilisation de code, forks), chargeur-charge utile (Emotet charge TrickBot charge Ryuk), paternité partagée (un même acteur de menace développe plusieurs outils), et partage d'infrastructure (cadres C2 communs).
Flux de travail
Étape 1 : Interroger l'API Malpedia pour les familles de malwares
import requests
import json
from collections import defaultdict
class MalpediaClient:
BASE_URL = "https://malpedia.caad.fkie.fraunhofer.de/api"
def __init__(self, api_key):
self.headers = {"Authorization": f"apitoken {api_key}"}
def get_family_list(self):
"""Obtenir la liste de toutes les familles de malwares."""
resp = requests.get(f"{self.BASE_URL}/list/families",
headers=self.headers, timeout=30)
if resp.status_code == 200:
families = resp.json()
print(f"[+] Malpedia : {len(families)} familles de malwares")
return families
return {}
def get_family_info(self, family_name):
"""Obtenir des informations détaillées sur une famille de malwares."""
resp = requests.get(f"{self.BASE_URL}/get/family/{family_name}",
headers=self.headers, timeout=30)
if resp.status_code == 200:
info = resp.json()
print(f"[+] Famille : {family_name}")
print(f" Alias : {info.get('alt_names', [])}")
print(f" Acteurs : {[a.get('value', '') for a in info.get('attribution', [])]}")
print(f" URLs : {len(info.get('urls', []))} références")
return info
print(f"[-] Famille non trouvée : {family_name}")
return None
def get_family_yara(self, family_name):
"""Obtenir les règles YARA pour une famille de malwares."""
resp = requests.get(f"{self.BASE_URL}/get/yara/{family_name}",
headers=self.headers, timeout=30)
if resp.status_code == 200:
rules = resp.json()
rule_count = sum(len(v) for v in rules.values()) if isinstance(rules, dict) else 0
print(f"[+] Règles YARA pour {family_name} : {rule_count} règles")
return rules
return {}
def get_actor_families(self, actor_name):
"""Obtenir les familles de malwares associées à un acteur de menace."""
resp = requests.get(f"{self.BASE_URL}/get/actor/{actor_name}",
headers=self.headers, timeout=30)
if resp.status_code == 200:
data = resp.json()
families = data.get("families", {})
print(f"[+] {actor_name} : {len(families)} familles de malwares")
return data
return {}
def search_families(self, keyword):
"""Rechercher des familles par mot-clé."""
all_families = self.get_family_list()
matches = {
name: info for name, info in all_families.items()
if keyword.lower() in name.lower()
or keyword.lower() in str(info.get("alt_names", [])).lower()
}
print(f"[+] Recherche '{keyword}' : {len(matches)} correspondances")
return matches
client = MalpediaClient("VOTRE_CLE_API_MALPEDIA")
families = client.get_family_list()
emotet_info = client.get_family_info("win.emotet")
Étape 2 : Mapper les relations entre familles de malwares
class MalwareFamilyMapper:
def __init__(self, malpedia_client):
self.client = malpedia_client
self.relationship_graph = defaultdict(list)
def map_actor_ecosystem(self, actor_name):
"""Mapper l'écosystème de malwares utilisé par un acteur de menace."""
actor_data = self.client.get_actor_families(actor_name)
families = actor_data.get("families", {})
ecosystem = {
"actor": actor_name,
"families": [],
"family_count": len(families),
}
for family_name in families:
info = self.client.get_family_info(family_name)
if info:
ecosystem["families"].append({
"name": family_name,
"aliases": info.get("alt_names", []),
"description": info.get("description", "")[:200],
"shared_actors": [
a.get("value", "")
for a in info.get("attribution", [])
],
"reference_count": len(info.get("urls", [])),
})
print(f"\n=== Écosystème de malwares de {actor_name} ===")
for fam in ecosystem["families"]:
shared = [a for a in fam["shared_actors"] if a != actor_name]
print(f" {fam['name']}")
print(f" Alias : {fam['aliases'][:5]}")
if shared:
print(f" Également utilisé par : {shared}")
return ecosystem
def find_shared_tooling(self, actor_names):
"""Trouver les familles de malwares partagées entre acteurs de menace."""
actor_families = {}
for actor in actor_names:
data = self.client.get_actor_families(actor)
actor_families[actor] = set(data.get("families", {}).keys())
# Trouver les chevauchements
shared = {}
for i, actor1 in enumerate(actor_names):
for actor2 in actor_names[i+1:]:
common = actor_families[actor1] & actor_families[actor2]
if common:
shared[f"{actor1} <-> {actor2}"] = sorted(common)
print(f"\n=== Analyse de l'outillage partagé ===")
for pair, families in shared.items():
print(f" {pair} : {len(families)} familles partagées")
for f in families[:5]:
print(f" - {f}")
return shared
def build_loader_payload_chain(self, family_name):
"""Construire la chaîne de distribution chargeur-charge utile pour une famille."""
info = self.client.get_family_info(family_name)
if not info:
return {}
chain = {
"family": family_name,
"description": info.get("description", ""),
"known_loaders": [],
"known_payloads": [],
}
# Chaînes de distribution connues courantes
known_chains = {
"win.emotet": {"loaders": ["email/macro"], "payloads": ["win.trickbot", "win.qakbot", "win.cobalt_strike"]},
"win.trickbot": {"loaders": ["win.emotet"], "payloads": ["win.ryuk", "win.conti", "win.cobalt_strike"]},
"win.qakbot": {"loaders": ["email/macro", "win.emotet"], "payloads": ["win.cobalt_strike", "win.blackbasta"]},
"win.cobalt_strike": {"loaders": ["win.emotet", "win.trickbot", "win.qakbot"], "payloads": ["ransomware"]},
}
if family_name in known_chains:
chain["known_loaders"] = known_chains[family_name]["loaders"]
chain["known_payloads"] = known_chains[family_name]["payloads"]
return chain
mapper = MalwareFamilyMapper(client)
ecosystem = mapper.map_actor_ecosystem("Wizard Spider")
shared = mapper.find_shared_tooling(["Wizard Spider", "FIN7", "Lazarus Group"])
chain = mapper.build_loader_payload_chain("win.emotet")
Étape 3 : Extraire et compiler les règles YARA
def compile_yara_ruleset(client, family_names, output_file="malware_yara_rules.yar"):
"""Compiler des règles YARA pour plusieurs familles de malwares."""
all_rules = []
for family in family_names:
yara_data = client.get_family_yara(family)
if isinstance(yara_data, dict):
for source, rules in yara_data.items():
if isinstance(rules, list):
for rule in rules:
all_rules.append(f"// Source : {source} - Famille : {family}\n{rule}")
elif isinstance(rules, str):
all_rules.append(f"// Source : {source} - Famille : {family}\n{rules}")
with open(output_file, "w") as f:
f.write(f"// Règles YARA Malpedia - {len(all_rules)} règles\n")
f.write(f"// Familles : {', '.join(family_names)}\n\n")
for rule in all_rules:
f.write(rule + "\n\n")
print(f"[+] Compilé {len(all_rules)} règles YARA dans {output_file}")
return all_rules
compile_yara_ruleset(client, ["win.emotet", "win.trickbot", "win.cobalt_strike"])
Critères de validation
- API Malpedia interrogée avec succès pour les familles de malwares
- Informations sur les familles récupérées avec alias, acteurs et références
- Relations acteurs-familles mappées correctement
- Outillage partagé entre acteurs identifié
- Règles YARA extraites et compilées pour la détection
- Chaînes chargeur-charge utile documentées pour le renseignement sur les menaces