Désinfection WordPress : supprimer les scheduled actions malveillantes

Un site WordPress compromis ne ressemble presque jamais à un film catastrophe avec un écran rouge et des alertes spectaculaires. Le plus souvent, c’est banal. Le front reste “visuellement normal”, les pages se chargent, et pourtant quelque chose tourne en arrière-plan. Ce détail est justement ce qui rend la désinfection WordPress particulièrement délicate : quand on ne regarde que les fichiers, on peut passer à côté des tâches programmées qui continuent à exécuter des actions malicieuses, jour après jour.

Les “scheduled actions” (dans WordPress, on parle souvent de WP-Cron, d’événements planifiés via Action Scheduler ou d’autres mécanismes proches) sont un moyen très courant pour les attaquants de maintenir la persistance. Même si vous supprimez les fichiers webshell ou les scripts injectés, une tâche planifiée peut re-générer ces fichiers, réécrire des options, ou pousser du spam via des requêtes distantes. Résultat : vous nettoyez, puis le site “revient” à l’identique quelques heures plus tard.

Je vais vous guider à travers une approche pragmatique, orientée terrain, pour identifier et supprimer les scheduled actions malveillantes, tout en évitant deux pièges classiques : casser un plugin légitime et faire une “désinfection” incomplète qui laisse une minuterie derrière.

Pourquoi les scheduled actions sont l’arme de l’ombre

WP-Cron n’est pas un planificateur “système” au sens strict, c’est un mécanisme interne qui se déclenche selon les visites et le trafic. Beaucoup de plugins s’en servent pour exécuter des tâches régulières : sauvegardes, mises à jour de caches, synchronisations, envois de newsletters, calculs de statistiques.

Quand un attaquant obtient un accès, il peut profiter de cette architecture pour programmer une action qui s’exécute sans que vous le voyiez. L’action peut :

    lancer un script externe (appel HTTP) vers un serveur contrôlé par l’attaquant, modifier des options WordPress (base de données), créer des comptes administrateurs, injecter des contenus ou redirections, ou déclencher d’autres étapes d’une chaîne.

L’important, c’est que vous pouvez ne rien remarquer côté fichiers. Les événements sont parfois enregistrés dans la base de données, pas dans le système de fichiers. Donc, une vérification “juste” des répertoires du https://gardewp.fr/ thème et des plugins ne suffit pas.

J’ai déjà vu un cas où l’analyse des fichiers a mis en évidence un petit greffon “sale” dans un dossier de plugin, supprimé proprement. Le site semblait fonctionner, puis, deux jours après, un pic de trafic bizarre est apparu, accompagné de requêtes similaires sur la même URL interne. La cause n’était pas le fichier supprimé, mais une action programmée qui se ré-exécutait et reconstruisait la persistance.

Ce qu’on cherche vraiment : cohérence et traces

Avant de toucher au système de planification, adoptez une logique simple : une action planifiée “normale” s’intègre à votre stack. Une action malveillante a souvent un profil décalé.

Dans les environnements réels, on retrouve fréquemment des indicateurs comme :

    un hook dont le nom ne correspond à aucun plugin ou script de votre installation, une fréquence excessive ou des exécutions en boucle, un déclenchement à des heures “qui n’ont pas de sens” par rapport à vos pratiques, une action qui tente d’appeler un endpoint externe ou d’exécuter une fonction rare, des entrées qui reviennent après suppression.

Le point clé, c’est la base de données. Même quand les entrées sont visibles via un écran d’administration ou un plugin de diagnostic, elles finissent souvent par être stockées dans des tables. Si vous supprimez sans comprendre où elles vivent, vous risquez de les recréer. Ou pire, vous supprimez ce qui maintient le bon fonctionnement de votre site.

Cartographier le terrain sans casser l’existant

Le réflexe que je recommande est de préparer un “plan de bascule”, même si vous êtes seul à faire la réponse à incident.

1) Faites une sauvegarde complète avant toute suppression. Selon votre contexte, cela peut être un export SQL + copie des fichiers, ou un snapshot hébergeur.

2) Identifiez quels plugins utilisent réellement des scheduled tasks chez vous. Certains plugins d’optimisation, de sécurité, de cache, ou des solutions de sauvegarde programment des événements. 3) Vérifiez l’état de WP-Cron et la fréquence d’exécution. Si votre site reçoit peu de trafic, WP-Cron peut se comporter de façon surprenante. En environnement “staging”, on voit parfois des événements qui s’accumulent puis s’exécutent d’un coup. Dans ce cas, attribuer la cause à la malveillance serait une erreur de jugement.

Un détail utile : si vous avez accès à l’administration, notez le comportement observé. Est-ce que le spam apparaît pendant une fenêtre précise ? Est-ce que des redirections se déclenchent uniquement après que certaines actions planifiées ont tourné ? Les événements planifiés donnent parfois un rythme.

Les scénarios les plus fréquents de scheduled actions malveillantes

Selon la méthode d’attaque, les scheduled actions se déclinent souvent en trois grands scénarios.

1) Reprogrammation après nettoyage des fichiers

Vous supprimez le plugin infecté, mais des actions reviennent. Cela pointe vers une persistance “DB first”, où l’attaquant enregistre un événement qui re-génère ou reconfigure quelque chose. Le plus frustrant est que vous pouvez répéter le nettoyage plusieurs fois sans comprendre l’origine.

2) Exécution silencieuse d’une fonction de type “hook”

Les actions planifiées déclenchent un hook ou une méthode. Même si vous ne voyez pas de fichier “malveillant” obvious, l’action appelle une fonction qui agit sur la base, par exemple en modifiant une option ou en insérant du code dans un endroit inattendu.

3) Appels réseau sortants

Parfois, l’action planifiée fait un appel à un serveur distant, pour récupérer une charge utile ou envoyer des données. Dans ce cas, même sans voir de contenu malveillant dans vos dossiers, vous pouvez observer des logs d’URL ou des traces côté serveur. C’est un signal fort.

Le traitement dépend du scénario, mais la logique reste la même : identifier précisément les événements, évaluer leur légitimité, puis les retirer.

Où les scheduled actions sont visibles et comment les lire

L’affichage dépend de la façon dont votre WordPress est configuré et du type exact de scheduled actions. Sans supposer votre setup, je vous donne des pistes qui fonctionnent dans la majorité des installations.

    Des événements WP-Cron peuvent être listés via des plugins de monitoring, ou via des outils qui interrogent la base de données. Certains systèmes comme Action Scheduler (très utilisé par WooCommerce et d’autres extensions) ont leurs propres tables et écrans. D’autres tâches peuvent être stockées via des options, des transients, ou des entrées dans des tables d’automatisation.

Sur le plan “terrain”, le plus efficace est de combiner un diagnostic applicatif (ce que vous voyez dans WordPress ou un plugin) avec une vérification côté base. Cela évite de supprimer une action “vue” mais pas réellement utilisée, ou de rater une entrée stockée mais non listée correctement.

Je me méfie particulièrement des plugins de “diagnostic” trop génériques. Ils peuvent afficher des éléments, mais pas toujours donner le contexte. Une bonne désinfection passe par une lecture attentive des détails de l’événement : nom d’action, arguments, horodatage, récurrence.

Identifier les actions suspectes sans tout effacer

Le réflexe “supprimer tout ce qui ressemble à une tâche” est tentant, surtout quand on est pressé. Pourtant, c’est le meilleur moyen de casser le site. Des plugins de sécurité, de sauvegarde, ou de facturation peuvent dépendre d’événements planifiés.

Voici la façon dont je procède en pratique, en restant méthodique et sans listes interminables.

D’abord, je crée une fenêtre d’observation. Si possible, je compare l’état avant et après : combien d’événements correspondent à un hook précis, à quelle fréquence, et à quel moment. Ensuite, je filtre mentalement selon l’écosystème : quel plugin a un hook qui correspond à la tâche ? Si aucun, c’est un drapeau rouge.

Ensuite, je regarde les arguments. Un argument peut contenir une URL, un identifiant de fichier, ou un contenu encodé. Un événement malveillant peut inclure des chaînes inattendues, par exemple des chemins relatifs bizarres ou des paramètres qui n’ont aucun sens pour votre configuration.

Enfin, je vérifie la cohérence de l’horodatage. Une action malveillante peut se programmer “tout de suite” après l’intrusion, puis se répéter. Si vous avez des logs d’accès ou des événements d’administration, vous pouvez parfois corréler.

Mini check avant suppression

    Vérifiez la disponibilité d’une sauvegarde (fichiers + base) Confirmez le type de planification (WP-Cron, Action Scheduler, ou autre) Repérez le hook ou l’action exacte, puis lisez ses arguments Comparez avec l’existant, quel plugin l’utilise réellement Planifiez une fenêtre de test, puis surveillez après modification

Cette petite discipline vous évite des “désinfections” qui cassent davantage qu’elles ne réparent.

Supprimer les scheduled actions malveillantes : approche par étapes

Le but n’est pas seulement de “désactiver”, c’est de retirer la persistance et de s’assurer qu’elle ne réapparaît pas.

image

Étape 1 : stopper la machine pendant l’enquête

Si votre site est encore accessible et subit des effets (spam, redirections, charge CPU), je recommande une mise en pause temporaire côté sécurité. Selon votre niveau d’accès, cela peut passer par :

    une réduction temporaire du trafic, une mise en mode maintenance, ou un blocage contrôlé de certaines actions.

L’idée est d’éviter que des événements planifiés continuent d’agir pendant que vous analysez. Dans beaucoup de cas, ça fait gagner des heures, parce que vous évitez des cycles “analyse puis action puis re-infection”.

Étape 2 : supprimer les événements ciblés, pas l’ensemble

Quand vous avez identifié un hook ou une action suspecte, supprimez-la ou désactivez-la précisément. Selon l’outil que vous utilisez, la suppression peut être “soft” (désactivation) ou “hard” (suppression des enregistrements).

Je préfère la suppression ciblée. La désactivation complète de tout un système planificateur peut être trop risquée, surtout si votre site dépend de tâches légitimes (cron de sauvegarde, nettoyage de transients, purge de cache, import de données).

Étape 3 : vérifier la persistance immédiate

Juste après suppression, vérifiez deux choses :

    l’événement n’apparaît plus dans la liste, et le comportement anormal disparaît ou diminue.

Si l’événement revient, c’est un signe que l’attaquant a un mécanisme qui re-crée les entrées. Dans ce cas, la suppression “ponctuelle” ne suffit pas. Il faut revenir aux sources : analyse des scripts, des hooks ajoutés dans le thème, des tâches qui exécutent du code à intervalle, ou des comptes qui peuvent reconfigurer la base.

Étape 4 : corriger la cause racine

La suppression d’événements malveillants sans retirer la cause est comme enlever le bruit sans traiter la casse. Les causes racines varient :

    plugin modifié, thème modifié, compte administrateur frauduleux avec capacité de changer la configuration, code injecté dans un hook, ou accès à des endpoints internes.

Sur un incident sérieux, je traite souvent le sujet en triade : fichiers + base + comptes.

Couper le cycle : comptes compromis et droits

Une action planifiée malveillante ne surgit pas de nulle part. Si elle est reprogrammée, il est probable que quelqu’un (ou quelque chose) ait accès au système au moment où vous nettoyez.

C’est là que les comptes entrent en jeu. Un attaquant peut :

    créer un compte administrateur, ajouter des rôles à un utilisateur existant, modifier des préférences ou options qui influencent la planification, ou mettre à jour un plugin pour remettre la charge.

Dans la pratique, quand je traite une désinfection WordPress orientée scheduled actions, je vérifie systématiquement :

    la liste des utilisateurs, y compris ceux créés récemment, les changements de rôles, l’historique d’authentification si vos logs le permettent, et l’existence de nouveaux plugins ou modifications de thème.

Si vous supprimez seulement l’événement malveillant, mais qu’un compte compromis reste en place, ce compte peut reconfigurer la base et re-créer la tâche planifiée.

Cas particulier : le “malware qui se cache” dans les paramètres

Un détail qui m’a surpris sur plusieurs incidents : certains scheduled actions semblent inoffensifs à première vue. Le hook ressemble à quelque chose de normal. Pourtant, les arguments contiennent une URL de rappel ou un contenu encodé.

Dans ce cas, vous ne devez pas vous arrêter au nom. Les noms de hooks peuvent être trompeurs, surtout si l’attaquant utilise des appels “génériques” comme des fonctions WordPress publiques, ou s’il s’appuie sur des bibliothèques déjà présentes.

Approche conseillée : lisez les arguments et cherchez des motifs concrets :

    URLs externes, chemins de fichiers inhabituels, chaînes encodées, identifiants de tâches qui n’ont rien à voir avec votre application.

Si vous avez un doute, vous pouvez mettre l’événement en observation plutôt que le supprimer immédiatement, mais je ne recommande pas de “laisser tourner” longtemps une action dont vous suspectez une charge.

Comment être sûr d’avoir fini

Une désinfection WordPress qui “a l’air finie” est le genre de situation qui se retourne contre vous. Il faut une méthode de validation.

À l’échelle d’un site, je valide souvent avec trois angles, chacun avec un seuil pragmatique : 1) Fonctionnel : aucune redirection anormale, pas de pages modifiées, pas de création de comptes inattendue.

2) Technique : les scheduled actions suspectes n’existent plus et ne reviennent pas après un nouveau cycle d’exécution. 3) Observabilité : les journaux (erreurs PHP, accès serveur, logs applicatifs si disponibles) n’indiquent plus d’activité récurrente corrélée à votre fenêtre de compromis.

Le point important est le “nouveau cycle”. WP-Cron dépend des visites. Si vous faites la suppression un jour où le site reçoit peu de trafic, la revalidation peut être trompeuse. Idéalement, vous déclenchez un peu d’activité applicative, ou vous forcez un cycle de cron si votre hébergeur ou votre configuration le permet.

Les erreurs fréquentes qui prolongent l’incident

Je vois souvent les mêmes erreurs, même chez des personnes très compétentes.

D’abord, supprimer uniquement le plugin ou le fichier, sans retirer la tâche planifiée. Vous nettoyez la scène, mais le projecteur continue de tourner.

Ensuite, supprimer toutes les actions sans comprendre leur rôle. Cela peut casser des fonctionnalités, et vous serez tenté de restaurer depuis une sauvegarde plus ancienne, ce qui relance l’incident.

Autre erreur, fréquente sur les environnements avec staging : corriger le staging, oublier le site production, puis appliquer un diff incomplet. Comme les scheduled actions sont stockées en base, la synchronisation n’est pas toujours automatique.

Enfin, ignorer les droits. Si un compte compromis reste admin, il pourra réinjecter des actions. Même si vous retirez la charge initiale, la persistance peut être recréée.

Un exemple concret de traitement (sans blabla)

Sur un site d’entreprise, le comportement était très subtil. Le site ne semblait pas défiguré, mais des pages “marketing” commençaient à renvoyer des requêtes vers des domaines non liés. Les fichiers du thème n’étaient pas modifiés de manière évidente. Les plugins de sécurité affichaient parfois des alertes sur des “changements de configuration”, mais rien de bloquant.

Le vrai tournant a été l’examen des scheduled actions dans la base. Il y avait une entrée planifiée récurrente avec un hook étrange, exécuté toutes les heures environ. Les arguments pointaient vers une URL interne, qui à son tour déclenchait une récupération de code. C’est là que le classique “on a supprimé le fichier, donc c’est réglé” n’a pas marché : l’action se chargeait de la suite.

La correction a consisté à supprimer l’événement suspect, puis à vérifier que l’entrée ne revenait pas après une nouvelle fenêtre de déclenchement cron. Ensuite, on a trouvé un compte récemment créé, avec un rôle élevé. Une fois ce compte désactivé et le plugin modifié remis à l’état propre, l’incident ne s’est pas reproduit.

Ce qui compte dans ce type de cas, c’est la chaîne complète : persistance puis exécution. Les scheduled actions sont souvent la première pièce de la chaîne à casser.

Durcir pour que ça ne se reproduise pas

Une fois la désinfection terminée, vous pouvez rendre votre WordPress moins attractif pour ce type de persistance. Sans promettre l’impossible, on peut réduire les angles morts.

Je pense d’abord à la réduction de surface : plugins à jour, suppression des thèmes et extensions inutiles, nettoyage des comptes, et durcissement des droits. Ensuite, l’observabilité : des alertes sur les modifications de fichiers et sur les changements d’options. Et enfin, une stratégie de sauvegarde testée, avec une restauration réaliste. Si vous devez revenir en arrière, vous voulez le faire rapidement et sans flou.

Il y a aussi une leçon concrète sur le cron. Dans certains contextes, une configuration plus déterministe du planificateur et une limitation des tâches inutiles rendent les comportements anormaux plus faciles à détecter.

Quand demander de l’aide, et quoi fournir

Il existe des situations où la désinfection manuelle devient trop risquée, notamment si vous n’arrivez pas à corréler le comportement anormal, ou si vous suspectez une compromission plus large (par exemple une prise de contrôle partielle via le serveur, pas seulement WordPress).

Avant de solliciter un spécialiste, préparez des éléments :

    la date approximative du début du problème, la description du comportement (spam, redirections, appels externes, charge CPU), votre liste de plugins et thèmes, et idéalement des extraits liés aux scheduled actions suspectes (nom de hook, fréquence, horodatage, arguments).

Ce sont des informations qui accélèrent l’analyse, parce qu’on ne repart pas de zéro sur les hooks et les enregistrements.

Checklist finale mentale, avant de dire “c’est propre”

Après la suppression des scheduled actions malveillantes, gardez ce cadre en tête. Si l’un des points échoue, vous n’êtes pas “fini”, vous êtes seulement au milieu.

    Les actions suspectes sont supprimées et ne reviennent pas après un cycle de cron. Les fichiers et hooks modifiés sont remis en état propre ou remplacés. Les comptes à risque sont corrigés (désactivation, suppression si nécessaire). Le comportement observable côté site s’est calmé. Les journaux n’indiquent plus de répétition liée aux mêmes horodatages.

À ce moment-là, vous pouvez considérer que la persistance a été retirée, pas seulement mise en pause.

Si vous avez déjà l’impression que votre WordPress “revient” après chaque nettoyage, commencez par là : les scheduled actions. C’est souvent moins spectaculaire que les fichiers, mais c’est souvent là que la menace se cache le mieux. Une fois que vous cassez la planification malveillante, le reste devient beaucoup plus simple.