Site WordPress infecté : inspecter les uploads et médias

Un site WordPress infecté laisse rarement des traces uniquement dans la console d’administration. Quand l’attaque est passée par les failles du thème, un mot de passe réutilisé, une installation de plugin “nul mais pratique”, ou une campagne plus directe, la première chose que l’on voit souvent, ce sont des symptômes côté contenu. Pages qui redirigent, formulaires qui “disparaissent”, fichiers qui apparaissent dans des répertoires où on ne devrait jamais rien trouver, et surtout des scripts qui se cachent dans la zone média.

Le dossier uploads est un terrain de jeu très apprécié par les attaquants. Il est vivant, il contient des images, des PDF, des documents, parfois des fichiers importés, parfois des “thumbnails”. Et surtout, WordPress y écrit régulièrement sans que l’on y pense. Cela rend l’inspection à la fois nécessaire et délicate: on doit distinguer le normal du suspect, et ne pas casser un site en voulant “nettoyer vite”.

Je vais partir du concret: comment inspecter les fichiers et médias au bon niveau, quels pièges éviter, et comment interpréter ce que l’on trouve. L’objectif n’est pas seulement de repérer une infection, mais de remonter à ce qui l’a déclenchée, et d’éviter que le problème revienne dès que l’on restaure.

Pourquoi les uploads et médias sont souvent le point faible

WordPress stocke les médias dans wp-content/uploads/ avec une structure par année et mois, comme wp-content/uploads/2024/07/. Les attaquants aiment ce répertoire pour plusieurs raisons:

    Il est largement toléré par les workflows, backups et CDN. Les équipes regardent moins souvent ce dossier que wp-content/plugins/. Les extensions “bizarres” peuvent passer inaperçues si la médiathèque n’est pas filtrée ou si certaines routes permettent l’accès direct. Des webshells et loaders peuvent se cacher dans des fichiers dont l’apparence ne fait pas peur au premier coup d’œil, par exemple un fichier “image” qui n’en est pas une.

Ce n’est pas automatique, mais dans beaucoup d’incidents réels, le répertoire uploads devient le premier endroit où l’on retrouve un comportement anormal, soit parce que l’attaquant y dépose des fichiers, soit parce que le site “s’auto-modifie” après l’infection initiale.

Je me souviens d’un cas où l’on voyait des redirections vers un domaine de publicité et un pic de requêtes, sans modification évidente dans les plugins. L’inspection a révélé un ensemble de scripts cachés au milieu de documents PDF et d’images compressées. Rien d’exotique en apparence, et pourtant c’était eux qui déclenchaient la suite.

Préparer l’inspection, sans aggraver la situation

Avant de toucher au serveur, il faut établir un cadre. Sur un site WordPress infecté, la priorité est de préserver des preuves. “Nettoyer” trop vite peut effacer ce qui permettrait de comprendre le vecteur d’entrée, donc de corriger la cause.

Dans la pratique, je recommande de travailler ainsi:

Copier le répertoire wp-content/uploads/ tel quel sur une machine d’analyse, ou au minimum lister le contenu et récupérer les fichiers suspects. Ne touchez pas aux fichiers pendant que vous mesurez. Vérifier l’horodatage. Si un fichier est apparu “entre deux heures”, cela donne des indices sur le moment de l’attaque. Capturer l’environnement: version de WordPress, versions de thèmes et plugins, et surtout la config Nginx ou Apache liée aux médias.

Le piège fréquent, c’est de lancer des opérations lourdes en production. Par exemple, si vous déclenchez un scan antiviral qui remplace des fichiers, vous pouvez casser des liens, des références d’images ou provoquer un comportement étrange sur les pages qui chargent des médias. L’inspection doit être d’abord non destructive.

Comprendre ce que vous cherchez dans les uploads

Quand on inspecte uploads, il ne suffit pas de regarder le nom de fichier. Les attaquants jouent sur plusieurs niveaux: l’extension, le contenu, l’emplacement, et parfois des chemins “logiques” qui n’auraient aucun sens pour une médiathèque.

Les indicateurs qui reviennent souvent sont:

    Des fichiers ajoutés dans des sous-dossiers qui n’ont pas de raison d’être. Par exemple wp-content/uploads/2024/07/cache/ ou wp-content/uploads/2024/07/tmp/. Des fichiers avec extensions inattendues, comme .php, .phar, .phtml, .cgi, .pl, .sh, .js ou des variantes. Des “faux” médias: un fichier nommé .jpg ou .png mais contenant du code exécutable ou des séquences typiques de PHP. Des tailles incohérentes par rapport aux autres médias. Un “jpg” de quelques kilo-octets peut être un fichier qui n’a pas été généré comme un média standard, surtout si le site publie habituellement des images de 100 Ko à quelques Mo. Des modifications répétées: le même fichier revient, se régénère, ou de nouveaux clones apparaissent.

Ces signaux ne prouvent pas à eux seuls une exécution. Ils prouvent une anomalie. Pour conclure, il faut croiser avec l’endroit où le code est appelé, et avec les logs.

Inspection “à l’œil” et par métadonnées, étape par étape

L’inspection commence souvent par une simple extraction de liste. Sur un site WordPress actif, la médiathèque peut contenir des milliers de fichiers. On ne peut pas tout lire, mais on peut filtrer intelligemment.

Une approche qui marche bien consiste à combiner trois angles:

    Dates: repérer les fichiers créés ou modifiés récemment, surtout si vous avez une fenêtre temporelle de l’incident. Extensions: faire la chasse aux extensions exécutables, et aussi aux extensions ambiguës. Tailles et entropie “suspecte”: les fichiers chargés de code compressé ou encodé peuvent être très “pleins” ou au contraire anormalement petits.

Vous pouvez ensuite ouvrir les candidats dans un environnement isolé et chercher dans leur contenu des motifs fréquents dans les webshells et loaders. Pour un fichier qui devrait être une image, cherchez des signatures PHP du type <?php, eval(, base64_decode(, gzinflate(, des chaînes qui ressemblent à des URL ou des commandes système.</p>

Je préfère procéder ainsi: d’abord repérer, ensuite vérifier le contenu, enfin tenter de comprendre comment cela s’exécute.

Le point clé: comprendre comment un fichier dans uploads peut s’exécuter

Beaucoup de gens supposent que “uploads est un répertoire de médias, donc il ne peut pas exécuter”. Ce n’est vrai que dans des conditions de serveur précises.

Sur WordPress, l’exécution dépend de la configuration web. Par exemple:

    Apache peut exécuter certains fichiers selon les directives AddHandler et la configuration des répertoires. Nginx peut être configuré pour refuser l’exécution, mais si une directive location spécifique laisse passer, certains fichiers peuvent être traités comme du code. Certaines attaques exploitent des chemins d’exécution indirecte, ou des endpoints qui relaient le contenu.

Donc, si vous trouvez des fichiers .php dans uploads, la question n’est pas seulement “sont-ils là ?”, c’est “sont-ils appelés et interprétés ?”.

Une façon pragmatique de trancher consiste à croiser la date de création des fichiers avec des logs d’accès HTTP. Même avec un niveau de logs minimal, vous pouvez souvent repérer des requêtes de type:

    accès direct à un fichier dans uploads requêtes avec des paramètres qui ressemblent à des “loader” pics sur des endpoints qui ne changent pas d’habitude

Si vous n’avez pas de logs serveur, les logs WordPress peuvent parfois montrer des appels, mais l’essentiel des demandes directes vers des fichiers média n’apparaît pas toujours côté WordPress.

Vérifier la cohérence avec la base de données

Même si vous inspectez les fichiers, WordPress garde aussi la trace de ce que la médiathèque référence. Il est utile de comparer le contenu du dossier avec https://gardewp.fr/ les entrées de la base.

Concrètement, dans wp_posts, les médias sont liés à des entrées de type attachment, et dans wp_postmeta vous trouvez des champs comme '_wp_attached_file', qui stocke le chemin relatif du fichier dans uploads.

Si vous voyez des fichiers présents sur disque mais absents de la base, vous avez deux scénarios fréquents:

    l’attaquant a déposé des fichiers “hors médiathèque” l’attaquant a modifié des structures, puis a effacé des entrées pour masquer

À l’inverse, si des entrées existent dans la base mais que les fichiers ont une taille ou un contenu anormal, vous pouvez avoir une modification après upload.

Je garde aussi en tête un risque: manipuler la base en production est rarement une bonne première action pendant une crise. Faites des exports, et travaillez sur une copie quand c’est possible.

Signaux typiques de charge malveillante dans les médias

Voici les patterns que je rencontre le plus souvent lors d’incidents où le dossier uploads est impliqué. Ce sont des repères, pas des preuves absolues.

    Fichiers avec extension “image” contenant des motifs de code, par exemple une balise PHP au début ou une logique qui lit un paramètre GET. Fichiers très courts mais “actifs”, dont la taille ne correspond pas aux autres médias du site. Appels “tordus” à des classes PHP ou fonctions système, souvent encodés (base64, rot13, gzip), avec ensuite une exécution via eval ou inclusion. Des fichiers dont le nom ressemble à un hash, mais dont le contenu est clairement un loader. Des variantes de fichiers clonés, identiques à une exception près, comme si un outil automatisé avait déposé plusieurs charges en test.

Si vous avez un doute, testez de manière contrôlée. Ouvrir un fichier suspect sur un navigateur n’est pas toujours une bonne stratégie, surtout si vous craignez une exécution côté serveur. Dans un environnement d’analyse, l’objectif est de lire le contenu sans déclencher quoi que ce soit.

Méthode de triage rapide: ce que je fais en premier

Sur un incident, le temps manque. Pourtant, si vous ne triez pas, vous allez vous noyer dans l’existant. Je fais d’abord un triage basé sur la différence, pas sur la totalité.

Une première phase simple consiste à regarder l’historique d’apparition dans uploads, puis à cibler les fichiers récents et les extensions anomalies. L’idée n’est pas de scanner 100% des fichiers, mais de réduire l’espace de recherche.

Voici le genre de mini check que j’utilise quand il faut avancer vite:

    Lister les fichiers modifiés ou créés après le dernier moment “sain” connu (mise à jour, sauvegarde, dernier scan). Filtrer immédiatement les extensions potentiellement exécutables ou ambigües. Repérer les fichiers “image” dont le contenu ne correspond pas à leur type attendu. Estimer la cohérence des tailles par rapport aux autres médias du même type sur le site.

Une fois cette base faite, je passe au diagnostic du contenu et à la recherche d’un lien avec les redirections ou l’activité malveillante.

Quand l’attaque utilise des images comme vecteur

Un point qui surprend certains propriétaires, c’est que des médias peuvent jouer un rôle dans https://gardewp.fr/nettoyage-malware-wordpress/ une chaîne d’exécution. Dans la plupart des cas, un fichier image ne s’exécute pas par lui-même. Mais il peut:

    contenir du code dans des sections qui seront lues par un script ailleurs servir de “cible” pour une requête qui déclenche un traitement via un endpoint être chargé par du JavaScript malveillant qui est lui injecté dans un autre endroit

Donc, même si uploads contient des images “suspectes”, vous ne devez pas supposer que l’image est le moteur principal. Souvent, c’est un marqueur, un payload secondaire, ou une pièce du puzzle.

C’est là que l’inspection doit être corrélée. Le site redirige vers une page précise à une heure donnée, vous retrouvez un fichier qui a été créé peu avant, puis vous trouvez dans ce fichier un chemin qui ressemble à l’endroit où l’attaque se branchait. Sans corrélation, on risque de nettoyer un symptôme et de laisser la cause.

Deuxième angle: les thèmes et plugins peuvent référencer des médias infectés

Un autre piège: l’infection n’est pas forcément déposée dans uploads. Parfois, uploads est la destination, mais la logique d’exécution est dans un plugin ou un thème compromis, et elle pointe ensuite vers un média malveillant.

Par exemple, un fichier peut contenir un script qui charge une ressource depuis uploads via wp_remote_get, ou une fonction qui inclut un fichier local, ou une redirection conditionnelle basée sur le User-Agent.

C’est pourquoi, au-delà du dossier média, il faut garder les yeux ouverts sur:

    les fichiers modifiés récemment dans wp-content/themes/ et wp-content/plugins/ les ajouts de code dans des fichiers PHP qui ne servent pas normalement à la génération d’assets les schémas d’injection qui construisent des URLs vers des dossiers média

Je l’ai vu plusieurs fois: “les uploads sont propres” en apparence, mais un plugin compromis pointe vers un fichier qui existe dans uploads. Ou l’inverse: des fichiers malveillants existent dans uploads, mais aucun fichier PHP ne les appelle, ce qui peut indiquer une infection incomplète ou déjà neutralisée.

Outils d’inspection et limites concrètes

Sans citer d’outils exotiques, l’essentiel tient dans trois catégories: recherche de contenu, analyse de structure, et vérification d’accès.

    La recherche de contenu par motifs dans les fichiers suspects aide à détecter du code encodé ou des hooks. L’analyse de structure du serveur (permissions, mapping de types MIME, règles d’exécution) explique si un fichier peut être interprété comme du code. Les logs (serveur et, selon le cas, WordPress) relient une présence sur disque à un événement observable.

La limite, c’est que les scans basiques peuvent donner de faux positifs. Par exemple, un fichier texte importé peut contenir des chaînes qui ressemblent à du code, sans être exécutable. Ou un export de formulaire peut contenir des segments encodés. Il faut donc travailler avec des critères d’exécution et des corrélations, pas uniquement avec des “matchs” de signatures.

Protéger le site après l’inspection: ne pas rester dans le pansement

Inspecter et nettoyer, c’est bien. Empêcher la réinfection, c’est indispensable.

Dans la zone uploads, les protections concrètes sont parfois plus simples qu’on ne le pense, mais elles dépendent fortement du serveur. Certaines mesures sont universelles dans l’esprit:

    Réduire ce qui peut être déposé et exécuté. Si votre serveur exécute un type de fichier dans uploads, il faut revoir la configuration. Filtrer les extensions autorisées dans WordPress, côté serveur si possible. Durcir l’accès à la médiathèque et aux comptes admins.

Sur WordPress, le vrai cœur est la cause initiale: un plugin vulnérable, un thème abandonné, une configuration d’identification faible, une mauvaise hygiène de mots de passe. Si vous nettoyez les médias sans corriger le point d’entrée, vous reverrez les mêmes dossiers se remplir.

Une stratégie de suppression prudente

Quand on identifie des fichiers malveillants, la suppression totale “à l’aveugle” est tentante. En incident, j’ai déjà vu des nettoyages qui ont cassé des fonctionnalités légitimes, parce que les fichiers suspects ressemblaient à de vrais assets et que la corrélation manquait.

Ma pratique est plus prudente:

    d’abord isoler: déplacer les fichiers suspects hors des chemins publics, ou les mettre dans un répertoire temporaire non servi ensuite vérifier s’ils sont appelés via logs seulement après, supprimer ou réintroduire des versions saines si nécessaire

Cette approche évite de supprimer des éléments légitimes par erreur, et elle laisse le temps d’identifier l’endroit où le code était déclenché.

image

Cas fréquents et ce que ça implique

Il y a quelques cas typiques qui se présentent souvent. L’important est d’en tirer une décision logique.

Premier cas: vous trouvez des .php dans uploads, avec du code visible, et les logs montrent des requêtes vers ces fichiers. Là, c’est relativement direct: suppression et durcissement côté serveur.

Deuxième cas: vous trouvez des fichiers “image” contenant du code, mais les logs n’affichent aucune requête vers ces médias. Cela peut signifier que l’attaque a cessé, ou que le déclenchement passe par une autre chaîne (un script ailleurs qui scanne ou qui charge). Dans ce cas, on investigue plus, on corrèle, on ne se contente pas de jeter.

Troisième cas: uploads est propre, mais vous voyez du JavaScript malveillant injecté dans des pages, ou des redirections. L’infection est ailleurs, uploads n’est peut-être qu’un bruit.

Quatrième cas: vous voyez beaucoup de fichiers récemment ajoutés, mais aucun n’a de contenu évident. Cela arrive quand l’attaquant utilise des techniques de déploiement plus complexes, ou quand les charges sont encodées et ressemblent à des blobs. Là, l’analyse de contenu et la corrélation logs comptent plus que la lecture directe.

Vérifier aussi les “uploads” liés à l’import et aux flux

WordPress peut recevoir des médias via des importations, des flux RSS, ou des intégrations. Les attaques savent exploiter ces canaux.

Si votre site importe des images depuis une URL, un attaquant peut forcer l’introduction de fichiers “sous le masque”. Si votre site convertit des contenus (PDF en images, par exemple), le pipeline peut être un levier.

Dans les situations où des médias “anormaux” apparaissent sans cause claire côté interface, je regarde toujours:

    les plugins d’import ou d’optimisation média les tâches planifiées (WP-Cron) qui manipulent les fichiers les endpoints qui récupèrent des ressources à distance

Même si le dossier uploads est la cible finale, le vecteur peut être ailleurs dans la chaîne.

Durcissement ciblé pour limiter les récidives

Après un nettoyage, je recommande de revoir la stratégie de durcissement, avec un focus sur ce qui a un impact réel sur l’espace uploads.

Sans entrer dans des recettes toutes faites, les points qui changent la donne sont:

    empêcher l’exécution de types de fichiers non nécessaires dans uploads limiter les droits d’écriture au strict nécessaire réduire la surface d’administration exposée sur Internet, et protéger l’accès aux comptes tenir à jour thème, plugins, et WordPress lui-même, sans retarder les mises à jour de sécurité

Il y a aussi une dimension organisationnelle. Si vos uploads “grossissent” sans justification, créez un point de contrôle interne. Un suivi mensuel du nombre de nouveaux fichiers et du type d’extensions peut sembler basique, mais en incident, les chiffres font gagner des heures.

Comment savoir si vous êtes vraiment sorti de l’infection

La tentation est de conclure dès que “plus rien ne redirige”. Ce n’est pas toujours un bon critère, surtout si l’attaque était conditionnelle, par exemple basée sur:

    l’IP le navigateur un chemin précis un agent utilisateur

Pour se rassurer, je cherche trois confirmations:

    l’absence de nouvelles traces côté uploads sur une fenêtre de temps cohérente avec l’attaque précédente l’absence de requêtes suspectes dans les logs correspondant à l’activité observée la stabilité du site, y compris sur les pages qui chargent des médias

Le détail qui compte souvent: certaines charges ne se manifestent qu’après un certain temps, ou seulement quand une ressource est appelée. Donc un “tout va bien” immédiat peut être trompeur.

Une façon structurée d’examiner uploads sans se perdre

Si vous devez mener l’enquête sur un site WordPress infecté, je propose une méthode de triage qui tient sur une logique simple: repérer, vérifier, corréler, puis décider. Et quand vous arrivez à un fichier suspect, ne cherchez pas uniquement “est-ce du code?”, cherchez surtout “est-ce exploité?”.

Voici les signaux de corrélation que je considère comme particulièrement utiles, car ils orientent vers la cause:

    Le fichier suspect a été créé ou modifié juste avant la période d’incident observée. Des requêtes HTTP visant ce fichier apparaissent dans les logs. Le fichier contient des éléments typiques d’un loader, d’un décodeur ou d’une redirection conditionnelle. D’autres fichiers du même lot montrent des patterns cohérents (mêmes chaînes, même structure). WordPress ou un plugin modifié référence des chemins dans uploads.

Quand plusieurs points convergent, la décision devient claire, et le nettoyage a plus de chances d’être définitif.

Derniers pièges à éviter

Deux erreurs reviennent souvent quand on inspecte les uploads et médias.

La première: confondre “gros dossier” et “infection”. Un site de e-commerce, un site média, ou un site d’annonces peut accumuler beaucoup de fichiers, parfois avec des sous-dossiers temporaires légitimes. Il faut comparer à votre normal, pas à une idée abstraite de ce que “devrait être” uploads.

La deuxième: supprimer sans comprendre. Un fichier malveillant est rarement isolé, et une chaîne d’infection implique souvent d’autres changements dans le système, même si le symptôme principal se situe dans les médias. Nettoyer uploads sans vérifier plugins, thèmes, utilisateurs compromis et tâches planifiées, c’est ouvrir la porte à la récidive.

Pour aller plus loin, mais garder les pieds sur terre

Au moment où vous inspectez wp-content/uploads/, vous êtes dans le concret. C’est une bonne position, parce que les preuves matérielles sont là: fichiers, dates, contenus, et parfois des patterns réutilisés par les attaquants. Mais la qualité du diagnostic dépend de votre capacité à relier ce que vous voyez à ce qui se passe côté navigateur et côté serveur.

Si vous ne deviez retenir qu’une règle, ce serait celle-ci: inspecter les médias, oui, mais toujours avec des critères de corrélation. Le dossier uploads peut être le véhicule, l’hébergement, ou un leurre. La différence entre ces trois cas se joue souvent dans les logs et dans les références croisées.

Si vous avez un incident en cours, vous pouvez commencer par isoler les fichiers les plus récents, filtrer les extensions, vérifier le contenu des médias anormaux, puis chercher les requêtes correspondantes. Ensuite seulement, vous décidez du nettoyage définitif et du durcissement. C’est plus lent au départ, mais c’est beaucoup plus rapide au final, parce que vous évitez le cycle “nettoyer, vérifier, re-nettoyer” qui fatigue tout le monde.