~/blog / anatomie-malware-php-obfusqué
🛡️ sécurité wordpress

Anatomie d'une backdoor PHP : décortiquer un malware obfusqué couche par couche

Luc Del Beato 11 juin 2026 11 min de lecture

Un fichier PHP d'une seule ligne, illisible, 40 ko de charabia base64. C'est le genre de chose qu'on trouve planqué dans un wp-content/uploads et qui fait tourner le serveur d'un client à 3h du matin. Plutôt que de le supprimer et passer à autre chose, je l'ouvre. Parce que comprendre ce que fait une backdoor, c'est savoir ce qui a été compromis. Voici la méthode pour l'éplucher, sans jamais l'exécuter.

TL;DR

Pourquoi obfusquer ? Pour passer sous le radar

Une backdoor en clair, c'est trois lignes : prendre un paramètre dans la requête, le passer à system(), renvoyer la sortie. Lisible, évident, et détecté en une seconde par n'importe quel scanner. L'obfuscation ne sert pas à protéger une propriété intellectuelle ; elle a deux objectifs très précis.

L'obfuscation n'est pas du chiffrement. C'est un déguisement. Et un déguisement, par définition, se retire, il suffit de savoir où sont les coutures.

Les couches, du plus simple au plus retors

Un malware obfusqué est un oignon : chaque couche, une fois retirée, en révèle une autre, jusqu'au code réel au centre. Voici les techniques qu'on croise, dans l'ordre de fréquence.

// ⚠️ illustratif et NON exécutable, placeholders, pas un vrai payload
// ce à quoi ressemble la couche externe typique :
eval(gzinflate(base64_decode("…REDACTED_BASE64_BLOB…")));

// la même idée, en cachant le mot "eval" lui-même :
$fn = "\x65\x76\x61\x6c";            // = "eval", jamais écrit en clair
$fn(gzinflate(base64_decode("…REDACTED…")));

// reconstruction par codes ASCII :
$fn = chr(101).chr(118).chr(97).chr(108);   // = "eval"
$fn(str_rot13("…REDACTED_ROT13…"));
🧅
Lire la pile à l'envers : on déobfusque dans l'ordre inverse de l'écriture. eval(gzinflate(base64_decode(X))) se lit de l'intérieur vers l'extérieur : d'abord on décode le base64, puis on décompresse, et le sink eval est la dernière opération, celle qu'on ne laissera jamais s'exécuter.

Le sink : là où le code se transforme en action

Toutes ces couches ne servent à rien tant qu'il n'y a pas une fonction qui transforme une chaîne en code exécuté. C'est le point d'exécution, le sink, et c'est exactement là qu'il faut couper. PHP en offre plusieurs.

La règle de diagnostic est simple : trouve le sink, et tu as trouvé le cœur. Tout le reste n'est qu'emballage. Une fois le sink localisé, on sait précisément quelle est la dernière opération avant l'exécution, et donc où l'intercepter.

Déobfusquer sans exécuter : la règle d'or

Voici l'erreur fatale, celle que je vois faire à des gens pressés : copier le fichier sur un serveur de test et le lancer « juste pour voir ce que ça fait ». Non. On n'exécute jamais du code inconnu pour le comprendre. Le payload peut se réinfecter, contacter un C2, supprimer des preuves, ou se déclencher différemment selon l'environnement. L'exécuter, c'est laisser l'attaquant écrire la fin de l'histoire.

La bonne méthode transforme le sink en observateur. On remplace l'exécution par un affichage : au lieu de lancer la couche suivante, on l'imprime.

// ⚠️ technique d'analyse statique, illustratif, pas un vrai payload
// AVANT (ce que l'attaquant a écrit) :
//   eval(gzinflate(base64_decode("…REDACTED…")));

// APRÈS (ce qu'on écrit pour analyser, dans un bac à sable isolé) :
echo gzinflate(base64_decode("…REDACTED…"));
//   ^^^^ on remplace le SINK par echo : on DUMPE la couche suivante,
//        on ne l'exécute pas. Le code décodé s'affiche, inerte.

On lit la sortie. Si c'est encore de l'obfusqué, une nouvelle chaîne base64, un nouveau eval, on recommence : on remplace ce nouveau sink par un echo, et on dumpe la couche d'après. On répète jusqu'à ce que le vrai source apparaisse, lisible. C'est exactement le pelage de l'oignon, manuel et contrôlé.

Variante encore plus sûre : ne pas exécuter de PHP du tout. On décode les couches à la main, dans un environnement isolé, base64_decodeinflaterot13, avec des outils qui ne font que transformer des octets, jamais les interpréter. Un peu de Python, un peu de CyberChef hors-ligne, et on déroule. Plus lent, mais zéro risque d'exécution accidentelle.

🧪
Toujours en bac à sable : machine jetable, hors-ligne, sans accès au reste de l'infra. Même un echo mal placé peut déclencher quelque chose si le sink est plus malin qu'attendu. On isole d'abord, on décode ensuite. L'isolation n'est pas une précaution optionnelle, c'est la première étape.

Ce qu'on trouve au centre de l'oignon

Une fois toutes les couches retirées, le payload réel se révèle. Dans la quasi-totalité des cas que j'ai disséqués, c'est l'une de ces cinq familles, et savoir laquelle, c'est savoir ce que l'attaquant cherchait.

Les tells : repérer la bête avant même de l'ouvrir

Avant la dissection, il y a la détection. Certains signaux trahissent une backdoor obfusquée à l'œil nu, sans même décoder quoi que ce soit :

🔎
Le grep qui révèle : chercher la présence simultanée des fonctions de décodage et des sinks dans une arborescence (base64_decode, gzinflate, str_rot13, eval, assert) remonte la majorité des backdoors en quelques secondes. C'est le premier balayage que je lance sur tout site suspecté d'être compromis.

La mentalité Fixer : observer, isoler, décoder, comprendre

Ce travail de dissection, c'est exactement la même méthode que je décris dans mon article sur le debug de hardware vintage : face à un système non documenté et hostile, on ne devine pas, on ne bricole pas au hasard. On observe ce qu'il fait, on l'isole pour le manipuler sans risque, on décode son fonctionnement couche par couche, et on remonte à la cause racine.

Une carte électronique des années 80 sans schéma et une backdoor obfusquée posent le même problème cognitif : un comportement opaque qu'il faut rendre intelligible. La discipline est identique. On ne se contente pas de « ça remarche » ou « j'ai supprimé le fichier », on veut comprendre pourquoi, parce que c'est la seule façon de savoir si on a vraiment réglé le problème ou juste masqué un symptôme.

C'est aussi la suite logique de mon article sur les backdoors qui échappent aux scanners : là, je montrais pourquoi un scan automatique passe à côté ; ici, je montre comment on prend le relais à la main quand le scan a échoué.

Ce que je retiens

Décortiquer un malware, ce n'est pas de la paranoïa : c'est la seule façon de transformer un incident en connaissance. On sort de chaque dissection en sachant exactement ce qui s'est passé, et un système qu'on comprend est un système qu'on peut vraiment défendre.

// wp-pirate.fr

Un site WordPress qui se comporte bizarrement ?

Spam qui part tout seul, fichiers qui réapparaissent, réputation IP en chute libre : ce sont les signes d'une backdoor active. Sur wp-pirate.fr, je dissèque, je nettoie en profondeur et je verrouille, sans me contenter de supprimer le symptôme.

Faire auditer mon site
L
Luc Del Beato

Senior Lead Engineer, ~20 ans de web. Do-er passionné de résolution de problèmes, de belle architecture et d'automatisation ; les agents IA, c'est ma direction. Mon parcours →