Cyberhunter Sandbox
Sandbox d’analyse de malware interactive et furtive, auto-hébergée, sans agent dans la machine analysée. L’analyste soumet un fichier, une URL ou un email, le regarde s’exécuter en direct dans une VM Windows jetable, et récupère un rapport de comportement. Toute l’observation se fait depuis l’extérieur de la VM, donc invisible au malware.
État du projet : en construction, mais utilisable de bout en bout. Ce README documente ce qui est déjà fait et validé, étape par étape. Il grandit avec le projet.
Table des matières
- Pourquoi ce projet
- Le parti pris technique : agentless
- Architecture d’ensemble
- Pile technologique
- Organisation du dépôt
- Installation pas à pas
- Pièges rencontrés et solutions
- Glossaire
- Changelog
1. Pourquoi ce projet
Les sandboxes open source existantes (Cuckoo, CAPEv2, Cuckoo3) ont deux limites que ce projet cherche à dépasser.
La première est la détectabilité. Elles installent un agent dans la VM analysée pour observer le comportement. Cet agent (un processus, une DLL, des hooks) est visible, et les malwares modernes le cherchent. S’ils le trouvent, ils s’endorment et ne montrent rien. Même ANY.RUN, la référence commerciale, fonctionne avec un agent invité visible.
La seconde est l’ergonomie. L’interaction en temps réel (voir l’écran, cliquer, taper un mot de passe d’archive, franchir un installeur) est soit absente, soit reléguée au rang de rustine.
L’objectif de Cyberhunter est d’avoir les deux à la fois : une observation 100 % externe à la VM, donc indétectable par principe, et une session interactive fluide dans le navigateur.
La cible est d’abord un usage personnel, puis un outil d’équipe SOC ou CERT. La contrainte qui structure tout le reste : le déploiement doit être scripté, idempotent et réinstallable sur une autre infrastructure. Rien n’est bricolé à la main dans une VM ; tout vit dans le dépôt, sous forme de scripts et de fichiers de configuration versionnés.
2. Le parti pris technique : agentless
Le principe de départ tient en une phrase : rien de ce qui observe le malware ne vit dans la VM.
| Ce qu’on observe | Comment, sans agent |
|---|---|
| Processus, modules, mémoire | Lecture de la RAM de la VM depuis l’hôte (introspection mémoire, memflow) |
| Réseau | Capture PCAP sur le pont de l’hôte, puis Suricata et mitmproxy |
| Fichiers déposés | Lecture hors ligne du disque qcow2 après extinction |
| Captures d’écran | Prises par l’hyperviseur (virsh screenshot) |
| Télémétrie Windows riche | ETW activé par une clé de registre (AutoLogger et Global Logger), pas un programme |
Le seul « code » côté Windows est une configuration : des clés de registre qui activent une trace ETW native de Windows. Jamais un exécutable que le malware pourrait énumérer comme un moniteur. C’est ce qui rend la sandbox plus furtive qu’ANY.RUN.
Pourquoi pas DRAKVUF ? DRAKVUF est l’excellent moteur d’introspection agentless de référence, mais il impose Xen et le matériel Intel (
altp2m), et il ne tourne pas en cloud (virtualisation imbriquée insuffisante). Notre approche memflow lit la mémoire du processus QEMU et fonctionne partout, y compris dans une VM Azure. On abandonne le tracing d’API exhaustif de DRAKVUF au profit d’un échantillonnage mémoire. C’est un compromis assumé, et réversible plus tard.
3. Architecture d’ensemble
Le système se lit sur deux niveaux : l’isolation (qui voit quoi) et le cycle de vie d’une analyse (ce qui se passe, dans l’ordre).
3.1 Deux plans strictement séparés
Le plan de contrôle orchestre mais ne touche jamais au malware. Le plan de détonation exécute l’échantillon dans une VM jetable, sur un réseau totalement clos où INetSim simule Internet. La seule chose qui traverse la frontière entre les deux, c’est le worker (en root) qui pilote l’hyperviseur.
3.2 Le cycle de vie d’une analyse, étape par étape
Une soumission traverse toujours la même chaîne. Chaque étape est un maillon indépendant et rejouable.
1. Soumission fichier / URL / email + choix d'OS API → file (queued)
2. Prise en file le worker récupère le job (running)
3. Overlay qcow2 jetable adossé à la base figée base intacte
4. Injection dépôt de l'échantillon + clé RunOnce hors ligne, sans agent
+ CA TLS + AutoLogger noyau (ruche SYSTEM)
5. Démarrage domaine éphémère branché sur virbr-sim → écran VNC (live)
6. Observation memflow · captures · PCAP · ETW pendant la fenêtre live
7. Arrêt propre extinction ACPI (repli : destroy) vide le cache disque
8. Récolte fichiers déposés + traces ETW hors ligne
9. Analyse Volatility · YARA · config · Suricata → events.json
· signatures ATT&CK · verdict → verdict.json
10. Restitution l'API sert le rapport et les exports interface (done)
Deux idées portent toute la chaîne. D’abord, la base Windows n’est jamais modifiée : on travaille sur un overlay jetable que l’on détruit à la fin, ce qui « restaure » la VM en une seconde. Ensuite, tout ce qui est observé est traduit dans un schéma d’événements unique (voir bloc D) : l’interface et le stockage ne savent jamais si un événement vient de Volatility, de memflow, de Suricata ou d’ETW ; ils consomment la même forme.
4. Pile technologique
| Composant | Choix | Rôle |
|---|---|---|
| Hyperviseur | KVM/QEMU + libvirt | Exécute les VM d’analyse ; API de gestion standard |
| Hôte | Ubuntu Server 24.04 / 22.04 | Même OS en lab et en prod, donc scripts identiques |
| Invité | Windows 11 Pro, Windows 10 Pro | Les cibles du malware moderne (catalogue extensible) |
| Réseau simulé | INetSim | Répond aux requêtes du malware sans laisser sortir de trafic |
| Détection réseau | Suricata | Signatures sur le PCAP (alertes, DNS, HTTP, TLS) |
| Déchiffrement HTTPS | mitmproxy | Coupure TLS transparente, CA importée dans l’overlay |
| Analyse statique | pefile + FLARE capa | Triage PE et capacités mappées ATT&CK, dès la soumission |
| Introspection mémoire | memflow | Lit la RAM de la VM depuis l’hôte, sans agent |
| Télémétrie invité | ETW (AutoLogger + Global Logger) | Traces natives Windows, activées par registre |
| Forensique mémoire | Volatility 3 | Analyse post-mortem des dumps |
| Extraction de config | parsers par famille (esprit CAPE) | Config des familles connues (Remcos, AsyncRAT, njRAT) |
| Édition d’image hors ligne | libguestfs, hivex | Injecte fichiers et registre sans démarrer la VM |
| Automatisation d’install | autounattend.xml | Installe Windows sans interaction |
| Plateforme | FastAPI + React/Vite + SQLite/PostgreSQL | API, interface, base |
5. Organisation du dépôt
cyberhunter-anyrun/
├── README.md ← ce fichier
├── deploy.sh ← pousse le code sur l'hôte (rsync/scp, préserve l'arbo)
├── install/ ← préparation de l'hôte
│ ├── lib.sh ← fonctions communes (log, config, idempotence, arrêt propre, TLS)
│ ├── config.env.example ← paramètres (copier en config.env)
│ ├── install.sh ← runner : exécute les étapes numérotées
│ ├── 00-storage.sh ← disques : /var/lib/libvirt + espace de travail éphémère
│ ├── 01-packages.sh ← KVM, libvirt, libguestfs, OVMF, swtpm…
│ ├── 02-analysis.sh ← venv d'analyse : Volatility 3, YARA, pefile, pycryptodome
│ ├── 03-webstack.sh ← venv web : FastAPI, Uvicorn, SQLAlchemy, reportlab
│ ├── 05-frontend.sh ← Node 20 + build du SPA React
│ ├── 06-postgres.sh ← PostgreSQL (option équipe)
│ ├── 07-services.sh ← unités systemd (worker root / API sans privilège)
│ ├── 08-memflow.sh ← compile le binaire Rust d'introspection live
│ ├── 09-emulator-oem.sh ← émulateur enveloppe : maquille l'OEM ACPI (BOCHS→LENOVO)
│ ├── 10-network.sh ← réseau isolé + INetSim + confinement pare-feu
│ ├── 11-netanalysis.sh ← Suricata (IDS) + règles ET open, pour l'analyse du PCAP
│ ├── 12-tlsmitm.sh ← mitmproxy + CA, pour déchiffrer le HTTPS (TLS MITM)
│ ├── 13-capa.sh ← FLARE capa + règles/signatures (analyse statique)
│ └── resources/
│ ├── qemu-oem-wrap.c ← source du wrapper d'émulateur (réécrit -machine)
│ ├── ca-to-reg.py ← CA (DER) → .reg d'import dans le magasin racine
│ └── flowlog.py ← addon mitmproxy : journalise les flux déchiffrés
├── image/ ← construction de l'image Windows
│ ├── config.env.example ← paramètres de l'image (édition, OS, VirtIO)
│ ├── autounattend.xml ← installation Windows non interactive
│ ├── 20-fetch-iso.sh ← récupère les ISO (Windows, VirtIO)
│ ├── 21-build-base.sh ← installe Windows dans une VM
│ ├── 22-harden.sh ← durcissement anti-évasion + ETW + profil « vécu »
│ ├── 24-freeze.sh ← fige l'image en base immuable + gabarit + manifeste
│ ├── 25-reharden-base.sh← ré-applique le registre sur une base déjà figée
│ ├── register-image.sh ← enregistre une image dans le catalogue (choix d'OS)
│ ├── harden/ ← clés de registre (.reg) versionnées
│ │ ├── anti-evasion.reg ← anti-évasion + désactivation Defender
│ │ ├── etw-autologger.reg ← trace ETW manifest au boot (AutoLogger)
│ │ ├── etw-kernel.reg ← trace noyau MOF au boot (Global Logger)
│ │ └── services-off.reg ← services Defender désactivés (ruche SYSTEM)
│ └── scripts/ ← utilitaires appelés par les étapes
│ ├── apply-offline.sh ← applique registre et fichiers hors ligne
│ ├── expand-reg.py ← génère les clés parentes pour hivexregedit
│ └── spoof-libvirt.py ← maquille SMBIOS/CPU/NIC dans le XML libvirt
├── detonate/ ← exécution d'un échantillon (bloc C)
│ ├── config.env.example
│ ├── detonate.sh ← orchestrateur mono-coup : overlay → injection → run → collecte
│ ├── session.sh ← détonation en phases (preheat/start/collect/stop) pour le live
│ ├── memflow-live/ ← binaire Rust d'introspection mémoire en direct
│ └── scripts/
│ ├── make-domain.py ← domaine éphémère dérivé du gabarit durci
│ ├── inject.sh ← dépose l'échantillon + RunOnce + CA TLS + AutoLogger noyau
│ └── harvest.sh ← récolte hors ligne des fichiers déposés et des traces ETW
├── analyze/ ← analyse post-détonation (blocs D + G)
│ ├── analyze.sh ← enchaîne Volatility, YARA, config, réseau, ETW, signatures, verdict
│ ├── rules/ ← règles YARA (starter.yar, extensible)
│ ├── extractors/ ← IOC génériques + parsers de famille (remcos, asyncrat, njrat)
│ └── scripts/ ← normalize, scan_yara, config_extract, netparse, etwfast, signatures, verdict
├── server/ ← plan de contrôle (blocs E, F, H)
│ ├── app.py ← API FastAPI + pont WebSocket↔VNC + service du frontend
│ ├── worker.py ← file de jobs → detonate.sh (root) + vivier de VM chaudes
│ ├── db.py ← modèle SQLite/SQLAlchemy (migrable PostgreSQL)
│ ├── images.py ← catalogue des OS invités (choix à la soumission)
│ ├── launchers.py ← lanceur natif selon le type de fichier (script, doc, dll…)
│ ├── emailparse.py ← analyse d'emails de phishing (.eml/.msg), hors sandbox
│ ├── report.py ← exports d'une analyse : rapport JSON, PDF, event MISP
│ ├── triage.py ← triage statique instantané (pefile) + capa (ATT&CK)
│ ├── serve.sh ← lanceur worker + API (lab)
│ ├── restart.sh ← redémarrage auto-réparant (orphelines, jobs bloqués)
│ └── frontend/ ← SPA React/Vite → build dans dist/
├── baselines/ ← mesures de furtivité (Pafish) horodatées
└── tools/
└── vnc-tunnel.sh ← tunnel SSH vers le VNC de la VM
Le principe de déploiement est simple : on écrit et on versionne le code sur le
poste de dev, deploy.sh le pousse sur l’hôte, et on lance les scripts là-bas.
Aucune commande tapée à la main sur l’hôte, tout est rejouable.
Chaque script d’installation est idempotent : le relancer ne casse rien et ne
duplique rien. Les scripts sourcent install/lib.sh, qui fournit log, ok,
skip, die, le chargement de config.env, l’arrêt propre graceful_stop et les
aides d’interception TLS.
6. Installation pas à pas
Prérequis : un hôte Linux avec la virtualisation matérielle disponible (/dev/kvm
présent). Sur une VM cloud, cela suppose la virtualisation imbriquée activée.
# sur le poste de dev
./deploy.sh user@hote
# sur l'hôte
cp ~/cyberhunter/install/config.env.example ~/cyberhunter/install/config.env
# … adapter config.env si besoin …
Les blocs se lisent dans l’ordre : chacun suppose le précédent en place. Les blocs 0 à C amènent une détonation fonctionnelle en ligne de commande ; D à H ajoutent l’analyse, la plateforme, l’interface et le passage à l’équipe.
Bloc 0 : le socle de virtualisation
But : confirmer que KVM fonctionne réellement (accélération matérielle), pas seulement qu’il est installé.
sudo apt install -y qemu-kvm libvirt-daemon-system cpu-checker
kvm-ok # → "KVM acceleration can be used"
sudo virt-host-validate qemu # → "Checking for hardware virtualization : PASS"
Le test décisif : démarrer une VM et vérifier qu’elle tourne en type='kvm' et non
type='qemu' (ce dernier étant de l’émulation logicielle, environ 20 fois plus
lente).
sudo virsh dumpxml <vm> | head -1 # → <domain type='kvm' ...>
Sur une VM cloud, le
type='qemu'silencieux est le piège classique : la VM démarre, tout semble marcher, mais l’émulation rend l’interactivité injouable. Toujours vérifier cette ligne.
Bloc A : l’hôte de détonation et le réseau isolé
But : un environnement où un malware peut s’exécuter sans jamais atteindre Internet ni le réseau de l’entreprise.
sudo ~/cyberhunter/install/install.sh 00 01 10
00-storage.shformate le disque de données en XFS sur/var/lib/libvirt(par UUID dans/etc/fstab, car l’ordre des/dev/sdXpeut changer) et prépare un espace de travail éphémère recréé au démarrage.01-packages.shinstalle QEMU/KVM, libvirt,libguestfs-tools, OVMF (firmware UEFI) et swtpm (TPM virtuel). Ces deux derniers sont exigés par Windows 11. Il déclare aussi le pool de stockage libvirt.10-network.shcrée le réseau isolécyberhunter-simet installe INetSim.
Le cœur de l’isolation, c’est que le réseau libvirt n’a aucune balise <forward>.
À lui seul, ce détail interdit tout routage entrant ou sortant.
<network>
<name>cyberhunter-sim</name>
<bridge name='virbr-sim' stp='on' delay='0'/>
<dns enable='no'/> <!-- libère le port 53 pour INetSim -->
<ip address='10.77.0.1' netmask='255.255.255.0'>
<dhcp><range start='10.77.0.100' end='10.77.0.200'/></dhcp>
</ip>
</network>
INetSim écoute sur 10.77.0.1 et répond à la place de tous les serveurs (HTTP,
HTTPS, DNS, SMTP, IRC…). Le malware croit parler à Internet, il parle à l’hôte. En
défense en profondeur, un service cyberhunter-firewall bloque depuis le pont les
métadonnées cloud (169.254.169.254) et les plages internes.
Ne jamais toucher la chaîne
INPUTdu pare-feu : seul le trafic émis par les VM (FORWARD -i virbr-sim) est filtré, pour ne pas couper le SSH d’administration.
Bloc B : l’image Windows durcie
But : une image de base Windows installée sans interaction, maquillée pour ne pas ressembler à une VM, et prête à être observée.
# 1. récupérer les ISO (Windows déposé à la main ou via config, VirtIO auto)
sudo ~/cyberhunter/image/20-fetch-iso.sh
# 2. installer Windows sans interaction (~10 à 15 min)
sudo ~/cyberhunter/image/21-build-base.sh
# → suivre à l'écran : ./tools/vnc-tunnel.sh puis client VNC sur localhost:5901
# 3. durcir l'image (VM éteinte)
sudo ~/cyberhunter/image/22-harden.sh
# 4. figer l'image en base immuable + gabarit + manifeste
sudo ~/cyberhunter/image/24-freeze.sh
L’installation automatisée repose sur autounattend.xml, lu par le programme
d’installation de Windows sur un CD dédié. Il définit le partitionnement, le compte
auto-connecté, contourne les écrans OOBE et, point crucial, empêche BitLocker (voir
§7).
Le durcissement (22-harden.sh) applique tout hors ligne, pour qu’il soit versionné
et rejouable. Il ne vit pas dans une VM bricolée à la main, il vit dans le dépôt
sous forme de .reg et de scripts. Sur une autre infra, on rejoue les mêmes
fichiers. Il comprend quatre volets :
-
Registre (
harden/*.reg) : deux AutoLoggers ETW démarrés au boot. Le premier trace des fournisseurs manifest (processus, réseau, DNS, AMSI, PowerShell) versC:\cyberhunter\trace.etl. Le second, le Global Logger, trace le noyau en format MOF (processus, images, fichiers, registre) versC:\cyberhunter\kernel.etl, la seule trace exploitable hors-Windows (voir bloc D). S’y ajoutent des réglages anti-évasion. -
SMBIOS maquillé (
scripts/spoof-libvirt.py) : la VM se présente comme un ThinkPad Lenovo réel, le bit « hyperviseur » du CPUID est masqué, KVM est caché, et la carte réseau VirtIO (signature de VM) devient une e1000e Intel ordinaire.<sysinfo type='smbios'> <system> <entry name='manufacturer'>LENOVO</entry> <entry name='product'>20UES2K600</entry> <entry name='family'>ThinkPad T14</entry> </system> </sysinfo> <cpu mode='host-passthrough'> <feature policy='disable' name='hypervisor'/> </cpu> <features><kvm><hidden state='on'/></kvm></features> -
Profil « vécu » : documents, dossier Projets, téléchargements, pour qu’un malware qui inspecte le profil ne tombe pas sur une VM toute neuve.
-
Désactivation de Windows Defender : indispensable pour une sandbox, sinon Defender met l’échantillon en quarantaine avant qu’on l’observe. Sur Windows 11, les clés de politique ne suffisent pas (Tamper Protection les ignore). Le volet décisif, c’est la désactivation des services Defender dans la ruche SYSTEM (
WinDefend,WdFilter,WdBoot…Start=4) : un service qui ne démarre pas ne peut rien réappliquer.
Enfin, 24-freeze.sh fige l’image durcie en base immuable (lecture seule, empreinte
SHA-256 enregistrée) et sauvegarde le gabarit XML (SMBIOS, masquage hyperviseur,
e1000e) dont dériveront les domaines de détonation.
Choisir l’OS d’analyse (catalogue d’images)
Comme sur ANY.RUN, l’analyste choisit l’OS invité sur lequel détoner. Chaque OS
gelé est une entrée d’un catalogue (/var/lib/libvirt/base/images.json) : id,
libellé, os/version/arch, et les chemins de sa base qcow2 et de son gabarit. On
enregistre une image avec image/register-image.sh (idempotent).
sudo ./image/register-image.sh \
--id win11-x64 --label "Windows 11 (64-bit)" --os windows --version 11 --arch x64 \
--base .../cyberhunter-win11-base.qcow2 \
--template .../cyberhunter-win11-base.template.xml --default
L’API expose le catalogue (GET /api/images), la soumission porte l’id d’image
choisi, et le worker résout base et gabarit pour lancer la VM sur le bon OS. Chaque
gabarit étant propre à son image, ajouter Windows 7/10/11, en 32 ou 64 bits, ne
demande que de construire l’image correspondante puis de l’enregistrer : aucun autre
code ne change.
Procédure complète : ajouter un OS
Toutes les commandes se lancent sur l’hôte, en root. Le pipeline de build est
paramétré par image/config.env (copie de config.env.example). Exemple pour
Windows 10 Pro (64 bits) :
-
Obtenir l’ISO Windows. Deux options : déposer l’ISO à la main dans
ISO_DIR(/var/lib/libvirt/iso) sous le nom voulu, ou laisser20-fetch-iso.shla récupérer via Fido (FIDO_ALLOW=yes, nécessite PowerShell sur l’hôte). -
Régler
image/config.envpour l’OS cible :VM_NAME=cyberhunter-win10-base # nom distinct → n'écrase pas Win11 WIN_ISO_NAME=win10.iso # nom de l'ISO dans ISO_DIR WIN_RELEASE=10 WIN_IMAGE_NAME=Windows 10 Pro # édition exacte (clé /IMAGE/NAME) WIN_OSVARIANT=win10 # identifiant osinfo pour virt-install VIRTIO_WIN=w10 # dossier de pilotes VirtIO de cet OS -
Récupérer les ISO :
sudo ./image/20-fetch-iso.sh. -
Installer sans interaction (~10 à 15 min, suivre en VNC jusqu’au bureau stable) :
sudo ./image/21-build-base.sh. -
Durcir (VM éteinte) :
sudo ./image/22-harden.sh. -
Geler en base immuable, gabarit et manifeste :
sudo ./image/24-freeze.sh. -
Enregistrer dans le catalogue :
sudo ./image/register-image.sh \ --id win10-x64 --label "Windows 10 (64-bit)" --os windows --version 10 --arch x64 \ --base /var/lib/libvirt/base/cyberhunter-win10-base.qcow2 \ --template /var/lib/libvirt/base/cyberhunter-win10-base.template.xml
Le nouvel OS est aussitôt proposé à la soumission, sans redémarrer l’API ni toucher
au code. --default bascule l’image par défaut (celle que le pool préchauffe). Le
durcissement anti-évasion est identique pour tous les OS.
Bloc C : la détonation
But : exécuter un échantillon dans une VM jetable, tout collecter, et rendre la VM propre en une seconde, sans jamais modifier l’image de base.
sudo ~/cyberhunter/detonate/detonate.sh /tmp/echantillon.exe \
'cmd.exe /c %SAMPLE% > C:\cyberhunter\sortie.out 2>&1'
Le cycle est entièrement automatisé, dans cet ordre :
-
Overlay jetable : un qcow2 dont l’image de base figée est le backing file. Créé en une seconde, il ne stocke que les différences ; le détruire « restaure » la VM. La base n’est jamais touchée.
qemu-img create -f qcow2 -F qcow2 -b base.qcow2 overlay.qcow2 -
Injection sans agent : l’échantillon est déposé dans l’overlay hors ligne (montage explicite, comme au bloc B), et une clé RunOnce le lance à la connexion automatique. RunOnce est natif Windows : une configuration, pas un programme résident. La même étape injecte la CA d’interception TLS et l’AutoLogger noyau dans la ruche SYSTEM, toujours hors ligne.
-
Domaine éphémère sur le réseau isolé, dérivé du gabarit durci, avec un nom et un UUID uniques, branché sur
cyberhunter-sim. Aucune sortie possible. -
Collecte pendant l’exécution : capture PCAP sur le pont et captures d’écran régulières prises par l’hyperviseur.
-
Dump mémoire en fin d’analyse (
virsh dump --memory-only) pour la forensique, lisible par Volatility 3. -
Arrêt propre : extinction ACPI pour que Windows vide son cache disque (voir §7), avec repli sur l’arrêt forcé si l’invité ne répond pas.
-
Récolte hors ligne des fichiers déposés (Temp, AppData, Startup…) et des traces ETW produites par les AutoLoggers.
-
Destruction de l’overlay. Base intacte, prête pour l’analyse suivante.
Chaque analyse produit un dossier horodaté :
results/run-AAAAMMJJ-HHMMSS-xxxxxx/
├── capture.pcap ← trafic réseau
├── screenshots/ ← captures périodiques (PNG)
├── memory.dump ← RAM complète de la VM
├── dropped/ ← fichiers déposés + traces ETW (trace.etl, kernel.etl)
└── manifest.json ← métadonnées de l'analyse
Le worker impose le type de lancement selon l’extension du fichier (launchers.py) :
un PE est exécuté directement, un script passe par son interpréteur natif (wscript,
powershell, mshta, rundll32, msiexec), un .lnk ou un document par l’ouverture
associée. Un email .eml ou .msg n’est pas détoné tel quel : il est décortiqué
hors sandbox (expéditeur, SPF/DKIM/DMARC, indices d’usurpation, liens, pièces
jointes), puis l’analyste détone une pièce jointe ou ouvre un lien dans la VM en un
clic.
Mesurer la furtivité : Pafish
Pafish est un exécutable bénin qui lance une
centaine de tests de détection de VM/sandbox et journalise, pour chacun, s’il a
repéré l’environnement (traced!) ou non (OK). C’est notre mètre-étalon de
furtivité : on le détonne comme un échantillon ordinaire, à chaque changement du
durcissement, et on suit le score. Les mesures sont archivées dans baselines/.
Trois mesures successives illustrent la boucle « mesurer, corriger, re-mesurer » :
| Mesure | traced! | Correctif apporté |
|---|---|---|
| v1 (baseline) | 12 | - |
| v2 | 11 | Microsoft Hv éliminé (retrait des enlightenments Hyper-V) |
| v3 | 9 | identifiant disque + OEM ID des tables ACPI (voir ci-dessous) |
Les deux corrections de la v3 se règlent au niveau de l’hôte, sans rien installer dans l’invité :
- Identifiant disque SCSI
QEMU HARDDISK. Windows recopie la chaîne ATA du disque dansHKLM\HARDWARE\DEVICEMAP\Scsi\...\Identifier; QEMU y metQEMU HARDDISK. En QEMU 6.2 le deviceide-hdaccepte unmodel=que libvirt n’expose pas :make-domain.pyle passe parqemu:commandlineet présente un SSD SATA crédible (Micron_1100_MTFDDAV512TBN). Zéro changement de contrôleur, donc aucun risque de boot. - OEM ID des tables ACPI
BOCHS/BXPC. QEMU code ces valeurs en dur ; Windows les recopie dansSystemBiosVersion, ce que Pafish repère. Seule l’option-machine ...,x-oem-id=...les change, et libvirt ne l’expose pas. On passe donc par un émulateur enveloppe (install/09-emulator-oem.shcompileqemu-oem-wrap) : il réécrit l’argument-machinepour y injecterx-oem-id=LENOVO,x-oem-table-id=TP-T14, puis exec le vrai qemu. Placé dans/usr/binet déclaré dans l’abstraction AppArmorlibvirt-qemu, il tourne sous le même confinement que qemu.
Les 9 items restants se répartissent ainsi :
| Famille | Nombre | Nature |
|---|---|---|
| Absence d’humain (souris, dialogues) | 6 | Disparaît en usage interactif, notre fonctionnalité phare |
Timing inhérent (rdtsc VM-exit) | 1 | Quasi imbattable ; toutes les sandboxes échouent |
| Uptime court (< 12 min) | 1 | Naturellement résolu par le pool préchauffé |
Hook ShellExecuteExW | 1 | Probable faux positif Win11, pas un marqueur VM |
En usage interactif (souris et dialogues satisfaits par l’analyste), il ne reste
donc que 3 détections, dont 2 quasi structurelles. Tout le reste passe en OK :
VirtualBox, VMware, Sandboxie, Wine, Cuckoo, taille disque, RAM, nombre de CPU,
username, chemins, bit hyperviseur, marque CPU, identifiant disque,
SystemBiosVersion.
Autres batteries de tests : al-khaser et sandbox_detector
Pafish reste le mètre-étalon rapide, mais l’image a été confrontée à deux autres détecteurs pour élargir la couverture (tous deux bénins, détonnés comme des échantillons ordinaires).
- al-khaser (~257 techniques, la référence exhaustive) : 229 OK, 28 détections.
- sandbox_detector (arxhr007, C) : 3 sur 22.
Les 28 détections d’al-khaser se regroupent nettement :
| Catégorie | Nombre | Corrigeable ? |
|---|---|---|
Absence de capteurs matériels (WMI : température, ventilateur, voltage, CIM_*) | ~16 | Non ; QEMU n’expose pas ces classes, échoue sur toutes les VM |
Artefacts structurels QEMU (PCI VEN_1B36, tables SMBIOS/ACPI, Enum IDE/SCSI) | ~6 | Oui, mais intrusif (risque de boot) |
| Ressources (nombre de cœurs, taille disque) | ~3 | Oui, paramètres de VM |
| Absence d’humain (souris, input, parent ≠ explorer) | 3 | Passe en usage interactif |
En croisant les trois outils, les seuls écarts récurrents et réellement actionnables sont l’uptime (résolu par le pool préchauffé), les ressources (cœurs, RAM, disque, réglables au lancement) et le réalisme de l’image (fichiers récents). Le gros bloc irréductible d’al-khaser relève des capteurs matériels physiques absents de toute VM, que même les sandbox commerciales ne passent pas. Ces pistes sont documentées ici mais pas encore mises en œuvre.
Bloc D : introspection et schéma d’événements
But : transformer les traces brutes (dump mémoire, PCAP, ETW) en observation comportementale exploitable, et surtout figer le schéma d’événements que tout le reste consommera.
L’analyse est séparée de la détonation (analyze/analyze.sh <dossier>), donc
rejouable sur des traces déjà collectées sans re-détoner. Elle tourne
automatiquement en fin de détonation si AUTO_ANALYZE=yes.
sudo ./analyze/analyze.sh /var/lib/libvirt/results/run-AAAAMMJJ-HHMMSS-xxxxxx
Le pipeline enchaîne, dans l’ordre : normalisation Volatility, YARA, extraction de config, analyse réseau Suricata, télémétrie ETW, signatures ATT&CK, puis verdict. Chaque étape produit des événements d’une même forme, indépendante de la source :
{
"type": "process", "source": "volatility3",
"ts": "2026-09-02T05:15:40",
"pid": 5452, "ppid": 5104, "process": "pafish.exe", "wow64": true
}
C’est le choix qui structure le plus le projet. Qu’un événement vienne de Volatility, de memflow, de Suricata ou d’ETW, il a toujours la même forme. L’interface et le stockage ne savent jamais d’où il vient ; ajouter une source revient à écrire un nouveau producteur de ces mêmes événements.
Deux sources de télémétrie processus coexistent. Volatility couvre le post-mortem
sur le dump. La trace ETW noyau (etwfast.py) couvre l’activité en direct :
processus avec leur ligne de commande complète, chargements de DLL, activité
fichiers et registre. Sur une détonation de Pafish, l’introspection reconstruit
toute la chaîne de lancement, sans aucun agent, depuis la seule mémoire :
userinit → explorer → runonce.exe → cmd.exe → pafish.exe
runonce.exe visible dans l’arbre prouve le mécanisme d’injection ; malfind
signale les régions mémoire exécutables suspectes (là où apparaîtrait le shellcode
injecté d’un vrai malware).
Pourquoi un parseur ETW maison (etwfast.py)
Décoder la trace noyau a demandé d’écrire notre propre parseur, pour deux raisons.
D’abord, il fallait la bonne trace. L’AutoLogger classique ne sait pas capturer les
événements du NT Kernel Logger (limite documentée par Microsoft) : on passe donc par
le Global Logger, dont les flags noyau se déclarent en EnableKernelFlags
(REG_BINARY, pas EnableFlags), injecté dans la ruche SYSTEM, en mode circulaire
pour garder l’activité récente de l’échantillon.
Ensuite, aucune bibliothèque toute faite ne convenait. La référence Python,
etl-parser, met environ 2 minutes pour un fichier de 325 Mo (elle reconstruit
chaque événement via construct, coûteux), et surtout il lui manque des structures :
elle ne décode ni le groupe Registry, ni la version 3 du groupe File, présentes dans
nos traces récentes.
etwfast.py est donc un parseur autonome, sans dépendance :
- Il marche directement les buffers WMI et les événements en
struct.unpack, sans passer parconstruct, ce qui ramène le même fichier de 325 Mo à environ 7 secondes (un facteur ~15), et ne décode en profondeur que les groupes utiles (Process, Image, Registry, File). - Les structures MOF manquantes ont été reconstruites à la main à partir des octets bruts d’une vraie trace x64 : Registry v2 (InitialTime, Status, Index, KeyHandle, puis le nom en WString) et File v3 (Create, qui donne la table FileObject vers chemin, et les opérations Delete/Rename/Write).
- Piège rencontré : les événements de création de processus sont en version 4 du
groupe Process, où le SID (de taille variable) est précédé d’un champ
Flagset d’un blob de 16 octets. L’offset du SID n’est donc pas celui de la v3 ; le parseur lit la version MOF en tête d’événement et se cale sur l’octet de révision du SID (0x01), avec repli sur l’autre offset.
Le résultat est concentré sur l’arbre de processus de l’échantillon (les traces noyau capturent tout l’OS), et alimente l’onglet Système, les signatures de persistance et le verdict.
Bloc E : la plateforme
But : sortir du terminal. Soumettre depuis un navigateur, voir passer en file, et consulter le rapport, sans jamais toucher au malware côté plan de contrôle.
Deux processus, séparés par privilège :
- l’API (
server/app.py, FastAPI) enregistre la soumission et sert les résultats. Elle ne lance jamais de VM. Elle écoute sur127.0.0.1(accès par tunnel SSH) ; - le worker (
server/worker.py) tourne en root, prend les analyses en file et exécutedetonate.shpuis l’analyse. Un worker par hôte, ce qui préfigure le découpage multi-machines du passage à l’équipe.
L’état est partagé dans une base SQLite (server/db.py, SQLAlchemy, migrable vers
PostgreSQL sans toucher au code). Une analyse suit le cycle
queued → running → live → collecting → analyzing → done | error.
sudo ./install/03-webstack.sh # venv web : FastAPI, Uvicorn, SQLAlchemy, reportlab
sudo ./server/serve.sh # démarre worker + API
# depuis le poste : ssh -L 8080:127.0.0.1:8080 <hôte>, puis http://localhost:8080
Le worker impose son identifiant d’analyse à detonate.sh via RUN_ID_OVERRIDE, si
bien que l’enregistrement en base et le dossier de résultats portent le même nom.
L’écran live interactif repose sur session.sh, qui refond la détonation en phases
(start / collect / stop). La VM reste vivante pendant une fenêtre
configurable, statut live, port VNC exposé. L’API fait elle-même le pont
WebSocket vers VNC (/api/analyses/{id}/vnc), si bien que noVNC dans le navigateur
affiche l’écran et transmet clavier/souris, le tout via le seul port 8080 déjà
tunnelé, sans second tunnel ni websockify externe.
navigateur (noVNC) ⇄ ws://localhost:8080/api/analyses/<id>/vnc ⇄ API ⇄ VNC de la VM
restart.sh rend le service auto-réparant : au redémarrage, il retire les VM
d’analyse orphelines et repasse à error les analyses restées bloquées.
Bloc F : l’interface
But : une vraie interface d’analyste, pas une page de debug.
L’interface est un SPA React (Vite), au design dense repris du site cyberhunter.fr (fond sombre, accent bleu, police IBM Plex). Le tableau de bord accueille la soumission d’un fichier, d’une URL ou d’un email, le choix de l’OS d’analyse, une recherche (nom, sha256, url, tag, verdict), et deux cartes de synthèse (activité sur 14 jours, familles détectées).
La vue d’analyse place l’écran en haut (live pilotable ou rejeu des captures, agrandissable en plein cadre) et, en dessous, les onglets :
- Timeline : les événements horodatés, en lignes, avec un temps relatif et une pastille colorée par type.
- Réseau : alertes IDS (Suricata), HTTPS déchiffré (mitmproxy si activé), requêtes DNS, transactions HTTP et TLS (SNI), plus les connexions vues en mémoire.
- ATT&CK : matrice des techniques MITRE par tactique, bordure colorée par sévérité.
- Système : télémétrie ETW en direct (processus avec ligne de commande, fichiers, registre, DLL).
- Statique : triage instantané, disponible dès la soumission, avant détonation (hachages, en-tête PE, sections et entropie, imports notables, indices d’empaquetage), puis les capacités FLARE capa dès qu’elles sont prêtes.
À gauche, l’arbre de processus se concentre sur l’échantillon et sa descendance (un bouton déplie l’arbre système complet). À droite, en boîtes distinctes : la jauge de verdict colorée par niveau, la configuration extraite (famille, C2, mutex), les exports (rapport JSON, PDF au design Cyberhunter, event MISP), les étiquettes et les détails.
sudo ./install/05-frontend.sh # Node 20 + npm run build → server/frontend/dist/
L’interface ne parle qu’au schéma d’événements normalisé du bloc D et aux endpoints de l’API : elle ignore tout de Volatility, de libvirt ou de la détonation.
Bloc G : enrichissement et verdict
But : passer des faits bruts à une appréciation lisible, un verdict justifié plutôt
qu’un chiffre opaque. Tout se greffe en fin de analyze.sh et alimente le schéma
commun.
- YARA (
scan_yara.py, règles dansanalyze/rules/) scanne les fichiers déposés. Chaque correspondance devient un événementyaraavec une sévérité. - Extraction de configuration (
extractors/+config_extract.py) : un cadre à greffons où chaque module exposeextract(data, path) -> dict|None. Deux niveaux : des extracteurs génériques d’IOC (URLs, IPs, .onion, emails, persistance), et des parsers par famille dans l’esprit CAPE. Fournis : Remcos (ressourceSETTINGSchiffrée RC4), AsyncRAT/Quasar/DcRat/VenomRAT (réglages AES-256-CBC, PBKDF2 et vérification HMAC) et njRAT. Chacun reconnaît sa famille par signature avant d’extraire, et un auto-test sur vecteurs synthétiques prouve la logique sans échantillon réel. Une config de famille identifiée pèse lourd dans le verdict.Les formats de famille dérivent avec les versions : ces parsers suivent des formats publics et sont à revalider sur échantillon réel avant production.
- Analyse réseau (
netparse.py, installée parinstall/11-netanalysis.sh) : Suricata rejoue le PCAP hors ligne et soneve.jsondevient des événements du schéma (alertes de signatures Emerging Threats open, DNS, HTTP, TLS avec SNI). Sur le réseau isolé, INetSim répond à la place d’Internet, donc le PCAP contient tout ce que l’échantillon a tenté de joindre, même sans sortie réelle. DNS/HTTP/TLS sont informatifs ; seules les vraies alertes pèsent dans le verdict. - Interception TLS (
install/12-tlsmitm.sh, activée parTLS_MITM=yes) : pour voir le HTTPS en clair, mitmproxy tourne en coupure transparente, termine le TLS avec un certificat signé par sa CA, journalise la requête, puis relaie vers INetSim. La CA est importée dans le magasin racine de chaque overlay à l’injection (registre, hors ligne). Un malware qui épingle son certificat échouera (limite du MITM). - Signatures comportementales et MITRE ATT&CK (
signatures.py) : un jeu de règles déclaratives balaie le flux d’événements et produit des événementssignature, chacun tagué d’une technique ATT&CK (Txxxx) et de sa tactique, d’où la matrice affichée dans l’interface. Exemples : injection mémoire (T1055), interpréteurs détournés (T1059, T1218), transfert d’outil (T1105), persistance Run et Startup (T1547.001, alimentée par la télémétrie ETW), C2 (T1071), anti-sandbox (T1497). Les signatures servent la lisibilité et n’ajoutent pas de points. - Verdict (
verdict.py) agrège les signaux en un score 0-100 et un niveau (bénin, à surveiller, suspect, malveillant). Le score est une somme pondérée explicable : injections mémoire, YARA (par sévérité), alertes IDS (par sévérité), configuration de famille, persistance observée (ETW), exécution via binaires système détournés. Chaque point est justifié par une entréesignals.
Exemple réel sur Pafish : suspect (51/100), soit +36 pour 3 régions mémoire
exécutables suspectes et +15 pour la règle Anti_Analysis_Sandbox_Checks. C’est
cohérent : Pafish sonde l’anti-analyse sans être malveillant.
Bloc H : passage à l’équipe
But : passer d’un outil mono-poste à une base multi-utilisateurs, et tenter le démarrage quasi instantané façon ANY.RUN.
Multi-utilisateurs et privilèges :
- Authentification par jeton (
server/auth.py), optionnelle et rétrocompatible : sansCYBERHUNTER_TOKENS, tout le monde est « local » (mono-poste en tunnel) ; avec, chaque requête porte un jeton et l’analyse est estampillée de sonowner_id. La liste est filtrée par propriétaire. - PostgreSQL (
install/06-postgres.sh) : la base passe de SQLite à PostgreSQL par la seule variableDATABASE_URL(SQLAlchemy abstrait le dialecte). - Séparation des privilèges (
install/07-services.sh, unités systemd) :cyberhunter-workeren root (pilote libvirt),cyberhunter-apisous un utilisateur sans privilège (ne lance jamais de VM).
Pool de VM préchauffées : l’objectif est double, supprimer les ~60 s de boot Windows
et faire vieillir l’uptime (une VM fraîchement bootée se trahit). Le préchauffage
amène une VM au bureau avec un lanceur bénin en attente. La livraison à chaud passe
par un changement de média sur un lecteur CD présent dès le boot (virsh change-media) : le lecteur a déjà sa lettre côté invité, donc Windows monte le
média de façon sûre. Le worker orchestre ce vivier : avec POOL_SIZE > 0, il
maintient N VM chaudes et en consomme une à chaque job (puis en relance une). Sans
VM chaude prête, il retombe sur le boot à froid. Par défaut POOL_SIZE=0, donc la
bascule est opt-in.
Côté furtivité, le pool ne change rien au principe : le lanceur est le seul code invité, trivial et sans surveillance, et toute l’observation reste externe. La règle « aucun moniteur dans la VM » tient.
7. Pièges rencontrés et solutions
Ces obstacles ont été rencontrés et résolus pendant la construction. Ils sont documentés ici parce qu’ils reviendront sur toute réinstallation.
BitLocker automatique (Windows 11 24H2+)
Windows 11 récent chiffre le disque automatiquement à l’installation (« Device
Encryption »), même sans compte Microsoft. La partition Windows devient alors
unknown filesystem type 'BitLocker' pour les outils hors ligne, ce qui casse tout :
durcissement, injection d’échantillon, introspection.
Solution (dans autounattend.xml, passe specialize) :
<component name="Microsoft-Windows-Deployment" ...>
<RunSynchronous>
<RunSynchronousCommand>
<Path>reg add "HKLM\SYSTEM\CurrentControlSet\Control\BitLocker" /v PreventDeviceEncryption /t REG_DWORD /d 1 /f</Path>
</RunSynchronousCommand>
</RunSynchronous>
</component>
À mettre dans la passe
specialize, paswindowsPE: là,HKLMdésigne le registre de l’environnement d’installation, pas celui du Windows installé.
Prompt de boot UEFI « Press any key to boot from CD »
Le firmware OVMF attend une frappe que l’installation automatisée ne fournit pas ;
faute de frappe, il tombe sur « No bootable device ». 21-build-base.sh envoie donc
Entrée en rafale juste après le démarrage :
for _ in $(seq 1 40); do
virsh send-key "$VM_NAME" KEY_ENTER # jeu de codes PAR DÉFAUT (usb1 ne marche pas)
sleep 0.3
done
Disque VirtIO invisible pendant l’installation
Windows ne voit pas un disque VirtIO en phase d’installation (pilote non chargé). Solution : disque OS sur bus SATA, vu nativement. Bonus, un contrôleur SATA est plus ordinaire qu’un VirtIO du point de vue furtivité.
libguestfs inspect-os cassé pour Windows récent
Sur Ubuntu 22.04 (libguestfs 1.46), l’inspection d’OS échoue (« No operating system
could be detected ») pour Windows 11 25H2, alors que hivex lit parfaitement les
ruches. Or virt-win-reg et virt-customize dépendent de cette inspection.
Solution (scripts/apply-offline.sh) : contourner l’inspection en montant la
partition Windows explicitement (détection dynamique de la partition qui contient
/Windows/System32/config/SOFTWARE), puis éditer les ruches avec hivexregedit, qui
n’a pas besoin d’inspection. Deux sous-pièges de hivexregedit : il veut de l’UTF-8
avec CRLF (pas de l’UTF-16LE), et il ne crée pas les clés parentes manquantes, d’où
scripts/expand-reg.py qui génère d’abord toutes les clés ancêtres.
Démarrage rapide de Windows (fast startup)
L’arrêt « hybride » de Windows laisse le NTFS dans un état sale, gênant pour les
opérations hors ligne. Désactivé via autounattend.xml : powercfg /h off.
UUID mismatch au maquillage SMBIOS
libvirt exige que l’UUID déclaré dans le SMBIOS soit identique à l’UUID du domaine.
spoof-libvirt.py reprend donc l’UUID existant du domaine plutôt que d’en générer
un nouveau.
Sessions libvirt (qemu:///system vs qemu:///session)
Une VM créée en sudo vit dans qemu:///system ; un virsh sans sudo tombe sur
qemu:///session (vide). On fixe LIBVIRT_DEFAULT_URI=qemu:///system et on reste
cohérent (toujours root pour les opérations libguestfs, sinon le cache d’appliance
partagé /var/tmp/.guestfs-* se corrompt).
ETW noyau : Global Logger, pas AutoLogger
Pour obtenir une trace noyau décodable hors-Windows, l’AutoLogger ne suffit pas : il
ne sait pas capturer les événements NT Kernel Logger (limite documentée par
Microsoft). Il faut le Global Logger, dont les flags noyau se déclarent en
EnableKernelFlags (REG_BINARY), pas en EnableFlags. Le fichier est configuré en
mode circulaire, avec une horloge FILETIME pour un horodatage directement
convertible, et des buffers assez petits pour être vidés régulièrement (les gros
buffers ne se vident jamais sur une courte détonation).
Récolte non déterministe : arrêter la VM proprement avant de collecter
Avec un arrêt brutal (virsh destroy), les écritures récentes de l’invité encore
dans le cache NTFS ne parviennent jamais au disque virtuel. La récolte hors ligne
devient alors non déterministe : d’un run à l’autre, tel fichier déposé est présent,
tel autre a disparu.
Correctif (install/lib.sh:graceful_stop, appelé par detonate.sh et session.sh) :
on envoie d’abord un arrêt ACPI (virsh shutdown, l’équivalent du bouton power).
Windows s’éteint en vidant son NTFS, et qemu referme le qcow2 proprement. On
n’arrête brutalement qu’en repli, si l’invité n’a pas répondu au bout de
STOP_TIMEOUT (45 s par défaut) : un vrai malware peut ignorer l’arrêt, on ne reste
donc pas bloqué. Le dump mémoire est pris avant cet arrêt.
C’est un compromis agentless : sans agent invité, on ne peut pas forcer un
FlushFileBuffersciblé ; l’arrêt ACPI est le seul levier propre pour vider le cache. Le comportement du malware à l’extinction (persistance, nettoyage) est d’ailleurs capturé au passage, puisqu’il s’exécute vraiment.
8. Glossaire
- Agentless : observation du malware sans aucun programme dans la VM analysée.
- VMI (Virtual Machine Introspection) : inspecter l’état d’une VM (mémoire, CPU) depuis l’extérieur, au niveau hyperviseur.
- ETW (Event Tracing for Windows) : mécanisme de traçage natif de Windows ; ici activé par registre (AutoLogger et Global Logger), donc sans agent.
- AutoLogger / Global Logger : sessions de trace ETW démarrées automatiquement au boot, configurées par des clés de registre. Le Global Logger est le seul à capter les événements noyau (MOF).
- SMBIOS : tables qui décrivent le « matériel » (constructeur, modèle, série) ; les malwares les lisent pour détecter une VM. On les maquille.
- INetSim : simulateur de services Internet ; répond aux requêtes du malware sans laisser sortir de trafic réel.
- Overlay qcow2 / backing file : fichier disque jetable qui n’enregistre que les différences par rapport à une image de base figée ; restaure une VM propre en une seconde.
- autounattend.xml : fichier de réponses qui automatise l’installation de Windows.
- libguestfs / hivex : outils pour lire et modifier une image disque (et le registre Windows) sans démarrer la VM.
- nested virtualization : faire tourner un hyperviseur dans une VM ; nécessaire pour héberger la sandbox sur une VM cloud.
- IOC : indicateur de compromission (domaine, IP, URL, hash) extrait de l’analyse.
9. Changelog
Il n’y a pas encore de versions étiquetées. Le journal est organisé par date (la plus récente en tête), puis par composant de l’infrastructure ; sous chacun, les changements sont classés en Ajouté / Modifié / Corrigé. Le hash du commit correspondant est indiqué entre parenthèses.
2026-09-03
Introspection et analyse
Ajouté
- Télémétrie noyau en direct (ETW) : processus avec leur ligne de commande et
leur parent, chargements de DLL, activité fichiers et registre, présentés dans
un onglet « Système ». La trace est produite par le Global Logger (l’AutoLogger
ne sait pas capturer les événements NT Kernel Logger), injecté par overlay dans
la ruche SYSTEM, en mode circulaire. (
7990e1b) - Parseur ETW autonome et rapide (
etwfast.py, ~7 s pour 325 Mo là où etl-parser mettait ~2 min) : walkerstruct.unpackqui décode les structures MOF Process/Image ainsi que Registry v2 et File v3, absentes d’etl-parser, reconstruites depuis les octets bruts d’une trace réelle (dont le piège du Process version 4, où l’offset du SID diffère). La sortie est concentrée sur l’arbre de processus de l’échantillon. - Persistance détectée en direct (écriture d’une clé Run/Winlogon, dépôt dans un dossier de démarrage) mappée MITRE ATT&CK T1547.001, avec un signal dédié au verdict. Les vraies lignes de commande ETW alimentent aussi les règles d’exécution et de découverte.
Plateforme et interface
Ajouté
- Recherche et historique : filtre libre (nom, sha256, url, image, tag, verdict),
filtre par verdict, étiquettes éditables par analyse et re-analyse en un clic.
Le niveau de verdict est recopié en base pour le tri et l’affichage. (
96fdb90) - Refonte visuelle de l’interface reprise du site cyberhunter.fr : palette et fond, logo réel, jauge de verdict circulaire, onglets en pastilles, matrice ATT&CK en cartes, vue d’analyse avec écran agrandissable et colonne droite en boîtes. Cartes de synthèse (activité, familles) sur le tableau de bord.
- Rapport PDF entièrement repensé au design Cyberhunter (bandeau logo, badge de verdict coloré, sections, thème sombre).
- Artefacts téléchargeables (PCAP, dump mémoire, fichiers déposés, traces ETW) via un onglet dédié, avec protection anti-traversal.
- Panneau IOC consolidé (domaines, IPs, URLs, C2, mutex, clés de persistance) avec copie en un clic et export MITRE ATT&CK Navigator.
- Enrichissement Threat Intelligence des IOC (VirusTotal, AbuseIPDB), strictement opt-in par clés d’API, côté plan de contrôle, jamais depuis la VM.
Détonation et collecte
Ajouté
- Détonation par type de fichier : PE lancé directement, scripts et documents via
l’interpréteur natif adéquat (wscript, powershell, mshta, rundll32, msiexec,
ouverture associée pour
.lnket Office), avec surcharge possible de la commande de lancement. (96fdb90) - Analyse d’emails de phishing (
.eml/.msg) : l’email est décortiqué hors sandbox (expéditeur, SPF/DKIM/DMARC, indices d’usurpation, liens, pièces jointes), puis chaque pièce jointe se détone et chaque lien s’ouvre dans la VM en un clic. (96fdb90)
2026-09-02
Image invité et construction
Ajouté
- Catalogue d’images (
base/images.json+image/register-image.sh) et choix de l’OS à la soumission, comme sur Any.run. (3e0cf57) - Pipeline de construction générique : l’édition installée, les pilotes VirtIO et
le type d’OS deviennent des paramètres, avec la procédure complète d’ajout d’un
OS (Windows 7/10/11, 32 ou 64 bits). (
ebba128) - Deuxième image construite : Windows 10 x64 (durcie et enregistrée au catalogue), aux côtés de Windows 11.
Corrigé
- Compatibilité de l’autounattend avec Windows 10 : retrait du bloc
DriverPathsmal placé (refusé par Win10) et guillemets obligatoires autour de l’édition. (f170d39,8fd114e)
Furtivité (anti-évasion)
Ajouté
- Mesure continue de la furtivité avec Pafish (baselines horodatées), étendue
ensuite à al-khaser (229 tests OK, 28 détectés) et sandbox_detector (3 sur 22).
(
b75f143)
Modifié
- Retrait des enlightenments Hyper-V : élimine le marqueur
Microsoft Hvet fait passer Pafish de 12 à 11 détections. (f20e773) - Identifiant disque
QEMU HARDDISKremplacé par un modèle de SSD crédible, et OEM des tables ACPI (BOCHS/BXPC) réécrit enLENOVO: Pafish de 11 à 9. (b75f143)
Corrigé
- Windows Defender remettait l’échantillon en quarantaine ; les services Defender
sont désactivés dans la ruche SYSTEM, car les clés de politique ne suffisent pas
(Tamper Protection les ignore). (
3d76ada)
Détonation, pool et collecte
Ajouté
- Détonation : overlay jetable adossé à la base figée, injection de l’échantillon
sans agent (clé RunOnce posée hors ligne), collecte du PCAP, des captures, du
dump mémoire et des fichiers déposés. (
3d76ada) - Pool de VM préchauffées piloté par le worker (
POOL_SIZE) : N machines déjà au bureau, utilisées à chaud dès qu’un fichier arrive (plus de temps de boot, uptime déjà élevé), avec repli sur le boot à froid. (74741ce)
Modifié
- Livraison à chaud du pool refaite : changement de média sur un lecteur CD
présent dès le démarrage, au lieu du disque USB branché à chaud dont la lettre
n’apparaissait pas de façon sûre dans l’invité. (
0bac3e9)
Corrigé
- Récolte des fichiers déposés rendue déterministe : arrêt ACPI propre avant la
collecte (avec repli sur l’arrêt forcé), pour que Windows vide son cache
d’écriture sur le disque au lieu de perdre des fichiers. (
5ec6c85)
Introspection et analyse
Ajouté
- Introspection post-mortem avec Volatility 3 sur le dump, et surtout un schéma
d’événements unique que toute la suite consomme. (
4350367) - Règles YARA sur les fichiers déposés et moteur de verdict avec un score
expliqué signal par signal. (
581da99) - Extraction de configuration : cadre à greffons, extracteurs génériques d’IOC
(
f000d4d) puis parsers par famille (Remcos, AsyncRAT/Quasar/DcRat/VenomRAT, njRAT) reconnaissant leur famille avant d’extraire C2 et réglages. (1efcc24) - Introspection mémoire en direct (memflow) : arbre de processus parent vers
enfant en temps réel, via un binaire Rust autonome (contourne l’incompatibilité
d’ABI des bindings Python), avec offset PPID calibré automatiquement.
(
81aad3c,3b9dfb7) - Analyse réseau enrichie : Suricata rejoue le PCAP hors ligne et alimente
l’onglet Réseau en alertes IDS (règles ET open), requêtes DNS, transactions
HTTP et TLS (SNI). Le worker capture aussi le PCAP (avant, seul le CLI le
faisait). (
697cfe5) - Interception TLS : mitmproxy en coupure transparente déchiffre le HTTPS. Sa CA
est importée dans le magasin racine de chaque overlay (registre, hors ligne),
les requêtes déchiffrées remontent en événements
https. Activable parTLS_MITM=yes. (76da679) - Signatures comportementales + cartographie MITRE ATT&CK : un moteur de règles
mappe les événements sur des techniques (Txxxx) et alimente une matrice ATT&CK
par tactique, plus un onglet dédié. Sert la lisibilité, sans modifier le score.
(
9e609fe) - Analyse statique instantanée : triage PE (pefile) dès la soumission, puis
capacités FLARE capa (mappées ATT&CK) calculées en tâche de fond. (
3cd9757)
Modifié
- Le verdict distingue une configuration de famille (signal fort) des simples IOC
génériques (plus bruyants). (
1efcc24)
Plateforme et interface
Ajouté
- Plateforme web : API FastAPI, worker de détonation, base de données,
soumission et rapports. (
5dc5c73) - Écran VNC live pilotable dans le navigateur, via un pont WebSocket porté par
l’API (un seul port tunnelé, sans composant externe). (
298e3f5) - Interface analyste React/Vite : dépôt du fichier, vue en trois colonnes, arbre
de processus, timeline, rejeu des captures, écran VNC intégré. (
b0c58f4) - Passage à l’équipe : comptes par jeton avec
owner_id, PostgreSQL en option et séparation des privilèges (API sans droits, worker en root) via systemd. (d0a04bc) - Sélecteur d’OS et colonne OS dans l’interface, alimentés par le catalogue
d’images. (
3e0cf57) - Soumission d’une URL (et plus seulement d’un fichier) : l’URL est ouverte dans
Edge côté invité (écran de premier lancement contourné), pour analyser phishing
et téléchargements pilotés. (
e39bbad) - Exports d’une analyse : rapport JSON consolidé, PDF (reportlab), et event MISP
(attributs IOC + tags galaxy MITRE ATT&CK) importable. (
44cfba7) - Onglet Statique : triage PE (hachages, en-tête, sections, imports notables,
indices d’empaquetage) puis capacités capa. (
3cd9757)
2026-09-01
Hôte et réseau
Ajouté
- Socle de virtualisation KVM/QEMU + libvirt, imbrication validée sur AMD
(la VM tourne bien en
type='kvm', pas en émulation). (504cf6c) - Réseau d’analyse totalement isolé (aucune route vers l’extérieur), INetSim qui
répond à la place d’Internet, et un service pare-feu qui bloque depuis le pont
les métadonnées cloud et les plages internes, sans jamais toucher au SSH
d’administration. (
504cf6c)
Image invité et construction
Ajouté
- Image Windows 11 installée sans interaction (autounattend), puis durcie hors
ligne : SMBIOS de ThinkPad, bit hyperviseur masqué, carte réseau e1000e, trace
ETW démarrée au boot, et profil “vécu” (documents, téléchargements). (
504cf6c)
Corrigé
- Chiffrement BitLocker automatique de Windows 11 empêché à l’installation, sans
quoi le disque devient illisible hors ligne (durcissement, injection,
introspection impossibles). (
504cf6c)
Documentation
Ajouté
- README initial (motivation, architecture, installation, pièges, glossaire) et
.gitattributesforçant les fins de ligne LF pour un rejeu identique quel que soit le poste. (6db7ac8,8f49e15)