Analyse des Artéfacts de Malwares de la Chaîne d'Approvisionnement
Aperçu
Les attaques de la chaîne d'approvisionnement compromettent les canaux de distribution de logiciels légitimes pour fournir des malwares via des mécanismes de mise à jour de confiance. Les exemples notables incluent SolarWinds SUNBURST (2020, affectant plus de 18 000 clients), 3CX SmoothOperator (2023, une attaque en cascade sur la chaîne d'approvisionnement provenant de Trading Technologies), et de nombreuses campagnes d'empoisonnement de packages npm/PyPI. L'analyse implique la comparaison de binaires trojanisés avec des versions légitimes, l'identification de code injecté dans les artéfacts de build, l'examen des anomalies de signature de code, et le traçage de la chaîne d'infection depuis la compromission initiale jusqu'à la livraison de la charge utile. En 2025, les attaques de la chaîne d'approvisionnement représentent 30 % de toutes les violations, soit une augmentation de 100 % par rapport aux années précédentes.
Quand l'utiliser
- Lors de l'investigation d'incidents de sécurité nécessitant l'analyse d'artéfacts malwares de la chaîne d'approvisionnement
- Lors de la construction de règles de détection ou de requêtes de threat hunting 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 de la sécurité pour les techniques d'attaque connexes
Prérequis
- Python 3.9+ avec
pefile,ssdeep,hashlib - Outils de diff binaire (BinDiff, Diaphora)
- Outils de vérification de signature de code (sigcheck, codesign)
- Outils d'analyse de composition logicielle (SCA)
- Accès aux versions logicielles légitimes pour comparaison
- Surveillance des dépôts de packages (npm, PyPI, NuGet)
Workflow
Étape 1 : Analyse de comparaison binaire
#!/usr/bin/env python3
"""Comparer un binaire trojanisé avec une version légitime."""
import hashlib
import pefile
import sys
import json
def compare_pe_files(legitimate_path, suspect_path):
"""Comparer les structures de fichiers PE entre versions légitimes et suspectes."""
legit_pe = pefile.PE(legitimate_path)
suspect_pe = pefile.PE(suspect_path)
report = {"differences": [], "suspicious_sections": [], "import_changes": []}
# Comparer les sections
legit_sections = {s.Name.rstrip(b'\x00').decode(): {
"size": s.SizeOfRawData,
"entropy": s.get_entropy(),
"characteristics": s.Characteristics,
} for s in legit_pe.sections}
suspect_sections = {s.Name.rstrip(b'\x00').decode(): {
"size": s.SizeOfRawData,
"entropy": s.get_entropy(),
"characteristics": s.Characteristics,
} for s in suspect_pe.sections}
# Trouver les sections nouvelles ou modifiées
for name, props in suspect_sections.items():
if name not in legit_sections:
report["suspicious_sections"].append({
"name": name, "reason": "Nouvelle section absente de la version légitime",
"size": props["size"], "entropy": round(props["entropy"], 2),
})
elif abs(props["size"] - legit_sections[name]["size"]) > 1024:
report["suspicious_sections"].append({
"name": name, "reason": "Taille de section considérablement modifiée",
"legit_size": legit_sections[name]["size"],
"suspect_size": props["size"],
})
# Comparer les imports
legit_imports = set()
if hasattr(legit_pe, 'DIRECTORY_ENTRY_IMPORT'):
for entry in legit_pe.DIRECTORY_ENTRY_IMPORT:
for imp in entry.imports:
if imp.name:
legit_imports.add(f"{entry.dll.decode()}!{imp.name.decode()}")
suspect_imports = set()
if hasattr(suspect_pe, 'DIRECTORY_ENTRY_IMPORT'):
for entry in suspect_pe.DIRECTORY_ENTRY_IMPORT:
for imp in entry.imports:
if imp.name:
suspect_imports.add(f"{entry.dll.decode()}!{imp.name.decode()}")
new_imports = suspect_imports - legit_imports
if new_imports:
report["import_changes"] = list(new_imports)
# Vérifier la signature de code
report["legit_signed"] = bool(legit_pe.OPTIONAL_HEADER.DATA_DIRECTORY[4].Size)
report["suspect_signed"] = bool(suspect_pe.OPTIONAL_HEADER.DATA_DIRECTORY[4].Size)
return report
def hash_file(filepath):
"""Calculer plusieurs empreintes pour un fichier."""
hashes = {}
with open(filepath, 'rb') as f:
data = f.read()
for algo in ['md5', 'sha1', 'sha256']:
h = hashlib.new(algo)
h.update(data)
hashes[algo] = h.hexdigest()
return hashes
if __name__ == "__main__":
if len(sys.argv) < 3:
print(f"Utilisation : {sys.argv[0]} <binaire_legitime> <binaire_suspect>")
sys.exit(1)
report = compare_pe_files(sys.argv[1], sys.argv[2])
print(json.dumps(report, indent=2))
Critères de validation
- Composants trojanisés identifiés par diff binaire
- Code injecté isolé et analysé séparément
- Anomalies de signature de code documentées
- Chronologie de l'infection reconstituée à partir des artéfacts de build
- Périmètre d'impact aval évalué sur les systèmes affectés
- IOCs extraits pour la détection et le blocage