WordPress piraté à répétition : trouver la porte dérobée
WordPress piraté à répétition malgré le nettoyage ? Trouvez les portes dérobées qui survivent : auto_prepend_file, mu-plugins, faux plugins, cron, sites voisins.
Aussi en : English Deutsch Español Italiano Polski Português Türkçe
Si WordPress est piraté de nouveau après votre nettoyage, l'attaquant dispose presque certainement encore d'un accès. Soit une porte dérobée a survécu, soit la faille qu'il a exploitée la première fois est toujours ouverte. Une copie neuve du cœur de WordPress et quelques fichiers supprimés ne règlent ni l'un ni l'autre.
Ce guide suppose qu'un nettoyage a déjà eu lieu. Il passe en revue les endroits où une porte dérobée survit le plus souvent, dans l'ordre d'exécution du code, avec une vérification précise pour chacun. Il se termine par l'étape qui brise réellement le cycle.
Pourquoi WordPress est de nouveau piraté après un nettoyage
Il y a deux causes, et on les cumule souvent. La première est la persistance. Les attaquants laissent rarement une seule porte dérobée : ils en posent plusieurs, à des endroits différents, de sorte que si vous en trouvez une, les autres continuent de fonctionner. La seconde est un point d'entrée toujours ouvert : une extension vulnérable, un mot de passe volé ou un site voisin hébergé dans le même compte.
Le délai de réapparition donne un indice. Une réinfection quelques heures après le nettoyage désigne en général une porte dérobée restée en place ou une tâche planifiée qui réécrit des fichiers. Une réinfection au bout de plusieurs jours ou semaines pointe plutôt vers une faille ouverte, qu'une personne ou un robot a retrouvée.
Les failles ouvertes sont repérées vite. Le rapport State of WordPress Security in 2026 de Patchstack émane d'un éditeur : lisez-le comme tel. Il indique que, pour les vulnérabilités massivement exploitées, le délai médian pondéré avant exploitation de masse était de 5 heures. Environ la moitié des vulnérabilités à fort impact ont été exploitées en moins de 24 heures. Le même rapport précise que 46 % des vulnérabilités n'avaient aucun correctif disponible au moment de leur divulgation publique. Une extension non corrigée n'est pas un petit risque que l'on traite la semaine suivante.
Avant de chercher : copiez d'abord les preuves
Faites une copie complète des fichiers et de la base de données, et sauvegardez les journaux d'accès du serveur web, avant de supprimer quoi que ce soit d'autre. Relevez ensuite les horodatages des fichiers, afin de pouvoir les rapprocher plus tard des entrées de journal :
cd /path/to/site
find . -type f -printf '%T@ %TY-%Tm-%Td %TH:%TM %p\n' | sort -n > ~/file-times-$(date +%F).txt
wp db export ~/evidence-db-$(date +%F).sql
L'export de la base contient des données personnelles : déplacez ces copies hors du serveur, vers un stockage sécurisé. Sinon, la recherche de sauvegardes plus bas les listera, et elles pourraient être lues ou supprimées par erreur. Tenez une liste simple de chaque fichier, utilisateur et tâche cron que vous supprimez, avec la date. Elle vous servira à retrouver le point d'entrée. Si des données personnelles sont concernées, elle vous servira aussi parce que l'article 33, paragraphe 5, du RGPD impose de documenter toute violation, y compris celles que vous ne notifiez pas. Les étapes complètes de confinement figurent dans notre plan de nettoyage d'un WordPress infecté.
Portes dérobées exécutées avant le chargement de WordPress
Le paramètre PHP auto_prepend_file exécute un fichier avant chaque requête PHP, donc avant le chargement de WordPress et avant que n'importe quelle extension de sécurité puisse le voir. C'est précisément pour cela que les attaquants l'apprécient. Il peut être défini dans php.ini, dans un fichier .user.ini placé dans n'importe quel répertoire, ou via php_value dans .htaccess sous Apache avec mod_php. Lancez la recherche sur tout le compte :
grep -Rn --include='*.ini' --include='.htaccess' -E 'auto_(prepend|append)_file' ~ 2>/dev/null
php --ini
php --ini indique quels fichiers de configuration le PHP en ligne de commande charge. Le PHP du serveur web peut en utiliser d'autres : vérifiez donc aussi les paramètres PHP dans votre panneau d'hébergement. Toute valeur qui pointe vers un fichier que vous ne reconnaissez pas est une trouvaille. Notez le chemin, gardez une copie du fichier, puis supprimez le paramètre. Les modifications de .user.ini peuvent mettre quelques minutes à s'appliquer, car PHP met ces fichiers en cache.
Contrôlez ensuite wp-config.php. Un fichier sain contient un seul require_once ABSPATH . 'wp-settings.php'; vers la fin, et rien d'exécutable avant le bloc de commentaires d'ouverture. Cherchez tout le reste :
head -n 5 wp-config.php
grep -nE 'eval\(|base64_decode|gzinflate|str_rot13|include|require' wp-config.php
awk 'length > 300 {print FILENAME": line "NR" is "length" chars"}' wp-config.php
Comparez-le ensuite avec diff à une copie saine issue d'une ancienne sauvegarde. Si vous n'en avez pas, comparez sa structure avec wp-config-sample.php. Des include supplémentaires, des lignes très longues et du code avant le bloc de commentaires n'ont rien à faire là.
Cachettes dans wp-content : mu-plugins et faux plugins
Mu-plugins et drop-ins
Tout ce qui se trouve dans wp-content/mu-plugins s'exécute automatiquement à chaque requête. WordPress ne les affiche que dans un onglet « Must-Use » distinct de l'écran Extensions, les fichiers PHP situés dans des sous-dossiers n'y figurent pas du tout, et aucun ne peut être désactivé depuis le tableau de bord. Beaucoup de sites n'ont aucun mu-plugin. Certains hébergeurs en installent un pour le cache ou l'administration.
ls -la wp-content/mu-plugins
wp plugin list --status=must-use
wp plugin list --status=dropin
Les drop-ins sont un autre endroit à vérifier : object-cache.php, advanced-cache.php et db.php, directement dans wp-content. Ils sont légitimes quand votre extension de cache ou votre hébergeur les a placés là. Si rien de ce que vous avez installé ne les explique, considérez-les comme suspects.
Dossiers d'extensions factices ou imitant de vraies extensions
Un dossier d'extension sans en-tête valide n'apparaît pas dans WordPress, mais ses fichiers PHP restent appelables directement par URL. Comparez ce qui est sur le disque avec ce que WordPress connaît :
ls -1 wp-content/plugins | sort > on-disk.txt
wp plugin list --field=name | sort > registered.txt
comm -23 on-disk.txt registered.txt
index.php et les extensions d'un seul fichier comme hello.php apparaîtront dans le résultat. Tout le reste mérite un examen. Méfiez-vous des noms qui imitent de vraies extensions : un tiret en trop, un numéro de version dans le nom du dossier, ou « wp-security-core ». Lancez ensuite wp plugin verify-checksums --all. La commande ne couvre que les extensions hébergées sur WordPress.org. Pour les extensions premium, téléchargez une copie neuve chez l'éditeur et comparez-la avec diff -r. Pour le cœur de WordPress, suivez notre guide sur la vérification des sommes de contrôle de WordPress.
Fichiers PHP modifiés récemment
find wp-content -type f -name '*.php' -mtime -30 -printf '%TY-%Tm-%Td %p\n' | sort
find wp-content -type f -name '*.php' -ctime -30
Si vous n'avez pas encore réinstallé le cœur, -newer wp-includes/version.php est un autre repère utile. Un attaquant peut falsifier les dates de modification avec touch, mais pas facilement la date de changement d'état (-ctime) : lancez donc les deux commandes. Les fichiers PHP dans le dossier uploads font l'objet de notre guide sur les web shells dans wp-content/uploads.
Persistance dans la base de données et le planificateur : cron, administrateurs, mots de passe d'application
Les événements WP-Cron vivent dans la base de données et survivent à un nettoyage des fichiers. Listez-les :
wp cron event list --fields=hook,next_run_relative,recurrence
Chaque hook doit appartenir au cœur ou à une extension que vous connaissez. Pour un hook inconnu, cherchez dans le code avec grep -Rn "hook_name" wp-content. Si le code qui l'enregistre se trouve dans un mu-plugin ou dans un fichier que vous ne savez pas expliquer, vous avez trouvé une porte dérobée. Un hook sans code correspondant peut signifier que la porte dérobée a été retirée mais que l'événement subsiste. Supprimez-le avec wp cron event delete hook_name.
Contrôlez ensuite le côté serveur : crontab -l pour l'utilisateur d'hébergement, ainsi que la section cron de votre panneau d'hébergement. Une tâche qui lance curl ou wget, ou qui écrit dans la racine web, peut restaurer discrètement une porte dérobée chaque nuit.
Viennent ensuite les comptes. Cette commande liste chaque administrateur, puis les mots de passe d'application de chacun :
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered
for id in $(wp user list --role=administrator --field=ID); do echo "User $id"; wp user application-password list "$id"; done
Les mots de passe d'application continuent de fonctionner après un changement de mot de passe : une entrée inconnue ici est aussi grave qu'un administrateur inconnu. La revue complète est détaillée dans notre guide sur les rôles utilisateur de WordPress.
Cherchez enfin les scripts injectés dans les contenus et les réglages. Remplacez wp_ si votre préfixe de tables est différent :
wp db search 'script src=' wp_options wp_posts --stats
wp db search 'atob(' wp_options wp_posts --stats
wp db query "SELECT option_name, LENGTH(option_value) AS size FROM wp_options WHERE autoload IN ('yes','on','auto','auto-on') ORDER BY size DESC LIMIT 20;"
Les deux premières recherches trouvent les inclusions de scripts externes et le décodage base64 que le JavaScript injecté utilise souvent. La requête liste les plus grosses options chargées automatiquement : un nom d'option inconnu avec une valeur volumineuse mérite un examen approfondi.
L'extension d'audit de secaudit.eu regroupe ces indicateurs de compromission : administrateurs cachés, entrées cron suspectes, spam en base de données et écarts de sommes de contrôle du cœur. Elle détecte et signale ; les décisions de suppression vous reviennent → secaudit.eu
Le reste du compte d'hébergement : sites voisins et anciennes copies
Sur un hébergement mutualisé, plusieurs sites tournent souvent sous un même utilisateur système. Une copie de préproduction infectée, une installation oubliée dans /old/ ou /test/, ou une archive ZIP de sauvegarde dans la racine web réinfectera le site que vous venez de nettoyer. Listez tout ce que contient le compte :
find ~ -name wp-config.php 2>/dev/null
find ~ -type d -name wp-includes 2>/dev/null
find ~ -type f \( -name '*.zip' -o -name '*.tar.gz' -o -name '*.sql' \) 2>/dev/null
Nettoyez tous les sites du compte en même temps, ou supprimez ceux dont personne n'a besoin. Si tous les sites partagent un même utilisateur SFTP et un même utilisateur de base de données, n'importe quel site compromis peut écrire dans tous les autres. Séparez-les en utilisateurs distincts, ou en comptes d'hébergement distincts, dès que c'est possible.
Fermer le point d'entrée et tout renouveler d'un seul coup
Retrouvez le point d'entrée dans les journaux d'accès, autour de l'horodatage de la première infection. Cherchez les requêtes POST vers des fichiers inhabituels et vers les points d'accès de l'extension que vous soupçonnez :
grep '"POST ' access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -30
Mettez à jour l'extension vulnérable. S'il n'existe aucun correctif ou si l'extension est abandonnée, remplacez-la. Si vous n'en avez pas besoin, supprimez-la. La désactiver ne suffit pas : les fichiers restent sur le disque et peuvent toujours être appelés par URL.
Renouvelez ensuite tous les secrets en une seule session, après le nettoyage et pas avant. Si vous le faites trop tôt, une porte dérobée encore présente lira simplement les nouvelles valeurs.
- Tous les mots de passe des administrateurs WordPress, et chaque mot de passe d'application
- Mot de passe de la base de données : changez-le dans le panneau d'hébergement, puis mettez à jour
DB_PASSWORDdans wp-config.php - Accès au panneau d'hébergement, mots de passe SFTP et clés SSH (supprimez les clés que vous ne reconnaissez pas)
- Clés d'API stockées dans les extensions (paiement, SMTP, CDN, stockage de sauvegarde)
- Clés de sécurité et salts :
wp config shuffle-salts
Relancez les mêmes vérifications 24 heures plus tard, puis après 7 jours, et comparez les résultats avec vos fichiers de preuves. Si un mu-plugin, un événement cron ou un fichier modifié réapparaît, il reste quelque chose que vous n'avez pas trouvé.
FAQ
Quand dois-je arrêter de m'en occuper moi-même ?
Arrêtez à la deuxième réinfection, ou tout de suite si des données clients ont pu être exposées ou si le site est une boutique avec des commandes. Faites alors appel à des professionnels de la réponse à incident. Si des données personnelles sont touchées, vérifiez aussi si la notification à l'autorité de contrôle sous 72 heures prévue par le RGPD s'applique. Notre guide sur la notification de violation sous 72 heures explique à partir de quand le délai court.
Restaurer une sauvegarde suffit-il ?
Seulement si la sauvegarde est antérieure à la première porte dérobée et si vous avez fermé le point d'entrée. Sinon, vous restaurez le problème avec le site.
Changer tous les mots de passe arrête-t-il les attaques ?
Non. Une porte dérobée encore en place n'a pas besoin de mot de passe. Renouvelez les identifiants après la chasse, pas à sa place.
Cet article est une information générale et ne constitue pas un conseil juridique.
Vérifiez votre propre site
Security Audit & Compliance est l’extension WordPress de secaudit.eu : elle recherche les logiciels malveillants, le spam caché, les fichiers du cœur modifiés et le code vulnérable, et rattache ses constats à l’article 32 du RGPD, à l’article 21 de NIS2 et à l’OWASP Top 10.
Voir ce que vérifie l’extension