Désinfection WordPress : détecter les domaines et URLs injectés

Quand on parle de désinfection WordPress, beaucoup pensent d’abord à “supprimer le fichier infecté”. Dans la pratique, l’enquête commence presque toujours avant ça. Le vrai travail consiste à identifier ce que l’attaquant a voulu injecter, et surtout vers où: des domaines, des sous-domaines, parfois des URLs complètes, souvent cachées dans du JavaScript tordu, dans des chaînes encodées, ou noyées dans des paramètres de la base de données.

J’ai vu des cas où le site redirigeait vers un domaine différent selon la géolocalisation, d’autres où le script ne s’exécutait que sur certaines https://gardewp.fr/nettoyage-malware-wordpress/ pages, ou encore où les contenus injectés venaient par morceaux, par exemple un iFrame en fin de page, puis un script chargé après validation du navigateur. Le point commun, c’est que le domaine injecté finit toujours par se retrouver quelque part, même si vous devez creuser.

Le schéma classique de l’injection, et pourquoi les domaines se cachent

La plupart des malwares WordPress “modernes” ne cherchent pas à afficher une page complète à eux. Ils préfèrent s’accrocher à l’existant. Ils modifient un endroit où le code s’exécute systématiquement, puis ils récupèrent la suite depuis l’extérieur.

Typiquement, vous verrez une combinaison de signaux:

    Un fichier PHP modifié dans un thème ou un plugin, avec un petit bloc “d’activation” qui charge du code distant. Un base64 ou un encodage léger dans un champ, souvent dans une option WordPress. Des URLs externes injectées dans wp_options, dans des méta de contenus, ou via post_content sur des pages précises. Un cron ou un hook qui déclenche l’injection de manière progressive.

Les domaines injectés ne sont pas forcément visibles en clair dans les fichiers. Parfois ils le sont sous forme partielle, segmentée, ou reconstruite en concaténant plusieurs morceaux. Parfois, l’attaque commence avec un domaine “leurre” qui redirige ensuite vers la destination finale.

C’est pour ça que “faire une recherche de mots” en surface peut marcher, mais rate souvent le vrai lien. Il faut une méthode qui couvre plusieurs formes d’apparition des URLs.

Commencer par les symptômes, pas par les fichiers

Avant d’ouvrir un éditeur et de regarder “ce qui a été modifié”, je commence par documenter le comportement. Pas besoin de grand théâtre, mais une base minimale aide énormément à repérer la logique d’injection.

Deux exemples qui reviennent:

1) Le site charge une ressource externe seulement sur certaines pages. Vous tombez sur un script de tracking au lieu d’une redirection, et pourtant le domaine malveillant n’apparaît pas dans tous les fichiers. La logique peut être conditionnée sur l’URL demandée, ou sur des variables serveur.

2) La redirection dépend du user-agent. Dans ce cas, le domaine injecté peut être présent, mais sa construction dépend d’un test côté navigateur ou côté serveur. Si vous testez avec un navigateur “normal”, vous ne verrez pas tout.

Concrètement, notez ce que vous observez: page touchée, moment (tout de suite ou après quelques secondes), type d’action (iframe, script, redirection), et éventuellement le domaine que vous voyez dans l’onglet réseau de votre navigateur. Même une URL partielle relevée dans l’inspecteur réseau peut devenir votre meilleure piste.

Vérifier les vecteurs d’injection les plus fréquents dans WordPress

Les attaquants aiment les emplacements qui s’exécutent souvent, et qui sont faciles à modifier sans déclencher immédiatement des alertes.

Les cibles les plus communes sont liées à:

    L’exécution globale: fichiers chargés à chaque requête, comme index.php, ou des include dans le thème. L’exécution quasi globale: des fichiers inclus via functions.php, des hooks, ou des includes dans des modèles de thème. Le stockage dans la base: options, transients, metas, ou contenus.

Un détail qui m’a déjà fait gagner une demi-journée: quand vous voyez un bloc PHP “propre” mais entouré de conditions, ça ne veut pas dire que le malware est inoffensif. Souvent, le bloc conditionnel déclenche uniquement si une variable est vraie, par exemple une constante définie, un paramètre GET, ou une vérification de l’environnement.

Chercher les domaines et URLs dans les fichiers, sans se faire piéger

La recherche de texte reste la première étape, mais il faut la faire correctement. Le piège, ce sont les variations: minuscules, majuscules, espaces, encodage, concaténation, et surtout l’absence de “http” dans la chaîne.

Je procède en deux passes.

D’abord, une recherche classique sur les fichiers PHP et éventuellement JS, CSS (oui, on a déjà vu des injections qui démarrent en CSS via url()), en ciblant des motifs d’URLs. Ensuite, je refais une recherche sur des motifs indirects: fragments de domaines, segments TLD, ou des patterns “iframe”, “script”, “document.location”, “location.href”.

Le but n’est pas de trouver “tout” d’un coup, mais de réduire le périmètre.

Un domaine peut apparaître sous forme de chaîne complète, ou recomposé comme ceci: "exemple" . "." . "com" puis concaténé avec "/path". Dans ce cas, un grep sur http échoue, mais une recherche sur com/ ou sur iframe peut remonter le bloc.

Analyse réseau: l’URL “réelle” n’est pas toujours celle que le malware annonce

Quand le site charge une ressource malveillante, le navigateur a souvent la vérité. Mais il y a des nuances.

    Si le malware utilise un mécanisme de redirection, vous verrez peut-être une suite de 301, 302, ou des chargements successifs. Si la ressource est injectée après un délai, vous devez surveiller le réseau pendant quelques secondes, pas uniquement au chargement initial. Si l’attaque dépend du pays, testez depuis un environnement stable et idéalement plusieurs IP. Je sais que ce n’est pas toujours faisable, mais même une contrainte “c’est visible chez moi et pas chez l’autre” oriente les hypothèses.

Dans les cas où la redirection finale passe par un domaine intermédiaire, relever les deux domaines, celui de l’étape 1 et celui de l’étape 2, change la désinfection. Nettoyer uniquement un redirect visible peut laisser un mécanisme de reconstruction actif qui rechargera une autre URL.

Base de données: où les domaines injectés laissent le plus de traces

En WordPress, une grande partie des “modifications” d’un hack ne touche pas le fichier du thème. Elle touche la base.

Les endroits où j’ai le plus souvent trouvé des domaines injectés:

    wp_options et variantes: options de plugin, options SEO, paramètres de configuration, et parfois options “fourre-tout” écrites par le malware. post_content: pages ou articles modifiés, souvent avec un bloc HTML et un script externe. postmeta: métadonnées qui alimentent un modèle de thème compromis. Transients et cron: objets temporaires qui déclenchent la suite sans être visibles directement dans le contenu.

Un exemple typique: vous remettez un fichier functions.php en état propre, mais le contenu continue d’ajouter un script externe. En inspectant la base, vous trouvez une option qui contient une chaîne encodée (base64) qui reconstruit ensuite l’URL. Sans ça, vous “désinfectez” en surface mais le site revient.

image

Méthodes de recherche avancées dans la base (sans perdre de temps)

La base est souvent volumineuse. Chercher “mot à mot” en production peut être lent. Je privilégie une démarche qui limite les requêtes.

D’abord, j’identifie les tables concernées. Si le site est petit, vous pouvez vous permettre des recherches plus larges. Si vous avez plusieurs centaines de pages, commencez par:

    wp_options (et préfixe réel si différent) wp_posts wp_postmeta

Ensuite, je cherche des motifs d’URLs plutôt que des domaines exacts. Parce que les domaines peuvent être variables, ou reconstruits. Les motifs utiles sont généralement http, https, //, iframe, script, document.location, window.location, eval(, base64_decode, atob(, et dans certains cas des événements HTML comme onerror=.

Attention aux faux positifs: beaucoup de sites légitimes contiennent des scripts d’analytics, et WordPress enregistre parfois des contenus qui ressemblent à des injections. La différence se fait souvent sur https://gardewp.fr/ la structure: un bloc obfusqué ou concaténé, des conditions bizarres, ou une présence d’URLs inhabituelles dans un champ qui n’est normalement pas là.

Détecter la reconstruction des URLs: quand “le domaine n’apparaît pas”

Il arrive fréquemment que vous ne voyiez jamais le domaine en clair dans les fichiers, ou que vous le trouviez seulement sous forme de fragments. L’attaquant peut:

    utiliser des concaténations de chaînes, passer par un encodage (base64 ou variante), chiffrer très légèrement puis décoder à l’exécution, stocker le domaine dans une option, puis construire l’URL finale dans un autre endroit.

Dans ces cas, l’approche la plus efficace n’est pas d’essayer de “deviner” le domaine dès le début, mais de remonter l’exécution.

Quand un fichier PHP compromis exécute une fonction qui décode une chaîne, vous pouvez extraire la chaîne décodée (dans un environnement contrôlé) ou au minimum identifier des fragments, puis retrouver la logique dans la base.

Si vous avez la possibilité de lancer une analyse statique, c’est encore mieux. Sinon, une lecture patiente du code compromis suffit souvent à localiser les fonctions de décodage et les morceaux concaténés.

Check rapide: ce que je fais en premier pour trouver les domaines et URLs injectés

Je vous laisse une courte séquence qui marche bien dans la majorité des cas. Elle n’est pas “magique”, mais elle évite de se disperser.

1) Contrôler le comportement concret (page, redirection, ressource chargée) et relever une URL visible dans l’inspecteur réseau.

2) Rechercher dans les fichiers PHP les motifs base64_decode, gzinflate, str_rot13, eval(, document.location, iframe, script src= et http même si c’est incomplet. 3) Refaire une recherche dans la base sur http, https, //, iframe, et sur les fonctions de décodage quand elles sont stockées en clair. 4) Vérifier wp-cron et les hooks côté thème et plugins, surtout si le script ne s’exécute pas à chaque chargement. 5) Comparer les horaires et la fréquence des anomalies avec les tâches planifiées et l’historique de déploiement.

C’est volontairement “large”. Une fois que vous avez un ou deux domaines repères, vous réduisez le périmètre et vous confirmez dans les fichiers et la base où ils sont utilisés.

Où regarder dans les thèmes et plugins quand vous suspectez une injection “en cascade”

Quand je vois une injection qui ajoute des scripts sur plusieurs pages, je cherche d’abord un point d’entrée dans le thème. Les fonctions qui pivotent sont souvent dans functions.php, des includes de modèles, ou un fichier chargé conditionnellement.

Quelques signaux qui poussent à ouvrir un fichier en priorité:

    Présence d’une condition sur $_SERVER['REQUEST_URI'], $_GET, ou sur des en-têtes comme HTTP_USER_AGENT. Une boucle qui insère du HTML dans le contenu WordPress, ou une modification de the_content. Un require dynamique, ou un include basé sur une valeur modifiée.

Même si vous remettez des fichiers “visuellement” propres, vérifiez aussi ce qui reste dans la base. Certains malwares se comportent comme un mécanisme, pas comme un fichier unique. La base peut rester “infectée” même après restauration partielle.

Traitement des domaines: stratégie de neutralisation sans casser le site

Une fois les domaines et URLs identifiés, la question devient: que faire avec eux pendant la désinfection?

Vous avez deux objectifs qui se concurrencent:

    Éliminer l’exécution du script ou la redirection. Ne pas casser le site pour autant, surtout si l’URL identifiée est utilisée par un plugin légitime dans un autre contexte.

La neutralisation la plus sûre consiste à supprimer le code malveillant à la source, et à supprimer les entrées de base qui déclenchent la reconstruction. Si vous devez faire une mesure temporaire, par exemple un blocage réseau au niveau serveur, faites-le avec prudence. Un blocage par domaine peut bloquer d’autres ressources si le domaine est aussi utilisé par un outil légitime.

Dans l’enquête, je recommande d’étiqueter chaque domaine trouvé par “type d’usage”:

    script externe injecté dans une page, iframe, redirect final, endpoint utilisé pour récupérer une seconde charge, domaine de test qui redirige ensuite.

Cette classification évite de traiter “tout le monde” comme malveillant au même niveau.

Cas limites que j’ai rencontrés: faux positifs et pièges de “nettoyage”

Il y a deux types de problèmes qu’on sous-estime.

1) L’hébergement de scripts pour des intégrations légitimes

Un site peut intégrer un script tiers qui ressemble à un comportement d’injection, surtout si les URLs sont en clair dans des options ou dans des pages. Le spam de désinfection arrive quand on supprime trop vite. Si vous ne reconnaissez pas le code, prenez une minute pour regarder la logique autour: qui a écrit la donnée, et pourquoi c’est stocké à cet endroit.

2) La persistance via des données “propres” mais interprétées comme malveillantes

Il arrive que la chaîne injectée soit stockée de manière “inoffensive” et n’exécute rien par elle-même. Le déclencheur est ailleurs. Si vous supprimez uniquement ce que vous voyez, l’attaque peut continuer avec une autre chaîne, ou via une reconstruction.

Dans ce genre de cas, le mieux est de suivre l’exécution jusqu’au point qui injecte dans la page, et seulement ensuite nettoyer les données nécessaires.

Exemple d’enquête réaliste: retrouver l’URL finale en suivant les fragments

Sans raconter un cas exact de client (les détails changent trop), je peux décrire un scénario fréquent.

On observe des redirections depuis une page de blog vers une URL externe. Dans les fichiers du thème, la recherche de http ne donne rien. En revanche, on trouve un bloc PHP avec une concaténation de morceaux, et une fonction de décodage légère. Dans ce bloc, on voit des fragments, par exemple un TLD isolé et un chemin reconstruit.

Ensuite, dans wp_options, on repère une option qui contient une longue chaîne encodée. Quand on localise le code qui l’utilise, on comprend que l’option n’est qu’une “matière première”. Elle ne contient pas forcément l’URL finale en clair.

Résultat: on finit par identifier deux domaines. Le premier sert à appeler un endpoint, le second reçoit ensuite le navigateur via redirection. Si vous ne bloquez que le second, l’appel initial peut encore déclencher une autre redirection.

C’est typiquement là que la détection “domaines et URLs injectés” devient un travail d’enquête, pas une chasse au mot.

Quand la désinfection ne suffit pas: sécuriser le point d’entrée

Détecter les domaines et URLs injectés est essentiel, mais ça ne règle pas la cause. La désinfection échoue souvent quand le compte qui a permis l’accès n’est pas traité, ou quand un plugin de type “vulnérabilité connue” reste en place.

Après avoir identifié les domaines, je vérifie aussi:

image

    les comptes admin et leur historique d’accès, la présence de nouveaux rôles, les sessions actives inhabituelles, les plugins et thèmes ajoutés récemment, la persistance via des fichiers nouveaux dans des répertoires non attendus.

Je sais que ce n’est pas le cœur de la question, mais sans ça, vous pouvez passer deux heures à retrouver des URLs, puis vous retrouver avec le même problème quelques jours plus tard.

Ce que j’attends d’un rapport d’incident utile (et pas seulement “c’est nettoyé”)

Après une désinfection, j’aime avoir une trace claire des domaines et URLs injectés, même si tout n’est pas public. Ça sert pour deux choses: vérifier qu’on a éradiqué la persistance, et accélérer une prochaine investigation.

Un rapport vraiment utile précise souvent:

    où le domaine apparaît (fichier, table, champ), la méthode d’injection (script, iframe, redirection), le mécanisme de reconstruction (concaténation, base64, condition), et ce qui a été supprimé ou corrigé pour empêcher la réactivation.

Si on ne fait que “remettre à zéro” sans expliquer comment la charge se déclenchait, vous gardez une zone grise. Et les zones grises, en sécurité, reviennent toujours.

Méthode pratique pour confirmer que les domaines sont bien neutralisés

Une fois la désinfection effectuée, la validation ne doit pas être uniquement “ça marche”. Je fais une vérification qui cible le comportement qui posait problème.

Le bon réflexe est de tester la même page et de surveiller ce qui se charge côté navigateur, ou ce qui se déclenche côté serveur si vous avez des logs. Si le domaine injecté ne doit plus être appelé, il ne doit plus apparaître dans le réseau. Et si une redirection était déclenchée, elle ne doit plus se produire.

Quand il reste une trace, ce n’est pas toujours parce que vous avez raté “un fichier”. Parfois, c’est une option de base qui reconstruit une autre URL. D’où l’intérêt d’avoir listé les domaines, pas seulement un seul.

Pour résumer la logique d’enquête sur les domaines et URLs injectés

La désinfection WordPress orientée “détection des domaines et URLs injectés” repose sur un principe simple: remonter du comportement vers les points d’exécution, puis suivre la trace jusqu’aux domaines, même s’ils ne sont pas en clair.

Quand vous adoptez cette logique, vous gagnez en précision. Vous nettoyez la cause, pas seulement la conséquence visible. Et surtout, vous évitez le piège classique: traiter un redirect final sans supprimer la reconstruction qui relancera le même mécanisme plus tard.

Si vous avez des éléments concrets (un domaine observé, une page touchée, un extrait de code ou une ligne d’URL vue dans le réseau), je peux aussi vous aider à déterminer où chercher en priorité, fichiers versus base, et quels motifs d’injection sont les plus probables dans votre cas.