Nettoyage malware WordPress : rechercher les cron jobs malveillants

Sur WordPress, un site “piraté” n’a pas toujours l’air d’un cambriolage en plein jour. Parfois, tout fonctionne, les visiteurs voient une page normale, et pourtant quelque chose tourne en arrière-plan, en silence, à intervalles réguliers. C’est là que les cron jobs malveillants prennent toute leur importance.

Quand un attaquant compromet un accès (souvent via un plugin vulnérable, un compte admin repris, ou une faille d’hébergement), il cherche surtout deux choses: persistance et exécution répétée. Le cron est parfait pour ça. Il permet de relancer un script à chaque minute, à chaque heure, ou toutes les quelques secondes, sans que personne n’ait besoin de recharger la page. Et dans WordPress, il existe deux mondes du cron, le “cron système” (celui de votre serveur) et le “pseudo-cron” de WordPress (wp-cron), qui dépend des visites.

Dans le cadre d’un nettoyage malware WordPress, savoir où chercher et quoi interpréter fait gagner des heures. Parce que le plus difficile n’est pas seulement de “supprimer”, c’est d’identifier la logique de l’attaque, puis de vérifier que le site ne continuera pas à recréer la charge utile.

Comprendre les deux cron qui peuvent être en cause

Avant de fouiller dans des fichiers, je conseille de clarifier ce qu’on cherche. Sur un hébergement classique, vous avez rarement “un seul cron”. Vous avez plutôt:

D’un côté, le cron système, configuré via crontab (au niveau utilisateur) ou via des fichiers dans /etc/cron.* selon les systèmes. Lui ne dépend pas des visites. Si le job tourne, il tourne, que votre site ait 10 visiteurs ou 10 000.

De l’autre, wp-cron. WordPress déclenche des tâches planifiées quand quelqu’un visite le site. Si personne ne visite, wp-cron peut retarder ses exécutions. C’est pour ça qu’un malware peut “sembler calme” un moment, puis se révéler après une hausse de trafic, ou après un test de monitoring.

Le point crucial: un malware peut installer un job au niveau système pour appeler un script externe, ou peut s’appuyer sur wp-cron pour déclencher une fonction malveillante dans le contexte de WordPress. Dans les deux cas, l’objectif est le même: exécution répétée.

Premiers signaux qui orientent vers un cron compromis

Avant de toucher au serveur, j’observe toujours ce qui ressemble à une exécution programmée. Les indices sont rarement “parfaits”, mais ils donnent une direction.

Sur des sites nettoyés puis re-pollués, j’ai vu des symptômes très concrets: un pic de trafic vers une URL interne sans raison (par exemple toutes les 5 minutes), des erreurs PHP récurrentes dans les logs, ou des logs serveur qui montrent des appels au même endpoint à intervalles réguliers. Parfois, la seule “preuve” visible est une augmentation de l’utilisation CPU, même quand personne ne modifie le site.

Voici les signaux qui, dans mon expérience, justifient de regarder en priorité le cron:

    répétition d’un même script ou d’une même requête dans les logs à intervalles fixes (minute, 5 minutes, heure, nuit) modifications suspectes de fichiers sur une cadence régulière (exécution qui reconstruit une charge utile) redirections vers des URLs externes, déclenchées de façon intermittente et difficile à reproduire manuellement présence de fichiers “utilitaires” sans utilité claire, déposés avec des noms camouflés (parfois aléatoires) et modifiés après coup alertes de sécurité qui mentionnent des injections via wp-cron.php ou des appels sortants depuis le serveur

Si vous ne voyez rien de tout ça, ça ne veut pas dire qu’il n’y a pas de cron malveillant. Mais cela change l’ordre des actions: on commence par les logs et par les endroits les plus probables, au lieu de “scénariser” trop tôt.

Où chercher les cron malveillants côté serveur (cron système)

Le cron système est souvent le chemin le plus direct pour un attaquant. S’il a accès à un utilisateur sur la machine (par exemple via un compte de type “shell access” ou via une faille qui lui donne la capacité d’écrire des fichiers), il peut programmer un lancement.

Le premier endroit à vérifier dépend de votre environnement. Si vous êtes en mutualisé, les chemins varient, mais l’idée reste identique: inspecter les crontabs de l’utilisateur qui exécute votre site, puis les répertoires de scripts système.

En pratique, je commence par regarder la configuration du cron pour l’utilisateur du serveur web ou pour l’utilisateur qui possède les fichiers WordPress. Sur beaucoup d’hébergeurs, cela peut être www-data, apache, ou un utilisateur spécifique du compte client.

Ensuite, je cherche dans les fichiers chargés de cron. Sur les distributions Linux, vous avez souvent des répertoires comme /etc/cron.d, et des répertoires /etc/cron.daily, /etc/cron.hourly, /etc/cron.weekly, /etc/cron.monthly. Sans vouloir supposer un layout exact, je contrôle aussi les fichiers dans /etc/cron.* car c’est là que des jobs peuvent être déposés discrètement.

Pour inspecter, j’utilise typiquement des commandes comme celles-ci (à adapter à votre contexte et à vos droits):

Crontab -l Crontab -u USERNAME -l Ls -lah /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.weekly /etc/cron.monthly Grep -R "curl\|wget\|php\|bash\|sh" /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.weekly /etc/cron.monthly 2>/dev/null

L’erreur courante, c’est de se limiter à une recherche “curl” ou “wget”. Un malware peut appeler directement php avec un fichier local, exécuter un binaire non évident, ou faire un appel HTTP via un langage différent. Il faut donc élargir la recherche aux commandes typiques d’exécution: php, python, perl, bash, sh, et aussi des chemins vers des scripts dans wp-content, ou des requêtes vers des URLs externes.

Un autre piège: certains cron malveillants ne ressemblent pas à des lignes bruyantes. Ils peuvent lancer un script “de maintenance” caché dans un répertoire inattendu, ou utiliser un nom de fichier qui ressemble à un composant légitime.

Reconnaître les patterns typiques d’un cron malveillant

Une fois que vous https://gardewp.fr/nettoyage-malware-wordpress/ avez la liste des jobs, la qualité du diagnostic dépend de votre capacité à distinguer “automatisation normale” et “persistante”. WordPress ou l’hébergement peuvent installer des tâches légitimes, notamment pour la sauvegarde, la rotation de logs, ou des mises à jour. Donc vous ne cherchez pas uniquement la malveillance, vous cherchez le comportement suspect.

Ce qui attire mon attention:

image

    un cron qui déclenche un binaire ou un script non documenté dans votre workflow habituel un cron qui appelle un PHP via un chemin qui ne correspond pas à un fichier attendu des jobs qui exécutent un téléchargement d’archive externe, puis une extraction dans wp-content un cron qui envoie des données, par exemple via curl vers un domaine inconnu, ou qui fait un POST vers une URL sans lien avec votre projet une cadence très fréquente (toutes les minutes ou plus), surtout si elle ne correspond à aucune contrainte de votre site

Mais surtout, je vérifie les chemins. Un job malveillant a souvent une “empreinte” sur le serveur: un fichier déposé dans un endroit précis, dont la date de modification coïncide avec l’apparition du problème. En comparant la date, vous pouvez relier l’exécution à la création du payload.

Où chercher dans WordPress: wp-cron et ses déclencheurs

Même si le cron système est le plus “coupable” dans beaucoup de cas, wp-cron peut être utilisé comme mécanisme d’exécution répétée. Le malware peut injecter du code dans un thème, un plugin, ou via un mécanisme d’action hook.

La première question à vous poser: est-ce que le site exécute des tâches programmées qui n’ont rien à faire là?

Dans WordPress, wp-cron s’appuie sur des événements planifiés enregistrés en base. Donc un malware peut:

    enregistrer un événement avec un hook custom, associé à une fonction malveillante modifier la façon dont WordPress déclenche ces hooks (via un changement de code) appeler une URL externe depuis une fonction qui s’exécute sur cron

Pour investiguer, j’aime combiner deux approches. La première est de vérifier la présence de traces d’événements anormaux dans la base, la seconde est de regarder les logs d’exécution.

Sans supposer que vous avez des outils spécifiques, vous pouvez examiner les traces d’exécution WP dans vos logs d’accès et d’erreurs, et repérer des appels réguliers à wp-cron.php ou à des endpoints qui provoquent le déclenchement.

Côté code, cherchez des insertions dans des emplacements “classiques”:

    fichiers de plugins et thèmes, surtout si leur modification est récente fichiers qui ajoutent des hooks, comme add_action ou add_filter, vers des événements liés à wp_cron fichiers inconnus déposés dans wp-content/uploads (moins fréquent pour le code, mais possible pour des scripts “proxy”)

Dans un cas que j’ai traité récemment, l’attaque ne se voyait pas dans les pages publiques. Le site semblait normal, puis toutes les 15 minutes, un appel externe partait depuis le serveur, et dans wp-content, un fichier PHP camouflé venait d’être modifié. En remontant le fil, le déclenchement était lié à un hook ajouté via un fichier de plugin “fantôme”, qui s’exécutait précisément au moment où wp-cron lançait un événement planifié.

Vérifier aussi les traces dans les logs (l’étape qui “relie tout”)

Les logs font souvent gagner du temps, parce qu’ils transforment une chasse aux “fichiers suspects” en une enquête structurée. Vous cherchez la cohérence entre:

    un moment d’exécution (heure précise) un job cron ou un déclencheur wp-cron une activité réseau (requête sortante ou téléchargement) une modification de fichiers

Selon votre hébergeur, vous avez des accès aux logs via cPanel, Plesk, ou via des fichiers sur disque. Les noms changent, mais l’idée ne change pas. Je contrôle typiquement:

    logs d’accès web, pour voir les requêtes répétées logs d’erreurs PHP ou du serveur web logs applicatifs si vous en avez (et si votre setup les conserve) logs système si vous y avez accès, comme /var/log/syslog ou des journaux équivalents

Une façon utile de progresser est de rechercher des motifs dans les logs, par exemple les occurrences de wp-cron.php, de cron, de chemins vers des scripts, ou de domaines externes connus pour être associés à des campagnes malveillantes.

Désactiver sans casser: approche de containment pendant l’enquête

Quand on suspecte un cron malveillant, la tentation est forte de “supprimer tout de suite”. C’est parfois nécessaire, mais ça peut compliquer l’enquête si vous effacez la preuve avant de comprendre.

Dans un nettoyage malware WordPress sérieux, je privilégie souvent un compromis: contenir, couper l’exécution répétée, tout en conservant les éléments utiles pour confirmer l’origine.

Concrètement, si vous identifiez un job cron système suspect, vous pouvez le désactiver temporairement, en sauvegardant la ligne incriminée. Idem pour wp-cron: si vous repérez un hook ou un événement planifié qui déclenche le payload, vous pouvez le neutraliser sans forcément supprimer tout WordPress d’un coup.

Ensuite seulement, vous faites le ménage: suppression du payload, nettoyage des modifications de fichiers, réinstallation ciblée des composants compromis, rotation des identifiants si nécessaire.

Ce qui m’importe, c’est d’éviter un cycle de recontamination: si vous supprimez le fichier, mais que le cron est toujours là, il peut le recréer à la prochaine exécution. Et là, vous perdez des heures à “nettoyer”, puis constater que c’est revenu.

Procédure pragmatique pour rechercher les cron malveillants (sans tout casser)

Voici la logique que j’applique sur des incidents réels, en adaptant à votre niveau d’accès et à votre hébergeur. L’objectif est de réduire le bruit, tout en couvrant les zones à risque.

Je commence par obtenir un intervalle de temps utile: quand l’infection a commencé, ou quand vous avez observé les symptômes. Si le site a des accès aux logs, je me cale sur une fenêtre d’une à deux journées, parfois plus si les volumes sont faibles.

Ensuite, je fais un premier tri sur les jobs cron système, puis sur les déclencheurs wp-cron. Je recoupe avec les logs web pour voir si des appels réguliers existent, puis je reviens à la base de données et au code pour confirmer le mécanisme.

Une fois l’origine identifiée, je contiens et je documente: copie des fichiers suspects, capture des lignes cron concernées, et relevé des hooks ou événements wp-cron anormaux. La documentation est utile même si l’incident est “simple”, car elle permet de vérifier que le nettoyage a bien coupé la persistance.

Enfin, je supprime et je purge. Puis je teste le déclenchement, en surveillant une période au moins égale à la cadence du job malveillant suspecté (si c’est toutes les 5 minutes, je surveille pendant au moins 15 à 30 minutes; si c’est toutes les heures, au moins une journée). Le temps de vérification évite de croire trop vite.

Exemple concret: quand tout semblait normal, sauf le serveur

Sur un site vitrine WordPress, aucune page ne menait vers une redirection. Les mises à jour plugins étaient à jour, mais le site était hébergé sur une configuration où l’accès au shell avait été accordé par erreur lors d’une prestation précédente.

Le seul symptôme visible était une consommation CPU qui montait à une fréquence régulière. Les logs d’erreurs montraient des appels PHP hors contexte, puis des requêtes sortantes vers un domaine sans lien avec le projet.

En inspectant le cron système de l’utilisateur concerné, j’ai trouvé un job très discret, déclenché toutes les 5 minutes, qui lançait un script PHP placé dans un sous-dossier inattendu sous wp-content. Le script téléchargeait un nouvel ensemble de fichiers, puis relançait un autre composant.

La partie la plus instructive a été le “pattern de recontamination”. Même après suppression manuelle du fichier principal, la prochaine exécution du cron remettait tout en place. C’est uniquement quand on a désactivé le cron, puis supprimé la chaîne complète (et corrigé la cause d’accès initiale) que l’incident a cessé.

C’est typiquement ce qui arrive quand on ne cible que les fichiers. Les cron jobs sont un mécanisme de persistance, donc ils doivent être neutralisés en priorité.

Deux erreurs fréquentes pendant le nettoyage

La première erreur est de confondre “présence d’un cron” et “malveillance”. Un hébergeur peut installer des scripts de sauvegarde ou de maintenance qui ressemblent à des commandes, parfois avec curl (pour un ping de santé) ou avec php (pour exécuter un script interne). Sans recouper, vous risquez de modifier quelque chose de légitime.

La seconde erreur est de ne pas vérifier la cadence. Si un cron malveillant tourne toutes les 15 minutes, un test sur 3 minutes peut vous donner une fausse impression de propreté. J’ai déjà vu des nettoyages “validés” trop tôt, puis re-déclenchés le lendemain à l’heure exacte du job.

Plan de vérification après suppression

Une fois le nettoyage malware WordPress effectué (fichiers, plugins, thèmes, identifiants, éventuelle base), je valide que la persistance est réellement coupée. La validation doit ressembler à une vérification, pas à une impression.

Voici comment je m’organise, avec peu d’outils mais une logique solide:

    surveiller les logs pendant une fenêtre couvrant au moins trois exécutions plausibles du cron (ou une journée si cadence faible) rechercher les occurrences dans les logs d’accès de wp-cron.php, et vérifier que les appels restent ceux attendus par WordPress vérifier que les fichiers suspects ne reviennent pas et que leurs dates de modification ne “rebondissent” pas confirmer que les lignes cron systèmes suspectes sont absentes ou désactivées de façon non réversible sans action volontaire tester les fonctionnalités planifiées légitimes du site (si vous utilisez des événements WP, formulaires, export, ou tâches custom)

Le point important, c’est de garder le “quoi surveiller” concret. Sinon, on finit par regarder trop large, ou trop tard.

Cas particulier: quand wp-cron est utilisé comme proxy

Un autre scénario que j’ai rencontré: l’attaquant ne met pas de cron système. Il s’arrange pour déclencher wp-cron par des requêtes répétées. Ainsi, il “pousse” WordPress à exécuter des tâches à une fréquence contrôlée, même si wp-cron dépend du trafic.

Résultat: vous voyez des appels fréquents à wp-cron.php dans les logs d’accès, venant parfois de mêmes IP, parfois d’un bloc d’adresses, parfois avec des patterns ressemblant à du scraping.

Dans ce cas, les cron jobs malveillants ne se trouvent pas forcément dans crontab. Ils se trouvent dans:

    un code injecté qui s’exécute au passage du hook un événement planifié ajouté dans la base parfois un endpoint ajouté pour faciliter l’escalade

Le nettoyage doit donc couvrir le code et les événements, pas seulement la configuration.

Sécuriser ensuite: empêcher que le cron malveillant revienne

Le nettoyage ne sert à rien si l’accès initial n’est pas corrigé. Un cron malveillant revient très facilement si vous laissez une porte ouverte, même une petite.

Je cible en général trois axes, sans entrer dans une liste interminable:

D’abord, la source d’accès. Si un mot de passe admin a été compromis, il faut le changer, désactiver les sessions, et vérifier les comptes utilisateurs et les rôles. Si l’attaque provenait d’un plugin vulnérable, la mise à jour et le retrait du plugin sont indispensables.

Ensuite, les vecteurs d’exécution. Beaucoup d’attaques se nourrissent de fichiers uploadables ou de mécanismes qui peuvent être abusés. Selon votre setup, durcir les autorisations sur les répertoires d’upload, limiter l’exécution de PHP dans uploads, et contrôler les fichiers éditables depuis l’admin.

Enfin, la surveillance. Un simple envoi d’alertes sur modification de fichiers sensibles et sur exécution cron inhabituelle peut éviter de découvrir l’incident trop tard. Même si vous ne pouvez pas tout automatiser, une vérification régulière, plus une rotation rapide des mots de passe, réduit l’impact.

Comment vous savez que vous avez vraiment trouvé le cron malveillant

La réponse la plus honnête est qu’on ne “sait” jamais à 100% sur un incident complexe. On démontre plutôt que le mécanisme de persistance a été neutralisé.

Dans le cas des cron jobs, la preuve pratique est assez concrète: vous coupez la ligne cron suspecte et vous observez que les scripts ne sont plus relancés à la cadence habituelle. Si le job est wp-cron lié, vous coupez l’événement ou le hook malveillant, puis vous attendez le déclenchement naturel et vous vérifiez que le comportement disparaît dans les logs.

image

Ce cycle, attendre et confirmer, est plus fiable que “supposer”. Et il évite aussi les nettoyages qui ont l’air beaux en surface, mais qui reviennent dès que le prochain créneau arrive.

Si vous bloquez: questions à vous poser avant d’aller plus loin

Si, après vos recherches, vous ne trouvez aucun cron système suspect et aucune anomalie évidente dans wp-cron, il reste des zones possibles: des scripts lancés par un autre mécanisme que cron (tâches d’hébergement, systemd timers, watchdog applicatif, jobs de sauvegarde détournés), ou une infection qui déclenche via des événements web (rechargements de page, endpoints d’API, faux webhooks). Ce n’est pas fréquent, mais ça arrive.

Avant de partir dans tous les sens, je recommande de revenir aux faits: quels moments correspondent à la charge malveillante? Sur quel intervalle? Quels logs montrent l’activité? Souvent, cette précision suffit à retrouver le déclencheur.

Rechercher les cron jobs malveillants, c’est surtout apprendre à lire le temps. Sur un site WordPress compromis, la malveillance s’exprime souvent dans la répétition, et dans la capacité à se relancer sans intervention humaine. En recoupant cron système, wp-cron, logs et dates de modification, vous réduisez considérablement la zone d’incertitude, et vous faites un nettoyage malware WordPress qui tient dans le temps, pas seulement un nettoyage “visuellement correct”.