Nettoyage fichiers infectés WordPress : repérer le code malveillant dans les thèmes

Quand un site WordPress commence à “se comporter bizarrement”, le réflexe consiste souvent à chercher côté plugins. Pourtant, dans la pratique, les thèmes sont une cible très fréquente, parce qu’ils restent visibles, modifiables et parfois peu surveillés sur le long terme. Un thème infecté peut injecter du code au moment du rendu d’une page, détourner des formulaires, rediriger vers des domaines frauduleux, ou encore transformer des appels AJAX en portes d’entrée discrètes. Et tout cela peut se produire sans qu’aucun bouton “hacké” n’apparaisse dans le tableau de bord.

Le nettoyage fichiers infectés WordPress prend donc une dimension “détective”. On ne se contente pas de supprimer un morceau de thème, on cherche la logique malveillante, ses déclencheurs, et surtout l’endroit exact où il s’exécute. L’objectif, c’est de comprendre assez pour ne pas casser le site et assez pour ne pas laisser une bombe à retardement.

Pourquoi les thèmes sont un terrain de jeu pour les attaquants

Un thème WordPress, ce n’est pas seulement une vitrine graphique. C’est un ensemble de fichiers PHP qui s’exécutent dans le contexte du serveur, avec accès à des variables, à la session utilisateur, aux options, et souvent aux mécanismes d’URL, de hooks et de templates. À la différence d’un plugin, un thème est parfois géré en “mode bricolage” dans les environnements plus anciens, avec des fichiers édités directement sur l’hébergement.

image

J’ai déjà vu des cas où un thème “propre” avait été modifié il y a des mois. Sur le moment, l’ajout était présenté comme une amélioration “mineure”. Le code malveillant était minuscule, 20 lignes maximum, mais parfaitement placé pour s’activer seulement sur certaines conditions: un agent utilisateur spécifique, une plage d’IP, une route précise, ou une heure de la journée. Résultat: le site semblait normal la plupart du temps, jusqu’au jour où Google a commencé à indexer des pages fantômes.

Les infections de thème jouent souvent sur trois leviers:

    modifier un fichier très tôt dans l’exécution (comme functions.php ou header.php) pour injecter de l’HTML ou du JavaScript; utiliser des fonctions PHP qui déguisent l’intention (base64, gzdecode, eval, str_rot13, fonctions de lecture de fichiers); déclencher seulement dans certains scénarios, ce qui réduit la probabilité d’attraper le problème pendant un contrôle rapide.

Repérer le code malveillant: l’approche qui évite les erreurs

Il y a un piège classique: quand on voit du code suspect, on a tendance à tout supprimer à la hache. Sur WordPress, ça casse vite la compatibilité avec le thème, les champs personnalisés, ou des mécanismes de performance (minification, scripts de suivi). L’idée est plutôt de repérer, qualifier, puis neutraliser proprement.

Commencez par poser une question simple: le thème “devrait-il” contenir ça? Un thème standard n’a pas de raison d’appeler eval, de tenter de télécharger du contenu depuis un domaine externe, ou d’écrire des fichiers sur le serveur. Même si certaines optimisations avancées peuvent utiliser des fonctions complexes, la combinaison “fonctions dangereuses + logique d’injection dans le rendu” est très rarement légitime.

Ensuite, regardez le contexte. Une charge utile malveillante a souvent besoin d’un point d’entrée stable. Sur WordPress, les points d’entrée courants sont:

    functions.php (chargé très tôt, riche en hooks); header.php et footer.php (pour injecter du code dans la page); index.php, page.php ou des templates spécifiques (pour cibler seulement certaines pages); parfois un fichier moins “évident”, comme inc/*.php, si l’attaquant a créé une structure ad hoc.

Enfin, travaillez avec des comparaisons. Si vous avez un thème officiel ou une version déployée récemment, comparez. Si vous n’avez pas de copie saine, au minimum comparez les fichiers modifiés avec une vue d’ensemble sur leur contenu et leur date. Le bon réflexe, c’est de chercher la différence, pas seulement ressources supplémentaires la “saleté”.

Les signaux d’alerte les plus courants dans un thème

Avant même de toucher aux fichiers, vous pouvez repérer des indices. Certains sont visibles dans les logs, d’autres dans le code. L’important est de ne pas confondre un faux positif (un thème qui utilise des transformations) avec une charge malveillante (une transformation au service de l’injection et de l’exécution).

Voici les signes les plus fréquents que j’ai rencontrés dans des nettoyages fichiers infectés WordPress liés aux thèmes.

    Présence de fonctions comme eval, assert, base64_decode combiné à une évaluation, ou encore gzinflate/gzdecode suivies d’une exécution. Insertion de JavaScript obfusqué dans header.php ou footer.php, souvent via un bloc PHP <?php ... ?> qui écrit du directement. Apparition de chaînes de type “loader”, “curl”, “http”, “download”, ou des URL externes dans des fichiers de thème. Modification de fichiers inattendue, par exemple functions.php avec des hooks qui ne ressemblent pas au thème d’origine. Le code s’exécute seulement dans certains cas, par exemple présence de conditions sur l’IP, le user agent, une URL particulière, ou un compteur.

Cette liste n’est pas une preuve en soi. Elle sert de filtre. Dans un dossier de thème, je préfère repérer deux ou trois signaux concordants au lieu de poursuivre un seul indice isolé.

Où chercher concrètement dans functions.php

Le fichier functions.php est souvent l’endroit où l’attaquant met son “cerveau”. Même quand l’injection finale se fait dans header.php, functions.php sert à:

1) définir des fonctions “utilitaires” obfusquées; 2) accrocher des hooks WordPress (add_action, add_filter) pour exécuter du code sur certains événements; 3) préparer des variables, puis appeler une écriture dans le rendu.

Un thème infecté peut ajouter des hooks sur des événements classiques, par exemple wp_head, wp_footer, ou des filtres sur le contenu. Si vous voyez un add_action('wp_head', ...) ou un add_filter('the_content', ...) qui ne correspond à aucune fonctionnalité attendue, c’est un point d’enquête immédiat.

Autre repère utile: les patterns d’obfuscation. Les attaquants utilisent souvent une séquence de décodage, puis une exécution. Le code peut ressembler à:

    un bloc avec base64_decode puis concaténation; une boucle qui transforme une chaîne caractère par caractère; un tableau de fragments, puis une concaténation avant envoi vers eval.

Même si le détail exact varie, l’objectif est le même: masquer l’intention au premier coup d’œil. Si vous tombez sur un bloc énorme alors que le fichier n’avait pas cette taille avant, c’est un signal fort. La taille seule ne suffit pas, mais combinée à des fonctions suspectes, elle devient parlante.

Injection dans header.php et footer.php: les déclencheurs discrets

L’injection de code dans les fichiers de template est souvent plus “visible” en sortie, mais le mécanisme d’entrée peut rester discret. On a parfois un simple appel à une fonction créée dans functions.php, qui écrit ensuite un script.

Dans un nettoyage fichiers infectés WordPress, j’ai tendance à ouvrir header.php en premier, puis footer.php. Parce que ce sont des fichiers dont le contenu influence immédiatement la page. Même si le code malveillant est obfusqué, on repère souvent la trace sous forme d’un echo de balises, ou d’un tableau script construit au moment du rendu.

Si le site injecte un script, la question suivante est: est-ce un script qui charge un fichier distant, ou un script “autonome” qui fait tout directement? Dans les cas lourds, le script distant pointe vers un domaine non lié au site, avec des paramètres qui identifient la victime (par exemple referrer, cookies, identifiants). Dans les cas plus “propres” (plus rares), l’attaquant fournit un code local, mais cela reste atypique pour un thème.

Pensez aussi à l’endroit exact de l’injection. Un script malveillant juste avant la fermeture de ou juste avant peut être ajouté à la volée, en fonction de l’URL consultée. Ce ciblage explique pourquoi certains visiteurs voient le problème, et d’autres non.

Obfuscation et logique d’activation: comment lire ce que vous ne comprenez pas encore

Dans un incident réel, on ne lit pas le code comme un roman. On le lit comme une cartographie. L’objectif est de répondre à quelques questions avant même de tenter de “décoder”:

    le code est-il exécuté à chaque chargement, ou seulement sur certaines pages? l’exécution dépend-elle d’une condition (URL, IP, user agent, heure, variable de session)? le code déclenche-t-il une requête réseau (curl, file getcontents sur URL, sockets)? le code modifie-t-il des fichiers (fopen, fwrite, unlink) ou écrit-il dans la base (requêtes SQL)?

Quand vous voyez des constructions conditionnelles complexes, ne vous perdez pas dans les détails d’un premier niveau. Cherchez d’abord les “points d’action”. Les zones qui ressemblent à une mise en place de hooks ou à une écriture de contenu sont en général les meilleures pistes.

Ensuite, seulement si nécessaire, vous pouvez analyser plus finement l’obfuscation. Par exemple, si vous voyez une chaîne base64 passée à une fonction d’évaluation, l’étape de décodage peut vous montrer l’intention réelle. Mais attention, sur un serveur de production, vous n’avez pas forcément le droit d’exécuter du code suspect, même en local. L’idéal est d’utiliser une copie du code, de reproduire l’analyse hors serveur, ou au minimum de faire un traitement statique.

Une méthode simple pour qualifier l’infection sans s’aveugler

Dans les procédures d’intervention, je privilégie une méthode “en trois temps” parce qu’elle limite les décisions irréversibles.

D’abord, identifiez les fichiers modifiés et comparez avec une version saine. Si vous utilisez un thème acheté ou un thème installé via dépôt, cherchez une version connue. Sinon, récupérez une version saine depuis un autre environnement (staging) si vous en avez un. Même si le site n’est pas identique, la base de comparaison reste utile.

Ensuite, lisez uniquement les zones “activables”. Dans un fichier PHP, ce sont les zones qui s’exécutent à l’inclusion du fichier (donc hors de fonctions conditionnelles), et les zones liées à des hooks. Tout ce qui est défini mais jamais déclenché peut être moins urgent.

Enfin, décidez du niveau d’intervention:

    suppression directe d’une portion clairement malveillante; remplacement complet du thème par une version saine; ou, pour un site complexe, remplacement partiel plus ciblé.

Ce choix dépend de votre capacité à restaurer un état propre. Dans un environnement où vous ne pouvez pas reconstruire facilement, remplacez entièrement le thème par une version saine, puis réintroduisez les personnalisations une par une. Oui, c’est plus long. Mais c’est souvent moins risqué que d’essayer de “réparer” un fichier déjà contaminé.

Checklist d’investigation (sans faire n’importe quoi)

Quand vous ouvrez un thème pour “voir ce qu’il y a dedans”, le risque est d’ajouter d’autres problèmes. Vous voulez rester en contrôle.

    Faites une copie du dossier du thème avant toute modification. Relevez les heures de modification des fichiers concernés, sans croire seulement à la date si votre système a été modifié après l’incident. Cherchez en priorité dans functions.php, header.php, footer.php, et les fichiers inc proches. Repérez les blocs avec décodage ou exécution (base64, eval, assert) et les hooks qui écrivent dans la page. Vérifiez si le comportement du site change selon l’URL, en testant quelques pages qui posent ou non problème.

Cette checklist n’est pas une marche à suivre “automatique”, mais elle aide à garder une ligne de conduite.

Exemple de scénario typique sur un thème infecté

Je vous décris un scénario fréquent, parce que c’est souvent là que les gens se trompent. Imaginez un site qui affiche normalement le contenu, mais dont les pages “produit” commencent à contenir un script supplémentaire. Le script ne charge pas forcément immédiatement une redirection. Parfois il exécute une requête pour récupérer une instruction distante, puis il remplace discrètement du contenu.

Sur le serveur, en ouvrant header.php, vous trouvez un bloc PHP placé en bas du fichier. Le bloc écrit un script. Le script est obfusqué, mais vous remarquez une variable construite depuis une chaîne décodée. Dans functions.php, vous repérez un add_action('wp_head', 'nom_de_fonction') qui n’était pas là auparavant. Et dans cette fonction, on voit base64_decode suivi d’un echo de contenu. La condition qui suit limite l’injection à une liste d’URL ou à un motif dans $_SERVER['REQUEST_URI'].

À ce stade, le nettoyage fichiers infectés WordPress ne consiste pas seulement à enlever le script visible. Si vous supprimez uniquement le

Autre détail utile: le code peut être “petit” mais s’exécute souvent. Les attaquants misent sur la répétition parce que les caches et les crawlers multiplient les chances d’exécution.

Entre sécurité et continuité: comment traiter les personnalisations du thème

Un thème de production a rarement une installation brute. Les clients ont des personnalisations: couleurs, templates enfants, overrides, champs additionnels. La tentation est forte de “réparer” uniquement les lignes suspectes. Pourtant, quand le thème a été compromis, je recommande souvent une restauration plus large.

Le compromis se joue sur deux axes:

    si vous avez un thème enfant, il peut contenir une partie des modifications attendues. Dans ce cas, vous pouvez restaurer le thème parent, puis vérifier le thème enfant; si vous n’avez qu’un thème parent modifié “en direct”, le remplacement complet peut effacer des réglages. Il faut alors reconstituer ces réglages, au besoin en les appliquant via un mode plus maîtrisé (child theme, Customizer, ou options).

D’expérience, les sites les plus fragiles sont ceux où le thème a été modifié à la main sans documentation. Quand vous remplacez, vous perdez ce que vous ne connaissez pas. C’est là que les comparaisons avec une version saine et la sauvegarde avant intervention prennent tout leur sens.

Remplacement complet du thème: quand c’est le meilleur choix

Le remplacement complet d’un thème par une version saine est parfois la meilleure solution parce que:

    vous supprimez d’un coup les blocs cachés dans des templates ou des fichiers inclus; vous réduisez la surface d’erreur, au lieu de “jouer” au chirurgien sur un code obfusqué; vous repassez sur une base cohérente avec le reste de WordPress.

Mais il faut anticiper les impacts. Si vous utilisez des fichiers personnalisés dans le thème, il faudra les réinstaller. Si des templates sont spécifiques, vous devrez vérifier leur compatibilité avec la version saine. Et si le thème est lui-même un thème enfant ou dérivé, vous devrez restaurer la bonne couche.

Dans un incident, j’ai déjà vu des équipes supprimer uniquement des bouts, puis découvrir plus tard qu’un autre fichier caché s’était greffé au même thème, quelques templates plus loin. Remplacer peut sembler brutal, mais c’est souvent plus “calme” que courir après les pièces manquantes.

Plan de remédiation raisonnable (et limité)

Quand vous avez identifié le thème suspect, il faut agir sans provoquer une nouvelle panne.

    Remplacez le thème par une version saine (idéalement la version exacte correspondant à votre site), puis réappliquez uniquement les personnalisations nécessaires. Si vous préférez corriger à la ligne près, supprimez toutes les zones qui créent des hooks ou injectent du contenu, pas seulement la sortie visible. Désactivez temporairement le thème sur un environnement de test si possible, afin de valider le comportement et les menus. Vérifiez aussi l’existence de fichiers inconnus dans le dossier du thème, ou de nouveaux inclus dans inc/. Après restauration, testez quelques URLs qui déclenchent généralement l’injection, et contrôlez le HTML rendu dans le navigateur.

Cette liste est courte, volontairement. Le reste du travail consiste surtout à analyser, valider et éviter de réintroduire une logique contaminée.

Après nettoyage: prévenir la réinfection liée aux thèmes

Un nettoyage fichiers infectés WordPress ne termine pas l’incident. Le plus dur commence après, parce que la question devient: pourquoi l’attaquant a pu modifier le thème?

Les causes fréquentes ne sont pas toujours techniques. Parfois c’est un mot de passe faible, un compte administrateur réutilisé ailleurs. Parfois c’est une mauvaise configuration de droits d’écriture sur le serveur. Parfois ce sont des identifiants FTP ou une clé SSH exposée.

Pour les thèmes, un point revient souvent: les autorisations sur les dossiers. Si votre serveur autorise un processus ou un utilisateur à écrire dans wp-content/themes, une compromission peut se traduire par un retour rapide de code malveillant.

Dans les jours qui suivent l’incident, je recommande d’aller au-delà du “thème nettoyé”:

    surveiller les changements sur le dossier du thème (heure et nature); vérifier l’intégrité des fichiers WordPress et du contenu thème; contrôler les comptes utilisateurs et leurs rôles; revoir les plugins et les thèmes installés, même ceux qui ne paraissent pas suspects.

Le but est de casser le chemin d’accès. Le code malveillant est un symptôme. L’accès initial est la vraie faille.

Cas particuliers: thèmes légitimes mais modifiés

Tous les thèmes “avec du code bizarre” ne sont pas infectés. Certains thèmes intègrent des libs pour des performances, des formats de cache, ou des composants de tracking. Le problème est que les attaquants utilisent aussi ces techniques pour se fondre dans le paysage.

Le bon discernement vient de la combinaison entre l’intention et le placement. Un thème légitime n’a pas besoin d’invoquer du code exécutable depuis une chaîne obfusquée. Il peut charger des scripts via wp_enqueue_script, mais il n’a généralement pas besoin d’un eval ou d’une exécution dynamique sur du contenu réseau.

Si vous trouvez une transformation, demandez-vous quel est le résultat final. Le code transforme-t-il une chaîne puis l’insère dans la page de manière cohérente et prévue? Ou bien transforme-t-il une charge pour ensuite l’exécuter ou la rendre sous une forme qui n’a aucun rapport avec la fonctionnalité du thème?

Ce tri évite des “chirurgies” inutiles qui dégradent le rendu.

Comment documenter ce que vous trouvez, pour gagner du temps la prochaine fois

Sur un incident, le travail de documentation semble secondaire. Pourtant, j’ai vu des équipes perdre des heures parce qu’aucune note n’avait été tenue sur les fichiers touchés et sur les mécanismes d’injection.

Après un nettoyage, notez au minimum:

    les fichiers exacts modifiés dans le thème; les fonctions ou hooks impliqués; les déclencheurs (URL, conditions); ce que vous avez restauré et comment.

Cette documentation sert lors d’une réinfection éventuelle. Et elle sert aussi si vous devez expliquer l’incident à un client, de façon factuelle, sans drama, et avec des actions concrètes.

Repérer le mal avant qu’il ne se propage

On pourrait résumer l’enjeu ainsi: un thème infecté, ce n’est pas seulement “du code en plus”. C’est souvent une logique d’exécution discrète, branchée sur des hooks et capable d’agir selon des conditions. Le repérage efficace passe par la lecture orientée exécution, la comparaison avec une version saine, et une restauration qui élimine réellement la surface d’attaque.

Si vous ne voulez retenir qu’une idée, c’est celle-ci: ne confondez pas l’endroit où le script apparaît avec l’endroit où l’infection commence. Sur beaucoup d’installations, la sortie visible est un symptôme, la vraie entrée est dans functions.php ou dans un fichier inclus. En nettoyant correctement, vous protégez le site, vous gardez la main sur le rendu, et vous évitez de reconstruire une instabilité qui revient dès le lendemain.

Et si vous êtes en plein milieu d’un incident, avec un doute sur un thème précis, le meilleur point de départ reste toujours le même: sauvegarde, comparaison, lecture des hooks, puis restauration cohérente. C’est moins spectaculaire que “supprimer au hasard”. C’est juste beaucoup plus fiable.