Tâches cron inconnues : le malware qui revient à 3 h du matin
Un cron relance le mineur, réécrit un `.htaccess`, republie un article, envoie du spam. On liste les cron du compte d'hébergement, pas seulement ceux du CMS. C'est le motif n°1 de « c'est nettoyé, ça revient à 3 h ».
Un cron relance le mineur, réécrit un fichier ou envoie du spam. Listez les cron du compte, pas seulement ceux du CMS. C'est le motif n°1 de « ça revient ».
Pourquoi 3 h du matin revient si souvent
Moins de clients sur le site, moins d'admins connectés, parfois une fenêtre où les backups tournent (I/O masquée). Les kits copient les uns des autres : `0 3 * * *` est devenu un cliché. Votre pic à 3 h 07 n'est pas une coïncidence culturelle ; c'est une copie de tutoriel d'attaquant.
Le symptôme du matin : footer de retour, `radio.php` ressuscité, CPU à 100 %, file mail. Vous aviez « tout enlevé » à 18 h. Le cron n'était pas dans le dossier que vous avez scanné.
Traitez tout incident qui revient à heure fixe comme un cron jusqu'à preuve du contraire. Un hit HTTP à heure fixe peut être un ping externe ; un process sans hit, c'est local.
Trois étages : système, panel, CMS
Système (VPS) : crontab des users, `/etc/cron.*`, systemd timers. Panel : « Tâches planifiées » o2switch, cPanel, Plesk — souvent invisible depuis WordPress. CMS : WP-Cron, cron Magento, `cron.php` Presta, actions planifiées.
Nettoyer l'étage CMS et laisser le panel, le fichier revient. Inversement, un mu-plugin s'accroche à WP-Cron sans ligne panel. Les trois listes, la même heure.
Les voisins de compte n'ont pas votre crontab. Si le process n'est pas à votre uid, ticket hébergeur, pas réinstall WordPress.
- crontab / timers (VPS).
- Planificateur du panel.
- WP-Cron, Presta cron, Magento cron.
- Hooks « shutdown » qui simulent un cron.
Lire crontab et le planificateur cPanel
cPanel → Cron Jobs : chaque ligne, chaque PHP. Un chemin dans `tmp`, `cache`, `uploads`, un nom `wp-file.php` à la racine. Copiez toutes les lignes dans un fichier hors serveur avant d'effacer. La ligne est une preuve.
Plesk et les panels « simples » cachent parfois les cron système. Demandez au support « liste des cron de l'uid ». Sur un VPS, `crontab -l` pour chaque user, pas seulement `root`.
Une tâche « backup » qui lance un PHP que vous ne reconnaissez pas n'est pas un backup. Les vrais jobs backup ont un nom d'outil (restic, jetbackup, un script d'agence daté et commenté).
WordPress : WP-Cron n'est pas crontab
WP-Cron tourne sur les visites (ou via un vrai cron qui appelle `wp-cron.php`). Les événements sont en base (`wp_options`, clé `cron`). Un plugin pirate s'y inscrit. Des outils (WP-CLI `cron event list`, un plugin d'inspection) listent les hooks. Un hook `my_custom_sync` toutes les cinq minutes vers un PHP inconnu sort.
`DISABLE_WP_CRON` + un cron système propre est une bonne hygiène après incident. `DISABLE_WP_CRON` + un cron système sale, vous avez seulement déplacé. Voir WP-Cron malware.
Les « Action Scheduler » WooCommerce (tables `actionscheduler_*`) sont un quatrième tiroir. Des milliers d'actions `spam_rebuild` : le générateur est là. Ne tronquez pas la table entière : vous tuez aussi les e-mails de commande. Filtrez le hook.
Presta, Magento, et les runners
PrestaShop : `Modules → Cron` et la crontab qui frappe `modules/…/cron.php`. Un token de cron dans l'URL, s'il est public, est une porte. Magento : `bin/magento cron:run` toutes les minutes est normal ; un second PHP dans la même crontab ne l'est pas.
Les runners CI (deploy à heure fixe) peuvent avoir été détournés. Lisez le script de deploy, pas seulement le site. Un deploy qui rsync un repo infecté « répare » le malware toutes les nuits — l'inverse du but.
Windows / Plesk scheduled tasks : même logique, autre UI. Exportez la liste.
Tuer ne suffit pas : trouver l'écrivain de cron
Un shell à la racine recrée la ligne crontab. Vous videz le panel, il la remet. Retirez le shell et changez le panel dans la même fenêtre. Voir PHP à la racine.
Un admin fantôme avec un plugin « Easy Cron » fait la même chose depuis l'UI. Les utilisateurs se listent le même jour.
Certains malwares écrivent dans `~/.bashrc` ou un `@reboot`. Sur VPS, lisez ça. Sur mutualisé, vous n'y avez souvent pas accès : le panel et le webroot suffisent presque toujours.
Ce que la ligne de commande révèle
`php /home/xxx/public_html/wp-core.php` : chemin du payload. `curl https://… | sh` : téléchargement à chaque run — tuer le fichier local ne suffit pas, l'URL fournit la suite. `wget` vers `tmp` idem.
Notez l'URL distante si elle est là. Elle sert au constat, parfois à un signalement. Ne la visitez pas depuis votre session admin habituelle sans précaution ; c'est un kit.
Un `mysql` en cron qui `UPDATE wp_posts` : l'injection est en SQL, pas en fichier. La ligne cron est alors plus précieuse que n'importe quel scan de thème.
Après : une nuit de test
Lignes pirates ôtées, shells copiés/retirés, panel changé. Laissez passer la fenêtre habituelle (3 h). Vérifiez : fichier absent, CPU calme, pas de nouvel article, `.htaccess` intact. Si ça revient, l'écrivain de cron est encore là — souvent un second étage.
Gardez la liste « avant » des cron. À J+7, comparez. Une nouvelle ligne est plus parlante qu'un Wordfence vert.
Google n'entre ici que si le cron régénère des pages ou un kit. Alors 404 + éventuellement Safe Browsing 24-72 h. Le cron lui-même n'a pas de réexamen.
Alertes pour ne pas revivre J+30
Moins de personnes avec l'accès panel. Un second admin hébergeur « au cas où » est une porte. Revue des cron à chaque incident, et une fois par trimestre si vous maintenez.
Sur VPS, une alerte si crontab change (inotify, un petit script). Sur mutualisé, une revue manuelle après chaque « le site a fait un truc bizarre ».
Vous pouvez déclarer le site. Les listes de cron exportées raccourcissent l'intervention plus qu'un accès admin CMS seul.
- Export des trois étages.
- Isolation des lignes inconnues, pas un wipe.
- Shells et panel dans la même heure.
- Nuit de test sur l'horaire habituel.