Symptômes · 11 min · publié le 22 juin 2024

Fichiers PHP inconnus à la racine : radio.php, wp-core.php, images.php

`radio.php`, `wp-core.php`, `images.php`, `about.php` : des noms anodins pour des webshells. On note les dates de modification, on copie, on ne supprime pas encore. Ces fichiers datent souvent l'entrée plus clairement qu'un access.log déjà rotaté.

Réponse directe

Des noms anodins cachent des webshells. Notez les dates de modification. Ne les supprimez pas avant d'avoir copié : ils indiquent souvent l'heure d'entrée.

fichier php inconnu racine radio.php malware wp-core.php backdoor

Des noms qui veulent passer pour du métier

L'attaquant n'appelle pas son shell `hack.php`. Il reprend le vocabulaire du CMS : `wp-core.php`, `wp-includes.php` (fichier, pas dossier), `xmlrpc.php.bak`, `css.php`, `radio.php`, `fw.php`. Un photographe voit `images.php` et pense à un script de galerie. C'est souvent un gestionnaire de fichiers.

Les extensions mentent aussi : `.php.jpg` se traite à part (double extension). À la racine, le `.php` tout court domine. Un `.phtml`, `.php5`, `.pht` apparaît quand le serveur est réglé pour les exécuter.

Un nom aléatoire de 8 caractères (`a7k2qm.php`) est plus honnête : il ne se cache pas. Il se mélange aux backups. Triez par date, pas par alphabet — l'alphabet enterre `z.php` sous `wp-cron.php`.

Lister la racine autrement qu'à l'œil

Le gestionnaire de fichiers du panel trie mal, cache les fichiers qui commencent par un point, et n'affiche pas la taille. Passez par SFTP ou `ls -la --time-style=long-iso`. Exportez la liste (nom, taille, mtime) dans un tableur. C'est le constat de la racine.

Cherchez aussi un cran en dessous : `wp-admin/about.php` trop récent, `wp-includes/images.php`, `cgi-bin/`. La « racine » du dossier web n'est pas le seul étage. Un shell dans `wp-admin` s'appelle souvent comme un fichier cœur.

Les archives : `backup.zip`, `old.tar.gz` à la racine sont téléchargeables. Même « propres », elles exposent `wp-config`. Traitez-les comme un incident parallèle : retirez-les du webroot après copie hors serveur.

  • Liste nom / taille / date, pas un coup d'œil FTP.
  • Fichiers dot (`.php`, `.user.ini`).
  • Étages `wp-admin`, `wp-includes`, `cgi-bin`.
  • Archives et dumps SQL publics.

Dates : le premier geste utile

Un `index.php` WordPress pèse quelques kilo-octets et date de la dernière MAJ cœur. Un `index.php` de 180 Ko modifié un dimanche à 4 h, pendant que le reste du cœur date de mars, est le dossier index trop lourd. Notez toutes les mtime de la fenêtre suspecte.

Plusieurs fichiers à la même minute : même session d'upload. Cette minute est l'heure d'entrée si les logs FTP l'alignent. Gardez-la pour l'assureur et pour chercher le compte FTP utilisé.

Attention au `touch` : un attaquant peut reculer une date. La taille et le contenu restent. Une date « 2018 » sur un fichier qui contient un domaine de 2026 ne trompe que le tri rapide.

mtime n'est pas ctime. Sur certains panels, « modifié » est l'un ou l'autre. Demandez-vous ce que la colonne affiche vraiment.

Copier avant d'effacer

Le fichier est une preuve et parfois la seule horloge. Un zip hors serveur, un hash si vous savez le faire. Puis isolation (renommer en `.off`) plutôt que delete immédiat si vous n'avez pas encore la copie.

L'ouvrir sur votre PC de tous les jours a un risque (macro, PHP local). Lisez-le comme du texte, pas en l'exécutant. Un `eval(base64_decode` suffit à classer « porte » sans tout comprendre. Voir code base64.

Ne l'envoyez pas par e-mail à toute l'équipe en pièce jointe exécutable. Un lien vers le zip de preuve sur un drive interne, ou le prestataire via un canal prévu. Voir accès prestataire.

Ce qui est légitime à la racine

WordPress : `index.php`, `wp-config.php`, `xmlrpc.php`, `wp-cron.php`, `wp-load.php`, `wp-settings.php`, `wp-blog-header.php`, `wp-links-opml.php`, `wp-mail.php`, `wp-signup.php`, `wp-trackback.php`, `wp-activate.php`, plus les `wp-admin/` `wp-includes/` `wp-content/`. Un `app.php` Symfony, un `index.php` Presta, un `cron.php` Magento : selon le CMS.

Un `health.php` posé par l'agence, un `deploy.php` oublié, un `phpinfo.php` : légitimes d'intention, dangereux en ligne. `phpinfo` et `deploy` sortent après incident, même s'ils n'étaient pas le shell.

En cas de doute, comparez à une install vierge de la même version. Tout ce qui n'y figure pas est suspect jusqu'à preuve d'un développeur nommé.

Un fichier, rarement une seule porte

Le shell à la racine est le salon. L'entrée est un plugin, un mot de passe, un voisin. Retirer `radio.php` et garder l'admin fantôme, le fichier revient. Cherchez dans la même session : utilisateurs, mu-plugins, cron, `.htaccess`.

Un webshell connu (C99, WSO) a une signature ; un one-liner n'en a pas. Ne vous arrêtez pas parce que Wordfence n'a flaggé que `radio.php`. Voir webshell C99.

Les permissions 777 sur la racine expliquent l'écriture. Les remettre à 755 / 644 après, pas « pour voir » pendant que vous cherchez encore — vous casserez l'upload légitime au mauvais moment. Après le tri.

Comparer au zip du CMS

Téléchargez le zip WordPress / Presta de la version exacte (`wp-includes/version.php`, Presta `settings.inc`). Comparez les fichiers cœur. Un `xmlrpc.php` modifié est plus grave qu'un `radio.php` : il se fait passer pour officiel à chaque scan visuel.

Ne remplacez pas tout le cœur « à l'aveugle » sans copie : vous perdez un mu-plugin utile, un drop-in (`object-cache.php`). Remplacez fichier par fichier ce qui diverge du zip, après avoir zipé l'état actuel.

Le thème et `wp-content` ne sont pas dans le zip cœur. Autre passe, autre méthode. Voir fichiers du thème.

Après le retrait : réécriture et cron

Attendez une nuit. Si `radio.php` réapparaît à la même minute, le cron ou le shell restant l'écrit. C'est le test, pas un scan vert. Surveillez la liste de racine à J+1 et J+7.

Changez le panel avant de vous féliciter. Un FTP encore ouvert republie le fichier en 30 secondes. Invalidez les clés, les users FTP ajoutés.

`site:votre-domaine.fr/radio.php` : si Google a indexé le shell, l'URL doit passer en 404. Une inspection Search Console confirme. Pas de réexamen Safe Browsing pour un PHP déjà 404, sauf si d'autres URL dangereuses répondent encore 200.

Quand Google ou Chrome s'en mêle

Si le shell servait un kit (banque, impôts), les visiteurs ont vu une page, pas un listing de fichiers. Traitez comme phishing hébergé : couper l'URL d'abord. Safe Browsing : 24 à 72 h après site réellement propre.

Un simple shell non visité par le public peut ne jamais allumer Chrome. Ce n'est pas un feu vert. C'est « pas encore signalé ».

Vous pouvez déclarer le site. En attendant : liste de racine, copies, panel. Ne commencez pas par un cleaner qui efface sans liste.

  • Liste horodatée de la racine.
  • Copies hors serveur.
  • Retrait + recherche des autres portes.
  • Contrôle réapparition 24 h / 7 j.

Questions fréquentes

Un fichier `wp-config.php.bak` à la racine, c'est un shell ?

+
Pas forcément un shell : c'est souvent une copie de secrets en téléchargement libre. Retirez-le du webroot. Lisez-le : s'il contient un payload en plus des defines, les deux problèmes sont là.

Le gestionnaire de fichiers ne montre pas `radio.php` mais l'URL répond. Pourquoi ?

+
Cache du panel, autre docroot (`www` vs `public_html`), ou fichier servi par une règle. Téléchargez l'URL, comparez au listing SFTP. Un CDN peut aussi servir une vieille copie : videz-le.

Puis-je tout supprimer sauf wp-content ?

+
Vous casserez le CMS et garderez souvent la porte (mu-plugin, uploads). Comparez, n'atomisez pas.

Wordfence a « réparé » le fichier. C'est fini ?

+
Il a peut-être remis un cœur officiel. L'entrée et le cron n'ont pas bougé. Vérifiez la réapparition et les admins.

Faut-il formater le VPS ?

+
Si le mineur a touché le système hors webroot, parfois oui. Sur un mutualisé, vous n'avez pas cette option : vous nettoyez le compte. Un format sans comprendre le cron applicatif ne suffit pas non plus sur un VPS.
À lire ensuite
index.php trop lourd Webshell C99 WSO Code base64 dans les fichiers Must-use plugin caché Déclarer mon site