Site WordPress infecté : supprimer les persistances de type eval/base64

Quand un site WordPress devient “injoignable” ou “bizarre” après une réouverture, on pense souvent au classique plugin compromis, à un thème modifié ou à des identifiants qui ont été pris. Pourtant, dans beaucoup d’incidents réels, la partie la plus difficile n’est pas de supprimer une porte d’entrée, c’est de casser la persistance. Et parmi les persistances les plus pénibles, celles qui s’appuient sur des charges utiles encodées en base64 et exécutées via des constructions de type eval, ou des variantes proches, reviennent très souvent.

Le but de cet article est de vous guider dans une démarche pragmatique pour traiter un site WordPress infecté qui utilise ce genre de mécanisme, en expliquant comment les persistances se mettent en place, comment les repérer, et surtout comment éviter le piège du “ça marche, donc c’est fini” alors qu’un traitement automatique réinjecte l’infection dès que vous rétablissez le trafic.

Ce que “eval/base64” raconte d’un compromis

Dans un code malveillant, la base64 sert souvent à masquer du contenu, et eval sert à l’exécuter au runtime. Le schéma typique ressemble à ceci, conceptuellement :

    Un fichier PHP (ou une ressource incluse) contient une chaîne base64. Le code décode cette chaîne pendant l’exécution. Ensuite, le résultat est exécuté avec eval, ou une construction qui revient à exécuter du code dynamique. Le tout est déclenché sur un événement discret, par exemple une requête vers une URL précise, ou une inclusion silencieuse dans l’ordre de chargement de WordPress.

Ce pattern n’est pas toujours exactement eval, mais l’intention est la même : contourner la lecture humaine, masquer la logique, et lancer une charge quand les conditions sont réunies.

Dans la vraie vie, quand on voit ce type de persistance, il y a trois raisons pour lesquelles les suppressions “au hasard” échouent :

Le code malveillant n’est pas seulement “dans un fichier”, il est distribué et relayé. Un fichier charge un autre fichier, qui à son tour décode et exécute quelque chose. Il peut y avoir un mécanisme de réécriture. Dès que vous supprimez un fichier, un autre élément réintroduit le contenu. Les conditions d’exécution peuvent être “douces” en apparence. Par exemple, le code ne déclenche que pour certains user agents, certaines plages d’IP, ou seulement quand un cookie existe. Un scan manuel en heure calme donne alors de faux sentiments de sécurité.

Les signaux qui doivent vous faire penser à une persistance, pas juste un “trou”

Avant d’ouvrir vos fichiers un à un, j’aime prendre cinq minutes pour cartographier les symptômes. Sur un site WordPress infecté, l’incident se manifeste rarement de manière unique. Il y a souvent un mélange de signes.

Vous pouvez, par exemple, observer :

    des redirections vers des domaines externes, parfois uniquement sur une page “au hasard” ou sur certaines sessions ; des pages qui se remplissent de contenu bizarre, sans que le CMS affiche une édition correspondante ; l’apparition de fichiers inconnus dans des répertoires qui ne devraient pas en contenir (ou des fichiers “nommés intelligemment”, proches de la structure WordPress) ; une hausse de charge serveur, des appels HTTP anormaux, ou des logs d’accès qui montrent des requêtes répétées vers des scripts non attendus ; des erreurs PHP qui pointent vers des lignes sans rapport direct avec votre thème ou vos plugins.

Le point important, c’est que la persistance, elle, aime rester discrète. Quand l’infection ne persiste pas, une suppression suffit souvent. Quand elle persiste, vous verrez généralement un retour du comportement après un délai très court, parfois dès la minute suivante, surtout si une tâche cron ou un mécanisme de chargement est en jeu.

image

Triage : sécuriser, figer, puis enquêter

La plupart des gens font l’erreur classique : ils nettoient immédiatement, puis ils laissent le site repartir en production, et ils “constatent” que ça revient. En production, ce que vous voulez, c’est figer l’état pour avoir une base factuelle. Sinon, vous chassez un fantôme.

Avant d’attaquer les fichiers liés à base64 ou eval, je recommande de mettre en place une séquence simple :

image

Désactiver l’exécution des parties les plus dangereuses (au minimum, réduire l’exposition). Préparer un inventaire des fichiers modifiés et des entrées suspectes. Reproduire le déclenchement de manière contrôlée, si possible en local ou via une copie du site. Ne pas supprimer sans comprendre, sauf si vous êtes sûr que c’est une impasse. Sinon vous risquez de casser la traçabilité et de perdre le fil.

Sur WordPress, “figer” signifie souvent : passer le site en maintenance, bloquer temporairement le trafic externe, ou au minimum limiter les accès pendant que vous inspectez. Le but est de ne pas permettre au code malveillant de continuer à s’exécuter en boucle.

Même si vous avez des accès shell, pensez à sauvegarder les fichiers suspects avant action. Vous voulez garder une copie lisible du code pour comprendre le chaînage, et pour vérifier si un autre fichier dépend de lui. Une fois que vous supprimez tout, vous perdez parfois la preuve de la persistance, ce qui rend la suite plus risquée.

Où regarder en premier sur WordPress (sans se faire piéger)

Le piège, avec les attaques WordPress, c’est qu’elles exploitent souvent la confiance implicite qu’on accorde aux emplacements “évidents”. Oui, les fichiers dans le dossier wp-content ou dans des plugins sont fréquents. Mais les persistances en eval/base64 peuvent être injectées via plusieurs voies.

Je commence généralement par trois “familles” d’endroits, car elles concentrent beaucoup d’implémentations :

    les fichiers PHP modifiés dans wp-content/plugins et wp-content/themes ; les emplacements d’inclusion non standard, y compris des fichiers ajoutés dans des sous-dossiers inattendus ; les scripts qui s’exécutent très tôt dans le cycle WordPress, par exemple des inclusions liées à l’amorçage, où une simple ligne peut déclencher la charge.

Ensuite, je vérifie aussi les mécanismes qui déclenchent ou réinjectent : un faux cron, un script d’amorçage via des hooks, ou un fichier qui “réécrit” d’autres fichiers lors de chaque requête. Dans un incident avec base64, il est fréquent de trouver un fichier “léger” qui fait uniquement décodage puis délégation vers une autre charge, souvent stockée ailleurs sur le filesystem.

Repérer la charge base64 et le point d’exécution eval

Quand vous cherchez de l’eval et de la base64, la tentation est de lancer un grep sur tout le serveur. C’est utile, mais attention aux faux positifs : eval peut apparaître dans des libs légitimes, même si c’est rare sur un WordPress standard.

La meilleure approche est de coupler :

    le repérage de chaînes base64 longues (celles qui ressemblent à du blob encodé) ; la présence d’appels de décodage (selon le code, base64_decode ou équivalents) ; l’existence d’exécution dynamique (eval ou équivalents de “construction d’exécution”).

Ensuite seulement, vous regardez ce qui suit.

Dans un cas que j’ai déjà vu, le fichier contenant l’eval était “simple”, mais derrière, la charge décodée ne faisait que charger un autre script via un chemin construit depuis des fragments. Visuellement, on pouvait croire que “supprimer le fichier eval” suffisait. En pratique, le second script restait en place et finissait par réinjecter la partie décodage au prochain passage.

Ce que je vous conseille, c’est de considérer ces éléments comme un ensemble. Votre objectif n’est pas juste de retirer une ligne. Votre objectif est de supprimer toute la chaîne de déclenchement et le mécanisme de reconstitution, sinon vous aurez un retour.

Une démarche de suppression qui ne laisse pas de trous

Le point délicat avec les persistances eval/base64, c’est qu’elles peuvent s’activer à partir de l’endroit le plus inattendu. Par exemple, un fichier inclus depuis index.php ou depuis un chargeur interne peut déclencher la charge si un certain test est vrai.

Je vous propose une approche en quatre étapes, pensée pour minimiser les “suppression réussies mais temporaires” :

Identifier toutes les occurrences liées au pattern (fichiers et fragments). Déterminer l’ordre de chargement ou le point d’exécution réel (ce qui déclenche le décodage). Supprimer la chaîne complète en gardant une copie de l’état avant. Vérifier, après redémarrage, qu’il n’y a plus de déclenchement et que rien ne réécrit le contenu.

Sur le papier, ça ressemble à une liste. Dans le travail réel, c’est surtout une façon de vous forcer à comprendre, pas seulement à effacer.

Ce que j’entends par “chaîne complète”

Une “chaîne” peut inclure, sans s’y limiter :

    le fichier qui décodage la charge ; le fichier qui exécute ou délègue ; le fichier qui persiste (par exemple en recopiant son contenu dans un autre emplacement) ; la mécanique de déclenchement (hook, cron, inclusion, condition de requête).

Si vous en retirez un seul maillon, vous pouvez arrêter l’exécution, mais laisser en place la persistance qui “reproduit” le maillon sur un autre chemin. Résultat, vous observez parfois une amélioration immédiate, puis une réapparition.

Attention aux “mauvais nettoyages” : ce qui casse le site ou le réinfecte

Il existe deux types d’erreurs fréquentes :

    Casser WordPress. Par exemple, supprimer un fichier essentiel en croyant qu’il est suspect. Si vous supprimez trop agressivement des fichiers dans des emplacements critiques, vous pouvez vous retrouver avec un site cassé, ce qui vous empêche de savoir si l’infection est encore là. Laisser un mécanisme de réécriture. Parfois, le code malveillant se contente d’injecter sa charge à chaque requête, ou périodiquement. Si vous ne supprimez que le fichier “visible”, l’infection revient.

C’est pour cela que je recommande une séquence : copie, analyse du déclenchement, puis suppression ciblée, puis validation de non-déclenchement.

Exemple concret de logique de persistance (typique, sans magie)

Imaginons une situation où vous identifiez un fichier dans wp-content/plugins/nom-du-plugin/loader.php contenant :

    une chaîne base64 ; un décodage ; une exécution dynamique.

En lisant les alentours, vous remarquez que le code ne fait rien “au moment de l’inclusion”, mais vérifie une condition, par exemple un paramètre GET, une valeur dans un cookie, ou un segment d’URL. Puis il inclut un autre fichier, ou il écrit un fichier de plus.

Ce que vous voulez alors comprendre, c’est l’un des deux scénarios suivants :

    soit le code est seulement une charge initiale, et tout le reste est dans un fichier inclus ailleurs ; soit le code est un “injecteur” qui reconstruit une charge plus complète ailleurs sur le disque.

Dans le premier cas, supprimer le fichier de charge suffit généralement si vous supprimez aussi la charge incluse. Dans le second cas, il faut éliminer l’injecteur et ce qu’il écrit, sinon vous verrez un retour.

Validation : comment être sûr que vous avez vraiment coupé la persistance

Après nettoyage, la validation ne consiste pas à recharger la page une fois. Une persistance se valide mieux en observant trois signaux : absence d’exécution suspecte, absence de nouveau contenu écrit, et cohérence des fichiers.

Je fais généralement ceci, dans un ordre qui évite de se raconter des histoires :

    je vérifie que les fichiers identifiés comme “chaîne d’exécution” ne contiennent plus de code de décodage/exécution dynamique ; je compare les fichiers modifiés avec une version connue (par exemple un ensemble de fichiers de votre version WordPress, ou la version de vos plugins et thèmes telle qu’attendue) ; je regarde les logs serveur pour repérer des hits répétés vers des chemins anormaux ; je teste depuis plusieurs pages, pas seulement l’URL qui semblait problématique. Une condition de déclenchement peut dépendre du type de page.

Si vous avez un accès aux requêtes web, vous pouvez aussi traquer le déclenchement en repérant des patterns de requêtes qui reviennent avant la charge. Si ces requêtes disparaissent du fait de votre nettoyage, c’est un bon indice.

Si les requêtes continuent mais que le contenu ne change plus, c’est un autre type de victoire. Dans tous les cas, l’idée est de vérifier le comportement, pas seulement la présence de lignes suspectes.

Ce qu’il faut traiter autour du malware, pas uniquement le code

Supprimer l’infection dans les fichiers n’est pas la fin quand il existe une compromission plus large. Eval/base64 et persistance ne sont que la partie visible. Souvent, l’attaque s’est appuyée sur un accès déjà présent : mot de passe faible, plugin vulnérable, jetons d’admin volés, ou compte mal sécurisé.

Avant de remettre en ligne, j’insiste sur deux zones, parce qu’elles conditionnent la “re-prise” d’un incident :

    les comptes administrateurs et leurs sessions ; la liste des plugins et thèmes installés, y compris les fichiers “inactifs” mais présents.

WordPress peut aussi être affecté par des paramètres de configuration modifiés, ou des ajouts dans des fichiers qui ne sont pas forcément ceux que vous regardez en premier.

Plan d’action court pour un site en production

Voici une séquence que j’utilise quand je dois calmer le risque rapidement, sans transformer l’enquête en chantier improvisé. C’est volontairement court pour pouvoir être appliqué pendant que le site est encore vivant.

Passer en maintenance ou limiter le trafic externe, le temps d’identifier la chaîne d’exécution. Faire une copie complète des fichiers suspects et de la configuration avant toute suppression. Isoler, supprimer et remplacer les fichiers liés à la charge base64 et à l’exécution dynamique, y compris les scripts inclus ou réécrits. Vérifier les logs et les comptes admin, puis remettre progressivement en ligne (pages test d’abord, puis trafic complet).

Cette séquence a un avantage : elle évite de “nettoyer” puis de redémarrer trop tôt. La plupart des retours d’infection arrivent parce que le nettoyage est incomplet ou parce que la persistance n’était pas identifiée.

Distinguer l’infection “dans un plugin” et la compromission d’identifiants

Souvent, on trouve un plugin vérolé, puis on le supprime. Ça règle une partie du problème, mais il faut être lucide : si des identifiants compromis existent, l’attaquant peut réinstaller une variante. Dans ce cas, vous pouvez supprimer une persistance, et voir revenir un autre mécanisme dans les heures ou jours suivants.

C’est pourquoi je regarde toujours :

    les utilisateurs admin et leur historique récent (créations, changements de rôles) ; les plugins récemment installés, même si vous pensez ne pas les avoir faits ; les changements de fichiers que votre déploiement habituel ne produit pas.

Le jugement ici repose sur des signaux concrets. Si votre site n’a pas été modifié récemment côté équipe, l’apparition d’un plugin ou la modification de fichiers à des heures incohérentes est une alerte.

Durcir WordPress pour réduire les persistances futures

Quand vous êtes sorti de l’incident, la tentation est de simplement “reprendre comme avant”. En pratique, pour réduire la probabilité de re-hacking via des persistances eval/base64, il faut diminuer les possibilités d’accès et compliquer l’écriture de code.

Je reste prudent ici : WordPress a beaucoup de configurations possibles selon l’hébergement, et chaque mesure a ses effets de bord. Mais plusieurs actions reviennent sans surprise dans les retours d’expérience, parce qu’elles sont simples à vérifier.

    Mettre à jour WordPress, thèmes et plugins, surtout ceux qui exposent des surfaces d’attaque. Révoquer et sécuriser les sessions, puis forcer une authentification solide. Réduire les droits d’écriture là où c’est possible. Les attaques aiment écrire dans les mêmes répertoires où vous déployez. Surveiller les fichiers et les changements. Même un contrôle manuel régulier peut vous donner une alarme précoce. Vérifier la liste des webhooks, tâches planifiées, et intégrations externes si vous en avez. Une persistance peut s’appuyer sur des déclencheurs indirects.

Le point clé est de comprendre que les persistances aiment survivre à vos nettoyages. Votre meilleur garde-fou, c’est la combinaison entre réduction d’accès et détection rapide de modifications inattendues.

Quand faire une remise complète à neuf est plus rentable

Il y a un moment où “nettoyer fichier par fichier” devient une perte de temps. Ce moment arrive quand :

    vous trouvez trop de variantes de charges encodées sur trop d’emplacements ; vous perdez le fil de la chaîne d’exécution ; vous voyez des réécritures en boucle ; vous n’avez pas de version de référence claire pour comparer.

Dans ces cas, une remise à neuf contrôlée peut être plus sûre : reconstruire à partir d’une base connue, restaurer seulement le contenu légitime (médias, contenus, uploads propres), et réinstaller proprement plugins et thèmes. L’idée n’est pas de “tout effacer”, c’est de repartir avec des composants sûrs, puis d’intégrer uniquement ce qui vous pouvez prouver comme propre.

Je n’en fais pas une règle générale, car le risque principal de la remise à neuf est de supprimer aussi des éléments légitimes ou de réintroduire un plugin compromis au moment de la réinstallation. Mais si vous êtes déjà dans une logique de reconstruction, le coût mental de “chasser à l’aveugle” finit parfois par dépasser celui d’une restauration encadrée.

Checklist finale mentale pour ne pas rater la persistance

https://gardewp.fr/nettoyage-malware-wordpress/

Je ne vais pas faire une grande liste, mais je veux vous laisser un cadre de vérification qui tient sur un écran mental. La question à vous poser est simple : “est-ce que quelque chose peut réintroduire la charge après mon nettoyage ?”

Pour y répondre, gardez en tête ces trois axes, en continu :

Le fichier ou le mécanisme d’exécution a disparu ou a été neutralisé, y compris les scripts inclus après décodage. Le mécanisme de reconstitution n’existe plus, par exemple un injecteur, une tâche, un script de réécriture, ou un déclencheur discret. Les accès qui permettent de réinstaller ou de modifier le code ont été traités, car sinon vous nettoyez un symptôme, pas la cause.

Si ces trois conditions sont réunies, vous êtes généralement sorti du cycle.

Ce que je ferais dans votre situation, concrètement

Je ne connais pas votre environnement (hébergeur, version WordPress, plugins installés, méthode d’accès). Mais si vous me décriviez votre cas, j’aurais tendance à poser quatre questions pour aller droit au cœur :

    Où avez-vous trouvé la base64 et l’eval, dans quels fichiers exacts ? Le code déclenche-t-il une inclusion d’un autre fichier, ou une écriture sur disque ? Voyez-vous des fichiers qui reviennent après suppression ? Avez-vous identifié des changements côté utilisateurs ou plugins récents ?

Ces réponses déterminent la stratégie. Parfois, la persistance est une seule chaîne simple, et la suppression ciblée suffit. Parfois, c’est un chapelet, et il faut casser plusieurs maillons, plus une mesure de durcissement.

Si vous voulez, collez ici (en masquant les noms de domaine ou les secrets) un extrait du code suspect autour du base64 et de l’exécution, ou décrivez les chemins des fichiers touchés. Je pourrai vous aider à identifier la logique de déclenchement probable et à structurer le nettoyage pour éviter la réapparition de la persistance.