Un site WordPress peut paraître sain, tant que personne ne vérifie les détails. Le jour où ça déraille, on ne voit pas toujours une page d’accueil défigurée ou un message d’alerte rouge dans le navigateur. Parfois, les premiers signaux sont plus discrets, et c’est souvent là que le piège se referme: des fichiers malveillants ont été déposés dans des endroits “banals”, comme le dossier des médias.
Quand on parle de “shells web”, on parle généralement de scripts uploadés sur le serveur qui permettent, selon le cas, de lire, modifier, télécharger ou exécuter du code à distance. Le fait que ces shells se cachent dans les médias est particulièrement vicieux, parce que WordPress y stocke beaucoup de fichiers et parce que l’équipe technique ne regarde pas systématiquement chaque extension, chaque nom de fichier, ni l’historique des uploads.
Dans cet article, je vous explique comment je m’y prends pour supprimer des shells web présents dans les médias lors d’une désinfection WordPress, comment limiter les dégâts pendant l’investigation, et surtout comment éviter que le problème revienne. Je vais rester concret, avec des exemples de contrôles et des choix de traitement réalistes.
Comprendre pourquoi les shells finissent dans wp-content/uploads
WordPress stocke la plupart des images, PDFs et autres médias dans wp-content/uploads/. Beaucoup d’organisations ont un flux “upload facile”, que ce soit pour les images d’articles, les pièces jointes, ou des documents. Les droits d’écriture sur ce répertoire sont normaux. C’est aussi ce qui rend l’emplacement logique pour un attaquant.
En pratique, un shell web peut être déposé de plusieurs façons:
- via un script qui exploite une vulnérabilité applicative ou un plugin mal maintenu via une compromission d’identifiants (mot de passe réutilisé, compte admin pris par phishing, cred stuffing) via un point d’entrée sur le serveur (faiblesse côté hébergeur ou configuration) via une injection de contenu qui pousse l’upload d’un fichier
Une fois le shell uploadé, l’attaquant cherche à le rendre accessible sans que personne ne crie. Souvent, il choisit une extension qui ne déclenche pas d’alertes, ou bien il cache la fonction malveillante dans un fichier qui “a l’air” d’un média. Il peut aussi appeler le shell via une URL qui ressemble à un chemin d’image banal. Dans certains cas, le serveur exécute malgré tout le code, selon la configuration PHP (ou selon le comportement du serveur pour certains types de fichiers).
C’est pour ça que la désinfection ne consiste pas seulement à supprimer “un fichier suspect”. Il faut comprendre le chemin d’accès, le comportement du shell, puis tracer la cause du dépôt.
Les premiers symptômes qui mènent aux médias
Je ne compte plus le nombre de fois où la demande arrive avec une formulation du type “le site a été hacké, mais on ne sait pas comment”. Une partie des signaux qu’on retrouve lors d’une analyse initiale:
- augmentation anormale du nombre de requêtes HTTP sur des URLs qui ne correspondent pas au trafic habituel hausse du 404 ou du 403 sur des chemins liés à des fichiers au format “bizarre” présence de nouveaux fichiers dans wp-content/uploads avec des dates incohérentes par rapport au planning éditorial tentatives de chargement d’URL qui ressemblent à des médias, mais qui renvoient des réponses typiques de scripts
Quand un shell est dans les médias, la différence entre “média légitime” et “média toxique” tient souvent à des micro-détails: une taille trop petite pour un fichier image censé exister, une extension incohérente, ou un contenu qui ne correspond pas au format annoncé.
Si le serveur a des logs, j’ouvre presque toujours:
- les logs Nginx ou Apache pour repérer l’URL exacte appelée les logs PHP pour voir si un fichier a été exécuté les logs applicatifs WordPress si disponibles (activités, erreurs, et surtout actions liées aux uploads)
Un point important: pendant l’investigation, je limite l’exposition. Je ne teste pas n’importe quelle URL suspecte sur un site public, surtout si je n’ai pas déjà isolé. Selon la configuration, une exécution peut déclencher d’autres actions.
Travailler proprement: éviter l’effet “je touche au site en direct”
Avant de supprimer, je fais une “pré-désinfection” pour stabiliser la situation. L’objectif est simple: éviter que l’attaquant garde une porte de retour.

La démarche dépend beaucoup du contexte, mais https://gardewp.fr/ le principe est le même: je protège d’abord l’accès, puis je récupère les éléments nécessaires, ensuite seulement je nettoie.
Quand je suis en présence de shells dans les médias, je traite souvent le site comme compromis jusqu’à preuve du contraire. Ça change le ton et le rythme: on ne garde pas une logique “on remplace juste un plugin”, on passe sur une logique de retrait complet et vérifié.
Identifier les shells dans wp-content/uploads
L’identification, c’est à la fois de la chasse et de la méthode. Les shells web n’ont pas un unique format, mais ils ont des caractéristiques récurrentes. Parfois, le fichier contient du code PHP directement, parfois il contient une obfuscation, parfois il abuse de fonctions classiques (lecture de fichiers, exécution, accès au système, etc.).
Je procède généralement en trois temps: repérer des candidats, vérifier, puis confirmer.
Voici la façon dont je le structure en pratique (sans vous demander d’exécuter ça en live sur un site de production sans sauvegardes).
Geler l’upload et réduire la surface d’attaque
Je coupe temporairement les points d’entrée qui permettent d’installer de nouveaux fichiers (par exemple, en désactivant l’accès admin si nécessaire, en mettant le site en maintenance, et en bloquant l’écriture si l’hébergement le permet). Je ne supprime rien tant que je n’ai pas fait une sauvegarde de travail.Lister les nouveaux fichiers dans les médias
Je compare l’arborescence wp-content/uploads à la période de suspicion. L’objectif n’est pas de trier tout le dossier, mais de repérer les “anomalies”: nouveaux fichiers dont l’extension n’a pas de sens pour votre usage, ou des tailles très faibles, ou des noms qui collent à des schémas de scripts.Contrôler la cohérence extension, contenu, exécution potentielle
Un fichier “image” qui contient du PHP est un indicateur. Un fichier avec une extension “bizarre” (par exemple un nom qui ressemble à une image, mais dont le suffixe n’est pas standard pour votre site) est aussi un indicateur. Je vérifie également la configuration serveur, parce que certains serveurs peuvent exécuter du PHP via des astuces de configuration.Relier les fichiers aux requêtes dans les logs
Le plus convaincant, c’est quand je retrouve une URL appelée dans les logs qui pointe vers un fichier dans uploads. Ça confirme que le shell n’était pas seulement posé, mais utilisé. Je documente l’URL complète et la chaîne de requête si présente.Valider avant suppression
Si j’ai un doute sur un fichier “inexpliqué” qui ne ressemble pas à un shell, je préfère le garder dans un environnement isolé pour analyse. Sur site en production, je n’aime pas supprimer un fichier légitime au hasard, surtout si le site dépend d’archives médias.Cette étape est la base de la désinfection WordPress: sans corrélation logs et contenu, on finit vite par nettoyer “au feeling”, et ça laisse des résidus.
La suppression: ce que je retire, ce que je garde, et ce que je surveille
Quand le shell est identifié, la suppression doit être nette. Mais il y a un piège fréquent: un shell peut recréer d’autres fichiers, soit en arrière plan, soit via une prochaine requête. D’où l’importance de couper le point de contrôle.
Concrètement, je fais souvent ceci:
- supprimer les fichiers identifiés comme malveillants dans wp-content/uploads vérifier les fichiers voisins (mêmes dates, mêmes motifs de nom, mêmes dossiers) vérifier s’il existe des hooks ou des scripts ailleurs (le shell peut seulement être la “façade”, la logique principale étant dans un autre répertoire) contrôler les répertoires qui accueillent des dépôts et des caches, parce qu’un nettoyage incomplet peut laisser une persistance
Il est tentant de tout supprimer dans uploads, mais c’est rarement une bonne idée sur un site vivant. À moins d’avoir un périmètre clair, je préfère retirer uniquement les fichiers confirmés et vérifier la cohérence des médias. Le coût d’un retour à la version précédente, ou d’un inventaire à grande échelle des médias, peut être plus élevé que le nettoyage ciblé.
Nettoyer aussi les persistances, même si “tout semble être dans les médias”
Les shells dans les médias sont parfois le seul artefact visible. Mais j’ai déjà vu des scénarios où la contamination venait de WordPress, et où des “agents” avaient été déposés ailleurs. Par exemple:
- un plugin inconnu ou un plugin modifié un thème altéré des fichiers dans wp-includes ou à la racine du site des fichiers ajoutés dans des répertoires temporaires ou des dossiers non attendus une modification de fichiers système, ou une persistance côté serveur (selon l’hébergement)
Je ne me contente donc pas de supprimer les shells. Je fais une tournée de vérification sur les emplacements typiques d’altération, et je compare avec ce que vous devriez avoir selon votre version de WordPress et vos plugins.
Ce point est important pour la désinfection WordPress: si vous ne retirez que le shell dans les médias, mais que le vecteur initial est toujours actif, l’attaquant repassera, et vous aurez une rechute rapide.
Cas fréquent: le shell a une signature d’upload en plusieurs fichiers
Un détail qui change la méthode: certains shells ne sont pas “un seul fichier”. Ils se décomposent en une série de composants, par exemple:
- un premier fichier “amorce” dans uploads un fichier d’exécution dans un autre dossier une configuration ou une charge obfusquée un mécanisme qui surveille ou qui réécrit
Quand je tombe sur ce schéma, je traite uploads comme un indice plutôt que comme la source unique. Je remonte dans la chronologie.
Je regarde:
- la date de modification des candidats dans uploads les dossiers où les fichiers ont été posés les noms qui se répètent, ou les extensions groupées qui apparaissent d’un coup l’évolution: parfois, la première série de fichiers apparaît, puis une seconde série après une tentative d’accès
Dans ces cas, la suppression isolée du “premier shell visible” n’est pas suffisante, car le reste peut encore fonctionner.
Vérifier la configuration serveur quand le shell est dans un média
Un point souvent oublié lors de la désinfection: même si vous supprimez le shell, il faut vérifier pourquoi le serveur l’a exécuté.
Selon votre hébergeur, WordPress est servi via Apache ou Nginx avec PHP-FPM. La question centrale est la gestion des types de fichiers. Dans certains environnements, un fichier dans uploads peut devenir exécutable selon les règles de traitement PHP. C’est variable.
Ce que je vérifie typiquement:
- règles de type MIME et de traitement PHP présence de directives (ou de configuration) qui associent PHP à des extensions non standard fichiers .htaccess ou équivalents, modifiés dans wp-content ou à la racine configuration du document root et alias
Si votre environnement est géré, l’hébergement peut vous expliquer ce qui est possible. Si vous avez accès à la configuration, l’objectif est d’empêcher l’exécution de code dans les répertoires de médias, tout en conservant l’affichage normal des fichiers statiques.
Je le dis sans dramatiser: je ne pars jamais du principe que “le shell ne peut pas s’exécuter depuis uploads”. Je vérifie, parce que c’est précisément ce genre d’hypothèse qui coûte du temps, et parfois des réinfections.
Remettre WordPress en état: au-delà de la suppression des fichiers
Après avoir retiré les shells, je passe dans la phase “désinfection WordPress” qui consiste à éliminer les causes. Ici, il ne s’agit pas seulement de “nettoyer”, mais de revenir à une base saine.
Les actions habituelles incluent:
- changer les mots de passe (admins et comptes créés pendant l’incident) vérifier les comptes, supprimer ceux qui n’ont aucun justificatif mettre à jour WordPress et les plugins, ou remplacer ceux qui sont compromis ou trop anciens vérifier la présence de fichiers modifiés (thème, plugin, ou scripts) révoquer les sessions si c’est proposé par la configuration
Le point délicat est la séquence: je préfère faire la suppression et la restauration logicielle, puis seulement ensuite rouvrir l’accès public, avec des comptes nettoyés et des mises à jour. Si vous remettez le site en ligne trop tôt, vous offrez un test en conditions réelles à un attaquant qui n’attend que ça.
Sécuriser les médias pour empêcher la prochaine tentative
Une fois le nettoyage terminé, on peut réduire la probabilité qu’un shell soit uploadé et exécutable. Là encore, je raisonne en compromis: trop bloquer les téléchargements peut casser des workflows. Mais laisser un répertoire de médias comme un terrain libre sans contrôle, c’est une invitation à la répétition.
Je fais généralement une série de durcissements pragmatiques, adaptés à votre usage réel.
Limiter strictement les types de fichiers autorisés aux uploads
Si vous n’avez pas besoin de .php, .phtml, .phar ou d’autres extensions à risque, je les bloque. Selon votre configuration, cela se fait côté WordPress (filtres) et idéalement côté serveur.Mettre en place une séparation plus stricte des répertoires
Si vous pouvez, je recommande de réduire le couplage entre “uploads” et des endroits qui pourraient être interprétés comme exécutions PHP. Cela dépend de l’architecture et de l’hébergement.Désactiver ou durcir l’édition de fichiers depuis WordPress
Les fonctions qui permettent de modifier des fichiers via l’interface peuvent devenir un accélérateur en cas de compromission.Activer des alertes et une surveillance ciblée
Je veux au moins savoir quand de nouveaux fichiers apparaissent dans uploads, et qui les a créés si WordPress peut le tracer.Auditer les plugins et retirer ceux qui ajoutent des risques inutiles
Un plugin “pratique” mais mal maintenu est souvent l’entrée. Si une fonctionnalité n’est plus nécessaire, je la retire. Moins de surface, moins de surprises.Ces mesures ne garantissent pas une immunité totale, mais elles changent la donne. Une attaque devient plus coûteuse, et surtout plus détectable.
Exemple concret: quand l’URL ressemble à une image, mais “ça répond comme un script”
Je me souviens d’un cas où le rapport de sécurité mentionnait “un fichier image corrompu”. En regardant les logs, on voyait des requêtes vers une URL du style:
- un chemin sous wp-content/uploads/ avec un nom qui ressemblait à un slug de publication une URL qui renvoyait un contenu non conforme à un média (tailles de réponse étranges, en-têtes inhabituels) une répétition des tentatives, comme si l’attaquant testait des paramètres
En ouvrant le fichier suspect, ce n’était pas une image. La structure interne trahissait un script, et le contenu était obfusqué. La suppression du fichier n’a pas suffi à arrêter les tentatives, car d’autres fichiers liés existaient, posés quelques minutes plus tôt.
La vraie “victoire” a eu lieu au moment où on a identifié le vecteur initial, dans un plugin obsolète, puis corrigé le point d’entrée, et enfin appliqué un durcissement sur la gestion des uploads. Le même jour, on a aussi vérifié que les comptes et rôles n’avaient pas été altérés. Sans ça, j’aurais pu nettoyer le site, puis le voir replonger.
C’est typiquement le genre de contexte qui rend la désinfection WordPress exigeante: il faut traiter le symptôme et la cause, sinon le site redevient un endroit accueillant.
Erreurs classiques pendant la désinfection
Pour éviter de perdre du temps, voici les erreurs que je vois le plus souvent, et pourquoi elles posent problème:
- Supprimer uniquement le fichier identifié comme shell, sans vérifier la persistance Résultat: rechute rapide, parce que l’outil de dépôt ou le point d’entrée reste actif. Relancer le site trop tôt, sans verrouiller les accès Résultat: un attaquant peut réinjecter pendant que l’équipe est en train de “finir le nettoyage”. Remplacer WordPress et les plugins, sans vérifier les fichiers ajoutés dans uploads Résultat: vous faites du neuf côté code principal, mais vous laissez un agent dans les médias. Faire un scan “automatique” sans corrélation avec vos logs Résultat: soit trop de faux positifs, soit des trous. Les logs aident à prioriser. Oublier les comptes utilisateurs et les rôles Résultat: un compte admin mal sécurisé peut réimporter et replonger le système.
Ces erreurs ne sont pas “graves” par intention, elles sont graves par conséquence. La désinfection, c’est souvent une course contre le temps, mais une course qui demande une logique.
Comment savoir que la désinfection est réellement terminée
Je n’aime pas “finir” une désinfection WordPress sur une impression. Je préfère des critères simples, vérifiables, et réalistes.
Une fois le nettoyage fait, je surveille:
- que les fichiers malveillants identifiés ne reviennent pas qu’il n’y a pas de nouvelles créations suspectes dans wp-content/uploads que les requêtes anormales observées au début cessent sur une fenêtre de temps raisonnable que les comptes utilisateurs et les rôles restent stables que les erreurs applicatives n’augmentent pas, ce qui pourrait indiquer une fonctionnalité cassée ou une autre altération
Il y a aussi un point de prudence: si l’attaquant a laissé un accès, il peut rester actif même sans déclencher immédiatement. Une surveillance sur plusieurs jours est souvent plus parlante qu’une simple observation sur quelques heures, surtout quand le trafic est faible.
Ce que vous pouvez faire dès maintenant si vous suspectez des shells dans les médias
Si vous êtes au milieu d’une crise, je propose https://gardewp.fr/nettoyage-malware-wordpress/ une approche immédiate et raisonnable, sans vous pousser à supprimer à l’aveugle.
- commencez par sauvegarder proprement, même si vous pensez “c’est trop tard” isolez le site pour limiter les exécutions identifiez les fichiers récents et ceux qui ont des extensions ou noms incohérents recoupez avec les logs, l’URL appelée est une boussole supprimez les composants confirmés, puis verrouillez les causes (mises à jour, comptes, configuration)
Ensuite seulement, revenez progressivement à la normale, en gardant un œil sur wp-content/uploads.
Si vous me donnez quelques informations, je peux vous aider à affiner la méthode: type d’hébergement (Apache ou Nginx, présence de PHP-FPM), version de WordPress, extensions récemment ajoutées, et surtout ce que montrent les logs autour des requêtes suspectes (chemins et dates). Dans ce genre d’incident, deux détails peuvent faire gagner plusieurs heures: l’URL exacte appelée, et les fichiers les plus récents dans wp-content/uploads au moment de la compromission.