Nettoyer site WordPress infecté : analyser les cron jobs et tâches suspectes

Quand un site WordPress se met à distribuer des scripts bizarres, rediriger vers des pages externes, ou simplement afficher des liens “fantômes” dans le front, on pense d’abord au thème, aux plugins, au compte administrateur. C’est normal. Pourtant, dans une bonne partie des cas, le vrai pilote automatique n’est pas dans le fichier PHP principal. Il est ailleurs, dans l’ombre, au niveau des tâches planifiées.

Les cron jobs, les tâches du planificateur d’hébergement, et les mécanismes équivalents côté WordPress peuvent servir à maintenir une compromission. Le malware n’a pas besoin d’être actif en permanence, il lui suffit de se réinstaller, de retoucher la base, ou de déposer du code à intervalle régulier. Voilà pourquoi, pour nettoyer un site WordPress infecté, l’analyse des cron jobs et des tâches suspectes est souvent la piste la plus rentable.

Pourquoi les cron jobs sont un “terrain de chasse” privilégié

Un cron job, c’est un déclencheur. Il ne “hacke” pas. Il exécute. Si quelqu’un parvient à faire exécuter une commande à votre insu, il peut automatiser des actions qui passeraient inaperçues si vous ne surveilliez que la page d’accueil.

Sur WordPress, l’automatisation prend plusieurs formes :

    au niveau système, via le cron de Linux ou l’équivalent chez votre hébergeur ; via des commandes déclenchées par le planificateur de l’hébergement (cPanel, Plesk, “Scheduled Tasks”, “Cron Jobs”) ; via le scheduler interne à WordPress (WP-Cron), ou des plugins qui utilisent WP-Cron pour exécuter des tâches régulières.

Le piège, c’est que WP-Cron n’est pas un “vrai” cron système. Il dépend du trafic. Vous https://gardewp.fr/nettoyage-malware-wordpress/ pouvez donc avoir un comportement intermittent, surtout si le site reçoit peu de visites. C’est le genre de détail qui explique pourquoi une infection semble “disparaître” puis revient, précisément au moment où une tâche planifiée se déclenche.

J’ai déjà vu des sites où tout semblait propre côté fichiers, puis une boucle de réinstallation se produisait toutes les 15 minutes. Les journaux n’étaient pas très bavards, mais les cron du compte d’hébergement contenaient deux entrées discrètes, avec un binaire masqué dans un répertoire temporaire. En corrigeant ça, le problème a cessé immédiatement, et le nettoyage “manuel” a enfin tenu.

Les signaux qui doivent vous pousser à regarder les tâches planifiées

Avant même d’ouvrir votre interface d’hébergement, cherchez des symptômes cohérents avec une automatisation. Ce n’est pas une règle absolue, mais certaines classes de comportements reviennent souvent.

Voici ce qui m’a le plus souvent mis sur la piste des cron jobs :

image

    redirections vers des URLs externes qui reviennent après un “reset” temporaire ; fichiers modifiés à intervalle régulier, même quand vous n’avez rien déployé ; accès “tardifs” aux pages de téléchargement ou aux scripts, au moment où personne n’est censé y toucher ; base de données qui se “réécrit” régulièrement (options modifiées, champs bizarres ajoutés) ; apparition de fichiers dans des répertoires inattendus (uploads, caches, tmp), avec des dates proches des symptômes.

Si vous observez au moins deux de ces points, prenez les tâches planifiées au sérieux. Cela ne veut pas dire que tout vient du cron. Mais ça justifie un examen méthodique.

Préparer l’analyse : réduire le risque et gagner du temps

Avant de modifier quoi que ce soit, j’aime bien sécuriser le terrain. Vous allez toucher à des éléments qui déclenchent des commandes. Une mauvaise action peut relancer l’infection, ou empirer la trace.

Concrètement, si vous pouvez, faites ces opérations dans cet ordre :

    lancez une copie de sauvegarde complète (fichiers et base) si vous avez un workflow propre ; identifiez l’heure approximative des symptômes (même une fourchette, comme “autour de 2h”, aide beaucoup) ; coupez temporairement la capacité du site à exécuter des scripts dangereux si vous êtes déjà en mode “contenant” (par exemple en mettant le site en maintenance), le temps de faire l’inventaire.

Le but n’est pas de figer le site pour toujours. C’est de vous donner une fenêtre pour lire, comparer, et décider sans que des tâches continuent à semer des fichiers.

Où chercher les cron jobs sur un hébergement classique

La manière exacte dépend de votre hébergeur. Mais dans la pratique, il y a trois endroits où je regarde presque systématiquement.

D’abord l’interface du panneau de contrôle, quand elle existe. Sur cPanel, on trouve souvent une section “Cron Jobs” liée aux utilisateurs. Sur Plesk, c’est “Tools” puis “Scheduled Tasks” ou équivalent. Sur des hébergements plus simples, vous avez un formulaire “Add Cron Job”.

Ensuite, sur un serveur avec accès SSH, je vérifie les crontabs :

    la crontab de l’utilisateur (fichiers spool sous l’environnement de l’utilisateur) ; les cron côté root s’il y a accès, ce qui est plus rare en mutualisé.

Enfin, certains hébergeurs n’exposent pas la crontab directement. Ils branchent un système d’ordonnancement interne. Dans ce cas, vous retrouvez surtout des journaux d’exécution et des configurations dans l’interface.

Si vous n’avez ni SSH ni accès développeur, vous pouvez quand même faire beaucoup : lire la configuration affichée dans le panneau, consulter les journaux accessibles, et comparer l’historique des modifications via des outils de détection.

WP-Cron : quand la tâche planifiée se cache dans WordPress

WP-Cron est souvent la source de confusion. Il n’exécute pas un cron “système”, il déclenche des événements WordPress quand quelqu’un visite le site. Un plugin compromis peut utiliser WP-Cron pour exécuter une fonction périodique.

En lecture, WP-Cron se matérialise notamment par des “événements” stockés dans la base. Dans certaines infections, l’attaquant crée un événement qui :

1) s’exécute toutes les X minutes selon une fréquence qui ne saute pas aux yeux ; 2) télécharge un script depuis une source externe ; 3) modifie un template, un plugin ou une option ; 4) supprime ensuite sa trace si possible.

Le problème pratique, c’est que vous ne verrez pas forcément un “cron job” dans le panneau d’hébergement si la source du déclenchement est WP-Cron. Pourtant, l’impact est le même, des actions automatiques, à intervalle régulier.

Méthode d’analyse : lister, dater, comparer, confirmer

L’erreur classique consiste à “désactiver tout”. Je l’ai vue et je l’ai fait au début aussi. Désactiver au hasard peut casser des tâches légitimes (sauvegardes, mises à jour automatiques, purge de cache) et vous priver d’indices.

Je préfère une méthode en quatre temps, assez simple à appliquer même sans accès avancé.

1) Lister les tâches planifiées visibles

Dans votre panneau d’hébergement, copiez la configuration exacte des cron jobs, avec leurs horaires, leurs commandes, et leur utilisateur cible. Si vous avez accès à la ligne de commande, récupérez l’équivalent brut.

Petit point d’hygiène : gardez une trace, même dans un document. Une fois que vous commencez à “nettoyer site WordPress infecté”, vous allez faire des modifications. Vous devez pouvoir revenir en arrière si vous coupez quelque chose d’utile.

2) Dater les symptômes par rapport aux horaires

Reliez les symptômes à un intervalle. Exemple concret : si le site redirige toutes les 20 ou 30 minutes, cherchez un cron avec un “ /20” ou “/30” dans l’expression. Si c’est autour d’une heure précise, cherchez “0 2 * * *” ou équivalent.

Sur WP-Cron, le déclenchement peut dépendre du trafic. Si le site a peu de visites, l’événement peut s’exécuter en retard, puis rattraper ou s’exécuter au prochain passage.

3) Comparer les commandes avec une base “attendue”

Une commande de cron normale pour WordPress ressemble à un appel à un binaire WordPress ou à un script interne de maintenance. Elle peut lancer WP-CLI, déclencher wp-cron.php, ou exécuter une purge de cache.

À l’inverse, une commande suspecte ressemble souvent à :

    un téléchargement via curl ou wget ; un exécutable en base64 ou une suite d’instructions en ligne ; un “chmod +x” suivi d’une exécution depuis un répertoire temporaire ; une chaîne qui commence par “sh -c”, “bash -c”, ou qui pipelise plusieurs commandes.

Le langage exact varie, mais la signature “exécution de pipeline” est souvent un drapeau rouge.

4) Confirmer côté fichiers et logs

Une fois que vous avez une liste de suspects, vous pouvez valider en recoupant avec :

    les fichiers modifiés récemment (uploads, wp-content, répertoires temporaires) ; les répertoires contenant des exécutables ou scripts inattendus ; les logs d’erreurs d’exécution cron si disponibles.

Si votre hébergeur fournit un journal des cron, lisez-le sans le parcourir trop vite. Le plus précieux est souvent le message d’erreur ou la ligne d’invocation, qui peut révéler le chemin réel de script.

Exemples typiques de tâches suspectes

Sans tomber dans le “ça ressemble à ça, donc c’est ça”, je peux vous donner des cas qu’on rencontre souvent. L’objectif est de développer votre œil, pas de vous faire cocher une case.

image

    Un cron qui exécute un script PHP dans un dossier non standard, par exemple un chemin sous un répertoire temporaire ou sous un nom qui ne ressemble pas à un plugin. Une tâche qui appelle wp-cron.php, mais avec un paramètre anormal ou un environnement préparé avant l’appel (variables d’export, changements de permissions). Une commande qui télécharge une archive puis la déplie, puis supprime le fichier source. Ce cycle est très fréquent dans les compromissions où l’attaquant veut minimiser la persistance apparente.

Dans un cas que j’ai traité, la commande semblait “courte”, mais elle enchaînait quatre opérations. Le cron faisait une requête HTTP vers une URL générique, redirigeait vers un fichier, puis exécutait un shell script depuis un nom aléatoire. Sur le moment, on pourrait croire à une simple mise à jour. En regardant le contenu de la commande complète, il n’y avait aucun rapport avec WordPress.

Désactiver sans aveugler : ce que je fais avant de toucher au cron

Quand je repère une tâche suspecte, je ne la supprime pas immédiatement. Je la mets d’abord en mode “test contrôlé”, si l’interface permet une désactivation temporaire.

Cela permet de vérifier deux choses :

1) est-ce que les symptômes cessent réellement dès le prochain créneau ; 2) est-ce que d’autres tâches étaient en cascade.

Car il arrive qu’un attaquant crée plusieurs tâches, certaines pour persister et d’autres pour se réinstaller. Si vous coupez la mauvaise, l’infection peut rebondir via une autre entrée.

Le trade-off est simple : la désactivation temporaire est plus lente mais plus sûre. La suppression directe est plus rapide, mais vous pouvez perdre la preuve ou casser un mécanisme qui vous aidera à comprendre l’attaque.

Une mini-checklist de triage (courte, mais efficace)

Si vous voulez une grille rapide pour ne pas vous noyer, utilisez ceci comme filtre pendant la lecture des tâches. (Et gardez votre liste de cron à portée de main.)

    repérez les commandes qui contiennent curl, wget, php téléchargé, base64, ou des appels “sh -c” ; vérifiez si la commande pointe vers un chemin qui n’existe pas ou qui vient d’apparaître récemment ; comparez les horaires des tâches à l’intervalle observé des redirections ou des changements de contenu ; cherchez les tâches qui modifient les permissions (chmod, chown) avant d’exécuter ; contrôlez si WP-Cron (événements) planifie des actions à une fréquence inhabituelle.

Si plusieurs cases sont positives, vous avez presque toujours un plan d’action clair.

WP-Cron : identifier l’événement responsable

Côté WordPress, l’objectif est d’identifier l’événement déclenché par WP-Cron, puis de comprendre quel code ou quel plugin l’a créé.

Sur une installation WordPress compromise, les événements peuvent être persistants dans la base. Vous pouvez souvent les lister via des outils internes ou via un plugin d’inspection, mais selon votre situation, vous pouvez aussi :

    inspecter la base pour les entrées de cron internes ; examiner la trace d’exécution dans les logs WordPress si le debug est activé (à condition que ce soit compatible avec votre niveau de contrôle) ; relier l’événement à un hook (nom de l’action planifiée) et trouver où ce hook est invoqué.

Le point délicat, c’est que le nom de l’événement peut être générique, et le code malveillant peut être déguisé en plugin légitime. L’arme la plus fiable reste le recoupement : quel fichier a été modifié, quel hook existe, quel plugin est apparu, et à quelle époque.

Le piège du “faux positif” sur WP-Cron

Des plugins légitimes lancent des tâches. Cache, optimisation, génération de sitemap, contrôle de mise à jour, maintenance. Le vrai danger est de confondre “tâche régulière” et “tâche malveillante”.

Dans mes expériences, une tâche WP-Cron malveillante a souvent trois caractéristiques en commun : elle déclenche des modifications de contenu ou d’options sans lien fonctionnel avec votre site, elle exécute un chargement externe, ou elle crée des fichiers dans des répertoires inattendus.

Si votre WP-Cron déclenche uniquement des recalculs d’images ou de cache, ce n’est probablement pas l’origine. Si la tâche est associée à un script externe, là oui.

Lecture des commandes : les détails qui trahissent l’attaque

Les cron jobs et commandes peuvent être longues et compressées, surtout quand l’attaquant a voulu rendre la ligne “discrète”. Il y a pourtant des motifs récurrents.

Regardez le “chemin” autant que la commande :

    si la commande exécute un script dont le chemin n’est pas sous votre arborescence WordPress normale ; si elle mentionne un répertoire de type tmp, cache custom, ou un nom de dossier aléatoire ; si elle s’appuie sur un binaire qui n’appartient pas à WordPress (par exemple un outil installé côté serveur) ; si elle modifie des permissions sur des fichiers PHP.

Même sans comprendre toute la logique, ces éléments font pencher la balance.

Traitement : comment neutraliser, puis nettoyer proprement

Une fois que vous avez identifié la tâche suspecte, le nettoyage a deux phases : neutraliser l’exécution automatique, puis nettoyer la persistance.

La neutralisation passe par :

    désactivation ou suppression contrôlée du cron job système suspect ; suppression des événements WP-Cron liés à l’action malveillante ; désactivation temporaire des plugins non fiables si vous suspectez leur rôle dans la création d’événements.

Ensuite vient le nettoyage :

    supprimer les fichiers déposés par la tâche ; restaurer les fichiers WordPress et plugins à partir d’une source de référence (version attendue) ; purger les caches si nécessaire ; refaire un contrôle de la base si des options ont été modifiées.

Il y a une nuance importante : parfois la tâche planifiée ne fait que redéployer du contenu à partir d’une source externe. Si vous nettoyez sans bloquer le cron, la persistance revient. Dans l’autre sens, si vous coupez le cron mais laissez des fichiers injectés, vous pouvez avoir un site “fonctionnel” mais encore compromis. D’où l’enchaînement.

Cas particulier : quand le cron n’est pas dans l’interface

Il arrive que l’interface ne liste rien, mais que des exécutions existent quand même. Dans ce cas, il faut élargir.

Sur des environnements plus complexes, la tâche peut être déclenchée par :

    un service de l’hébergement non exposé directement ; un script lancé au démarrage ou par un mécanisme interne ; des appels via un fichier .htaccess ou des scripts d’interception, puis une commande côté WordPress (moins fréquent, mais possible).

Si vous avez accès à l’environnement serveur et que la situation est sérieuse, l’analyse par logs et par timestamps de fichiers devient encore plus centrale. Si vous n’avez pas accès, vous vous appuyez sur l’inventaire des fichiers (à la recherche de modifications récentes) et sur l’inspection des tâches WordPress.

Sécurisation après nettoyage : empêcher la réapparition

L’étape que beaucoup de gens font trop vite, c’est la post-mortem. Vous pouvez nettoyer parfaitement les cron et les fichiers visibles, mais si la compromission initiale vient d’un mot de passe faible, d’une faille de plugin non mise à jour ou d’un compte compromis, la prochaine tâche planifiée peut apparaître ailleurs.

Après le nettoyage, j’insiste sur trois axes :

1) comptes : réinitialiser les mots de passe, vérifier les rôles, supprimer les comptes inconnus, activer 2FA quand c’est possible ; 2) surface : mettre à jour WordPress, thèmes, plugins, supprimer ce qui n’est pas utilisé ; 3) monitoring : vérifier les cron jobs de manière régulière, et garder une trace des changements (au moins au niveau fichiers et options si vous avez un outil).

Un bon comportement, c’est aussi de contrôler qui peut modifier le site. En mutualisé, les accès FTP et l’accès panneau sont souvent des portes d’entrée.

Comment savoir que vous avez réellement fini

La question “c’est propre maintenant ?” mérite une réponse factuelle. Dans ce contexte, les critères que j’utilise sont concrets :

    les symptômes ne reviennent pas au prochain créneau d’exécution (selon l’horaire identifié) ; les fichiers déposés par la tâche ne réapparaissent pas ; aucun nouveau cron suspect n’apparaît ; WP-Cron ne repopule pas l’événement malveillant ; l’inspection des plugins et thèmes montre un état attendu (pas de fichiers ajoutés, pas de modifications incohérentes).

Sur des sites peu visités, WP-Cron peut mettre plus longtemps à “révéler” une persistance. Si vous savez que l’événement se déclenche via trafic, pensez à simuler un minimum de requêtes ou à surveiller sur plusieurs cycles.

Si vous tenez un journal pendant le nettoyage, même en copiant des timestamps, vous gagnez en sérénité. Une infection, surtout par cron, a un rythme. Le rythme, c’est votre allié.

image

Aller plus loin, sans tomber dans la chasse aux sorcières

Il y a deux risques opposés : soit vous coupez tout sans comprendre, soit vous cherchez pendant des heures un seul cron alors que la persistance est ailleurs. La solution pratique, c’est de traiter le cron comme un centre de décision, puis de relier chaque suspect à un effet.

Quand vous analysez les tâches, posez-vous une question simple : “Si cette tâche s’exécute, quel dommage est plausible sur mon site ?” Puis vous vérifiez s’il y a des fichiers, des options, ou des changements qui correspondent à cette logique.

Si vous appliquez ce raisonnement, vous réduisez les faux positifs, et vous arrivez plus vite à une correction durable.

Si vous voulez, décrivez votre contexte (type d’hébergement, accès SSH ou non, présence d’un panneau type cPanel ou Plesk, et le symptôme exact : redirection, injection de contenu, spam, téléchargement de fichiers). Je pourrai vous dire quoi vérifier en priorité, comment interpréter les lignes de cron, et comment distinguer un cron légitime d’une persistance malveillante dans votre cas.