Site WordPress infecté : audits de la chaîne d’approvisionnement des plugins

Quand on parle de site WordPress infecté, on pense souvent au moment où l’attaque devient visible: un trafic qui explose, des pages qui changent de langue, des redirections bizarres, des comptes administrateurs qui n’ont pas été créés par vous. Pourtant, dans beaucoup de cas, le vrai problème arrive plus tôt, bien avant que quelqu’un ne clique sur un lien frauduleux. Il arrive quand un logiciel que vous installez, met à jour, ou met en cache fait entrer une compromission dans votre environnement.

Depuis quelques années, les audits de sécurité “classiques” (mises à jour, durcissement, règles de pare-feu, scans de fichiers) ne suffisent plus seuls. La surface d’attaque s’est déplacée vers la chaîne d’approvisionnement: développeurs, dépôts, plugins, thèmes, gestionnaires de dépendances, CDN, et même les mécanismes de mise à jour. Un plugin peut être légitime, puis être compromis, ou être “légitime” en apparence mais compromis dès la publication. La difficulté, c’est qu’une infection WordPress n’a pas besoin d’exploiter une faille de votre thème. Elle peut simplement s’exécuter en tant que code “normal” au prochain chargement de votre page.

Un audit sérieux de la chaîne d’approvisionnement des plugins vise un objectif simple: comprendre comment votre site récupère, exécute et dépend de code tiers, puis décider ce que vous acceptez et ce que vous n’acceptez plus.

Pourquoi la chaîne d’approvisionnement est devenue le point névralgique

WordPress a un modèle d’écosystème qui facilite la fonctionnalité, mais aussi la dépendance. Les plugins ne sont pas juste des “ajouts”. Ils touchent le routage, les formulaires, la base de données, les appels HTTP sortants, les tâches planifiées (cron), le rendu du front, parfois même l’authentification.

Quand un plugin se comporte normalement, vous le voyez à peine. Un mois plus tard, vous pouvez constater des symptômes qui ne correspondent à aucun changement de thème ou de configuration côté serveur. Les logs indiquent parfois des actions déclenchées par des requêtes apparemment anodines, mais l’origine se trouve ailleurs: un fichier ajouté, un mécanisme de persistance, un script qui ne s’exécute que sous certaines conditions, ou un comportement déclenché après une date précise.

Sur le terrain, la chaîne d’approvisionnement joue aussi sur le rythme. Un plugin peut être mis à jour sans que l’administrateur surveille chaque version. Les sites sous WordPress reçoivent des mises à jour automatiques, ou des installations faites “à la va vite” parce que le client a besoin d’un module précis (SEO, formulaires, e-commerce, gestion d’affiliation). À ce moment-là, le risque est diffus: il n’est pas “une vulnérabilité” que vous pouvez patcher tout de suite, c’est une confiance opérationnelle.

Et cette confiance, elle se décide.

image

Les symptômes qui font soupçonner un plugin compromis (pas seulement un site vulnérable)

Avant de parler méthode d’audit, je préfère ancrer la question dans ce qu’on observe vraiment. Sur un site WordPress infecté, l’attaque ne ressemble pas toujours à une “grosse” defacement. Souvent, c’est plus subtil, parce que le code malveillant cherche à rester discret.

On repère fréquemment des motifs récurrents:

    des redirections vers des domaines nouveaux, avec une logique dépendant de l’agent utilisateur, de la géolocalisation ou de l’heure; des pages qui semblent légitimes, mais dont une partie est modifiée au rendu, via injection côté PHP ou via scripts côté navigateur; des comptes utilisateurs qui apparaissent, parfois sans invitation claire, avec des rôles qui escaladent rapidement; une charge anormale du serveur à des heures particulières, liée à des requêtes sortantes ou à des tâches cron; des “traces” dans des fichiers qui n’ont pas été touchés: des inclusions de scripts obfusqués, des chaînes Base64, des hooks ajoutés à des moments précis.

Le piège, c’est que ces symptômes peuvent aussi venir d’un thème compromis, d’une fuite via identifiants, d’une mauvaise permission sur des répertoires, ou d’un plugin défectueux mais innocent. C’est justement pour ça qu’un audit de chaîne d’approvisionnement a besoin d’enquêter sur la provenance, pas seulement sur l’état actuel.

Ce qu’un audit doit apprendre, concrètement

Un audit n’est pas une accumulation de scans. Il doit répondre à des questions actionnables, sinon il devient une “conclusion” sans suite. Dans une approche orientée chaîne d’approvisionnement, je cherche généralement trois résultats.

image

D’abord, comprendre quelles dépendances de plugins sont réellement exécutées, et à quels moments. Ensuite, savoir si le code présent sur le serveur correspond au code attendu pour la version installée, avec des preuves vérifiables. Enfin, établir des règles de décision: on bloque, on remplace, on isole, ou on surveille.

Cette logique change la manière de traiter une compromission. Au lieu de “nettoyer et repartir”, on remonte le fil jusqu’à la confiance initiale. Si un plugin est suspect, on ne se contente pas de le désactiver: on doit savoir s’il a été altéré après installation, ou si c’est sa version elle-même qui est construite sur une base compromise.

Cartographier votre chaîne d’approvisionnement WordPress (sans tomber dans le théâtre)

La chaîne d’approvisionnement, en pratique, commence avant la minute où vous installez le plugin. Sur un site WordPress, elle inclut:

    les dépôts et mécanismes d’installation (dépôt officiel, zip fourni par un tiers, marketplace, scripts d’installation); l’historique des mises à jour (manuel, automatique, ou via outils d’administration); les dépendances internes du plugin (autres librairies embarquées, chargements de fichiers, inclusions conditionnelles); les requêtes sortantes (téléchargement de ressources additionnelles, appels à des API externes, chargements différés); les chemins d’exécution WordPress (où le code est hooké, dans quel contexte, et avec quel niveau de privilège).

Une erreur fréquente consiste à regarder uniquement la liste “Plugins installés” dans l’interface WordPress. C’est utile, mais insuffisant. Un plugin peut être “actif” et peu dangereux, tandis qu’un plugin “désactivé” peut contenir du code de persistance, ou être réactivé via un mécanisme d’attaque plus large.

Lors d’un audit réel, j’ai vu une installation où seuls deux plugins étaient actifs dans l’admin, mais trois autres plugins contenaient des fichiers ajoutés en base ou via des include dynamiques. Le site ne “fonctionnait” pas mal, il servait juste des contenus légèrement modifiés à certains visiteurs. L’équipe d’origine avait passé les scans en croyant que l’actif en admin suffisait. Non.

Valider l’attendu: vérifier la cohérence entre le plugin sur serveur et le plugin source

Une fois que vous avez identifié les plugins à risque, l’objectif devient très concret: vérifier que le code présent sur le serveur correspond au code attendu pour la version installée.

Cela peut se faire de plusieurs manières, selon votre capacité et vos contraintes. L’idée n’est pas d’être parfait à 100 pour cent du premier coup, mais d’être rigoureux sur les points qui ont le plus de valeur.

image

Par exemple, si vous pouvez récupérer une archive “source” pour la version exacte depuis une source officielle, vous pouvez comparer la structure de dossiers, repérer les fichiers additionnels, et analyser les zones typiques d’injection (fichiers PHP ajoutés, scripts dans des sous-dossiers rarement utilisés, chargeurs obfusqués).

Dans beaucoup de cas, on découvre un schéma: le plugin contient une logique “normale” et des lignes “qui n’appartiennent pas” à la version. Parfois c’est juste une petite fonction ajoutée, mais elle déclenche une redirection, un téléchargement de payload, ou une exfiltration.

Sur un serveur mutualisé ou avec accès limité, on ne peut pas toujours reconstruire le hash de chaque fichier. On peut alors utiliser des indicateurs comportementaux et structurels. Par exemple, chercher des hooks inhabituels, des appels curl ou file getcontents vers des domaines inconnus, des scripts qui s’exécutent depuis wp-cron, ou des inclusions de fichiers chargés depuis des répertoires non attendus.

Analyse statique: ce qu’il faut chercher dans le code des plugins

L’analyse statique n’exige pas de devenir analyste malware à plein temps. Mais il faut savoir où regarder. Dans la majorité des cas “réalistes”, la malveillance se manifeste dans quelques motifs récurrents.

Voici les éléments que je vérifie avec le plus de constance, parce qu’ils reviennent dans des plugins compromis:

1) le chargement dynamique de fichiers (inclusion via noms concaténés, chargement basé sur l’URL ou un paramètre); 2) l’obfuscation qui n’est pas cohérente avec le plugin (chaînes Base64 ou gabarits de décodage, fonctions de déchiffrement maison); 3) des appels réseau sortants déclenchés au moment du rendu (pas juste des requêtes d’API normales); 4) des hooks ajoutés pour l’authentification ou les requêtes de requêtes (ex: intercept de wp-login ou modification de réponse); 5) des écritures dans des emplacements inhabituels (fichiers dans des dossiers temporaires, écriture dans uploads, création de scripts).

Je fais attention à un piège: un plugin peut utiliser des appels réseau légitimes (SEO, analytics, intégration de services). Ce qui compte, c’est la logique, le déclenchement, et la destination. Une API connue peut être légitime, une destination anonyme ou nouvellement apparue, très souvent, ne l’est pas.

Analyse dynamique: observer ce que le plugin fait vraiment dans votre contexte

La partie “dynamique” est souvent celle qui donne le plus de certitude. Le code peut être inoffensif sur un environnement de test, et dangereux sur votre site, parce que l’attaque dépend de la configuration: paramètres réseau sortants, version PHP, présence d’un cookie, contenu d’une option en base, ou même un identifiant.

Dans un audit, je privilégie un parcours simple: reproduire les conditions qui déclenchent le comportement. Si vous voyez des redirections, je teste depuis plusieurs emplacements et différents user-agents. Si vous observez une modification de contenu, je vérifie si la logique dépend de pages précises.

Sans outiller lourdement, vous pouvez déjà gagner énormément en mettant en place une observation minimale. Par exemple, journaliser les sorties HTTP, ou surveiller les écritures de fichiers et les modifications de répertoires sensibles. L’objectif n’est pas de “capturer tout”, mais de détecter un schéma. Sur une infection opportuniste, le comportement se manifeste tôt, avant même que l’utilisateur comprenne ce qui se passe.

Une méthode de triage pour choisir quels plugins auditer en premier

Quand un site compte des dizaines de plugins, tout auditer à fond peut être irréaliste. Le triage est alors votre garde-fou: vous ciblez ce qui a la probabilité la plus forte d’être impliqué, et vous itérez.

Voici un triage court, basé sur ce que j’ai vu fonctionner en incident response:

    plugins installés récemment ou mis à jour récemment, surtout si la mise à jour a été automatique; plugins provenant de sources non officielles, bundles fournis par un tiers, ou packages “graphiques” dont le contenu n’est pas clair; plugins dont le code contient des appels réseau inattendus, ou des includes dynamiques; plugins qui déclenchent des hooks sensibles (auth, cron, rendu complet, filtres WordPress larges); plugins associés aux symptômes observés, par exemple ceux qui touchent les formulaires si l’attaque cible des soumissions.

Ce triage ne remplace pas l’enquête, mais il évite de perdre des heures à disséquer un module qui ne peut pas être à l’origine du comportement observé.

Construire une preuve: comment savoir si le plugin a été altéré après installation

Une différence essentielle sépare deux scénarios. Scénario A: le plugin que vous avez installé était déjà compromis. Scénario B: le plugin était sain, puis un acteur a modifié son code après coup, soit via un autre vecteur (identifiants, vulnérabilité ailleurs), soit via une persistance.

La séparation n’est pas toujours nette, mais vous pouvez chercher des indices.

Par exemple, la date de modification des fichiers dans le répertoire du plugin peut donner un signal. Si des fichiers changent “après” votre dernière mise à jour, c’est un indice fort d’altération. Sur un système stable, les timestamps ne bougent pas sans raison, sauf lors d’actions d’installation ou de mises à jour.

Autre indice: la base de données peut contenir des options, des enregistrements, ou des hooks persistants ajoutés par le code. Si ces modifications ont une temporalité cohérente avec l’apparition des symptômes, vous êtes face à un chemin d’infection plausible.

Et surtout: la correspondance code attendu vs code présent. Si vous comparez votre plugin https://gardewp.fr/ actuel avec le code d’une version réputée identique, et que des fichiers sont ajoutés avec une logique malveillante, le scénario B devient crédible. Si, au contraire, le plugin “attendu” contient déjà le comportement suspect, le scénario A domine.

Cas typiques d’attaques par chaîne d’approvisionnement sur WordPress

Sans inventer de “faits universels”, je peux décrire des modèles que j’ai rencontrés et qui reviennent avec des variantes. Souvent, les acteurs cherchent une exécution fiable. WordPress leur offre des points d’entrée très confortables.

Un modèle fréquent consiste à injecter un chargement dans un fichier qui est exécuté à chaque requête, ou presque. Par exemple, un hook côté front, ou un code déclenché sur init. Cela garantit que la malveillance est active dès qu’un visiteur arrive, tout en restant “dans le cadre” d’un plugin.

Un autre modèle: différer l’action. Le code peut vérifier des conditions comme la présence d’une signature, une plage d’IP, ou une date. Cela complique les tests rapides. Vous nettoyez trop vite, vous ne voyez rien pendant l’enquête, puis le comportement réapparaît plus tard.

Un troisième modèle: charger un second stage à partir d’une ressource distante. Dans ce cas, l’analyse statique locale ne montre qu’un “chargeur”. Sans surveillance réseau, on sous-estime le risque, car le code malveillant principal n’est pas dans votre serveur au départ.

C’est pour ça que la dynamique compte autant que la statique.

Durcir la chaîne d’approvisionnement: règles plutôt que promesses

Un bon audit ne s’arrête pas au diagnostic. Il produit des règles et des comportements. En matière de chaîne d’approvisionnement, le durcissement consiste souvent à réduire les zones grises.

D’un côté, vous pouvez agir sur l’installation: forcer des sources fiables, limiter l’installation depuis des “zip inconnus”, et garder une trace des versions installées. De l’autre, vous agissez sur l’exécution: permissions, journaux, surveillance des modifications de fichiers, et procédures de déploiement.

Le point délicat, c’est l’équilibre. Trop de verrouillage peut casser un site, ou rendre les mises à jour impossibles pour l’équipe. Je préfère des garde-fous progressifs: au début, vous limitez l’exposition sur les plugins les plus risqués, puis vous étendez.

Si vous gérez plusieurs sites, vous pouvez aussi standardiser la façon de contrôler les plugins. À une époque, une petite équipe d’exploitation que j’ai accompagnée avait des procédures différentes d’un site à l’autre. Résultat: lors d’un incident, ils savaient désinfecter, mais ils perdaient du temps à expliquer comment comparer les versions. Standardiser a réduit le temps de réponse, et donc la durée pendant laquelle un site reste exposé.

Mettre en place un processus d’audit répétable, sans transformer l’équipe en laboratoire

Il y a une réalité: votre client a un calendrier, votre équipe a des urgences, et WordPress n’attend pas. Un processus d’audit doit donc être répétable, pas parfait.

Le schéma que j’utilise souvent ressemble à ceci, mais adapté à votre maturité:

D’abord, établir une base de référence. C’est votre “avant”. Une archive des fichiers plugins, une capture des versions, et des journaux minimaux. Ensuite, lors d’une mise à jour, vous comparez ce qui change. Si un plugin est critique, vous acceptez moins de variation sans contrôle.

Ensuite, vous mettez en place des signaux d’alerte. Au lieu de scruter chaque fichier à la main, vous surveillez les modifications dans les répertoires sensibles. Vous analysez uniquement quand quelque chose bouge.

Enfin, vous planifiez la réponse. Si un plugin devient suspect, vous devez pouvoir isoler rapidement: désactiver, remplacer, restaurer à partir d’une version connue saine, et retirer l’accès temporairement si des comptes ont été compromis.

Ce côté “opérationnel” est souvent ce qui manque aux audits purement techniques.

Les questions que je pose toujours en audit, parce qu’elles changent la décision

Au fil des audits, j’ai remarqué que les meilleures conclusions viennent de questions précises, pas de longues descriptions de symptômes.

Par exemple:

    Qui a le droit d’installer des plugins, et comment ces droits sont-ils gérés? Les plugins sont-ils installés via l’interface WordPress, via un outil de gestion, ou via déploiement manuel? Y a-t-il des plugins “maison” ou des plugins acquis via échange, qui ne sont pas documentés? Comment sont gérées les mises à jour, auto ou planifiées? Le site a-t-il un historique d’incidents, même mineurs, qui indiquent un risque de persistance?

Ces questions font remonter la confiance, pas seulement le code. Un site WordPress infecté est presque toujours le résultat d’une chaîne humaine et technique, pas d’un bug isolé.

Une petite checklist de fin de boucle (pour décider quoi faire après l’audit)

Quand l’analyse est terminée, il reste une étape pratique: décider l’action suivante. Cette décision dépend de l’évidence que vous avez, du niveau de risque, et du coût de remplacement.

Voici une checklist courte qui m’aide à trancher sans m’égarer:

    confirmer si le code suspect est uniquement “actif” ou réellement intégré aux fichiers du plugin; vérifier la cohérence avec la source attendue de la version installée; identifier si le plugin a été modifié après installation (timestamps, fichiers ajoutés, base de données); déterminer si d’autres plugins peuvent être l’origine (vecteur initial) ou seulement des victimes; planifier le remplacement et la surveillance, pas uniquement le nettoyage.

Si ces cinq réponses ne sont pas claires, je considère que l’audit doit continuer. Nettoyer sans certitude, c’est s’exposer à une réapparition.

Edge cases qui rendent l’audit plus difficile (et comment éviter les faux positifs)

Les faux positifs existent, et ils coûtent cher. Un plugin peut contenir des obfuscations parce qu’il minifie, compresse, ou charge des templates de manière “propre”. Un autre peut utiliser des appels réseau pour ses fonctionnalités, et pourtant envoyer des requêtes vers des domaines qui ressemblent à du tracking.

La bonne approche consiste à contextualiser. Je regarde:

    la fréquence de l’appel réseau, une API “normale” se déclenche de manière régulière, la malveillance peut être plus irrégulière; le contenu des endpoints, un domaine très générique ou un chemin obscur est plus inquiétant qu’un endpoint d’un service connu; les hooks utilisés, un hook de rendu pour un plugin de formulaire peut être normal, un hook global qui manipule tout le contenu, moins; la présence de mécanismes de persistance dans des fichiers et options.

Enfin, si le site est en production avec un trafic réel, je privilégie une démarche de réduction de risque plutôt que de tout casser. Isoler d’abord, observer ensuite, puis décider de remplacer. C’est souvent plus prudent que de faire une purge totale immédiatement, surtout si vous ne savez pas encore quel plugin a déclenché la compromission.

Ce que la méthode change pour la prévention

Après un audit de chaîne d’approvisionnement, le bénéfice est rarement “juste” technique. Il transforme votre façon de gérer les plugins.

Vous commencez à raisonner en politiques de confiance: sources autorisées, versions verrouillées, contrôles avant déploiement, et surveillance des écarts. L’équipe devient plus prudente sur les installations “juste pour essayer”, parce que chaque plugin introduit un risque de code.

Et vous réalisez un point important: un plugin compromis peut ne pas être détecté par des scans habituels. Certains payloads restent silencieux, ou ne se manifestent que sous certaines conditions. Le meilleur rempart n’est pas la détection ponctuelle, c’est la réduction de la zone dans laquelle un code non vérifié peut s’exécuter.

Dans une organisation mature, l’audit de chaîne d’approvisionnement devient un réflexe lors de chaque nouveau plugin. Pas nécessairement un audit complet à chaque fois, mais un contrôle de cohérence et une surveillance suffisante pour repérer la dérive.

Dernier point pratique: garder l’évidence sans perdre la sérénité

Quand on gère un site WordPress infecté, on est parfois sous pression. On veut agir vite. Ce réflexe est utile, mais il peut aussi faire perdre des preuves. Si vous supprimez tout au premier nettoyage, vous effacez les indices de provenance.

Je recommande de capturer d’abord ce qui peut l’être sans risque irréversible: versions, liste plugins et thèmes, copies de fichiers de plugins suspects, échantillons de fichiers ajoutés, et quelques extraits de logs qui montrent le déclenchement. Ensuite seulement, vous nettoyez ou vous restaurez.

Cette discipline change tout dans la qualité de l’audit. Elle permet de répondre à la vraie question: le plugin était-il déjà corrompu, ou a-t-il été altéré chez vous? Sans cette réponse, la prévention future reste floue.

Si vous voulez, je peux aussi proposer une grille d’audit adaptée à votre cas, par exemple en fonction de votre volume de plugins, de vos accès (shell, base de données, logs), et du type de symptômes que vous observez (redirection, injection de contenu, création de comptes, charge serveur anormale).