Vous avez mis PHP à jour après la publication de PHP2Shell (CVE-2026-109618, CVE-2026-109619) le 21 octobre 2026. Bien. Mais patcher ne dit pas si quelqu'un est passé avant vous. Amazon Threat Intelligence a observé des tentatives d'exploitation dès le 17 octobre 2026, quatre jours avant le correctif, depuis son réseau de pots de miel MadPot. D'autres attaques ont suivi les 21 et 24 octobre, avec des charges utiles différentes, signe que plusieurs groupes exploitaient déjà la faille.
Ce guide est fait pour les administrateurs de VPS Linux et de serveurs dédiés qui veulent vérifier après coup si leur serveur PHP a été compromis pendant la fenêtre de vulnérabilité. Chaque étape donne les commandes à lancer en SSH, ce qu'il faut chercher, et comment interpréter les résultats. Aucune de ces vérifications ne suffit seule : c'est leur combinaison qui vous donne un diagnostic fiable.
Définir votre fenêtre d'exposition
La première question à se poser : votre serveur était-il vulnérable, et pendant combien de temps ?
Le code fautif existe depuis PHP 5.4, publié en 2012. Les correctifs sont sortis le 21 octobre 2026. Toutes les versions antérieures à PHP 8.5.12, 8.4.27, 8.3.36 et 8.2.35 sont touchées. Mais l'exploitation active connue a commencé le 17 octobre 2026 : c'est la date plancher pour votre investigation.
Commencez par retrouver quand vous avez réellement appliqué le correctif sur votre serveur. C'est la borne haute de votre fenêtre :
# Date d'installation du paquet corrigé (Debian / Ubuntu)
zgrep -h 'php' /var/log/dpkg.log* | grep ' install\| upgrade' | grep -i php | tail -20
# Red Hat, AlmaLinux, Rocky Linux
rpm -qa --last | grep -i php | head -10
# Si vous utilisez Docker, vérifiez quand l'image a été reconstruite
docker inspect --format='{{ .Created }}' $(docker images --filter=reference='*php*' -q) 2>/dev/null
Si le correctif a été installé le 23 octobre, votre fenêtre d'exposition va du 17 au 23 octobre. C'est sur cette période que vous allez concentrer vos recherches dans les journaux.
Étape 1 : confirmer que PHP répondait à des requêtes web
Un serveur sur lequel PHP n'est utilisé qu'en ligne de commande (scripts cron, outils d'administration sans serveur web) n'est pas exploitable à distance par PHP2Shell. Vérifiez quels processus servaient des requêtes web pendant la fenêtre :
# PHP-FPM actif ?
systemctl list-units --type=service | grep -i fpm
# Module Apache mod_php ?
apache2ctl -M 2>/dev/null | grep -i php
httpd -M 2>/dev/null | grep -i php
# Processus PHP-FPM en écoute
ss -tlnp | grep -E 'php|fpm'
Si aucun service PHP ne répondait à des requêtes HTTP pendant la fenêtre, le risque est nul. Si vous aviez PHP-FPM, mod_php, php-cgi ou FrankenPHP en fonctionnement, poursuivez.
Étape 2 : chercher les erreurs mémoire suspectes dans les journaux PHP
Le mécanisme d'exploitation de CVE-2026-109618 repose sur un dépassement de la limite de mémoire (memory_limit) provoqué volontairement par l'attaquant pendant la lecture de la requête, avant que votre code ne s'exécute. Ce dépassement laisse une trace caractéristique dans les journaux PHP : une erreur Allowed memory size of ... bytes exhausted suivie de in Unknown on line 0. Le « line 0 » signifie que PHP n'avait pas encore commencé à exécuter de script : c'est rarement le fait d'un usage normal.
# Chercher les erreurs mémoire "line 0" dans les journaux PHP
grep -rh 'Unknown on line 0' /var/log/php*.log /var/log/php*fpm*.log 2>/dev/null | head -20
# Même chose dans les journaux Apache (si mod_php)
grep -h 'Unknown on line 0' /var/log/apache2/error.log* /var/log/httpd/error_log* 2>/dev/null | head -20
# Avec journalctl (systemd)
journalctl -u 'php*' --since '2026-10-17' --until '2026-10-30' | grep 'Unknown on line 0'
Autre marqueur : le message zend_mm_heap corrupted, qui indique une corruption du tas mémoire de PHP. En temps normal, ce message est rare. Plusieurs occurrences pendant votre fenêtre d'exposition méritent une investigation.
grep -rh 'zend_mm_heap corrupted' /var/log/php*.log /var/log/apache2/error.log* 2>/dev/null
Pour approfondir la lecture de vos journaux, consultez notre guide analyser les logs d'un serveur Linux avec journalctl.
Étape 3 : repérer les plantages de processus PHP
Un exploit qui rate son adresse mémoire (l'ASLR rend l'exploitation non déterministe) provoque un plantage par erreur de segmentation (signal 11, SIGSEGV) du processus PHP. Un exploit qui réussit peut aussi provoquer un signal 6 (SIGABRT). Ces plantages apparaissent dans les journaux du serveur web et de PHP-FPM :
# Apache
grep -hE 'Segmentation fault|SIGSEGV|AH0005[12]|signal 11|signal 6|SIGABRT' \
/var/log/apache2/error.log* /var/log/httpd/error_log* 2>/dev/null | head -20
# PHP-FPM
grep -hE 'exited on signal (6|11)|SIGSEGV|SIGABRT|child .* exited' \
/var/log/php*fpm*.log 2>/dev/null | head -20
# systemd
journalctl -u 'php*' -u apache2 -u httpd --since '2026-10-17' --until '2026-10-30' \
| grep -iE 'segfault|sigsegv|signal 11|signal 6'
Un ou deux plantages isolés peuvent avoir d'autres causes (extension PHP buguée, OPcache corrompu). Mais une série de plantages concentrés sur quelques minutes ou quelques heures, surtout corrélés avec des erreurs mémoire (étape 2), est un indicateur fort de tentative d'exploitation.
Étape 4 : analyser les requêtes HTTP suspectes
L'attaque PHP2Shell utilise des requêtes multipart/form-data vers des URL qui n'acceptent normalement pas d'envoi de fichier. Certains scanners automatiques ont utilisé un User-Agent distinctif, php2shell-109618, et un boundary fixe B7kQpoc618, repérés par Fastly dans ses données réseau. Plus largement, cherchez :
- Des requêtes POST avec
multipart/form-datavers la racine (/,/index.php) ou vers des pages qui ne contiennent pas de formulaire d'envoi ; - Des requêtes contenant le champ
PHP_SESSION_UPLOAD_PROGRESS, utilisé par CVE-2026-109619 ; - Des réponses HTTP 500 en série, signe que l'attaquant rate plusieurs tentatives avant de réussir ;
- Des requêtes vers l'adresse IP brute du serveur (scan de masse) plutôt que vers un nom de domaine.
# Requêtes multipart suspectes dans les access logs Apache/NGINX
grep -hE 'multipart/form-data' /var/log/apache2/access.log* /var/log/nginx/access.log* 2>/dev/null \
| awk '$4 >= "[17/Oct/2026"' | head -30
# Le User-Agent scanner connu
grep -hri 'php2shell' /var/log/apache2/ /var/log/nginx/ 2>/dev/null
# Requêtes contenant PHP_SESSION_UPLOAD_PROGRESS
grep -rh 'PHP_SESSION_UPLOAD_PROGRESS' /var/log/apache2/ /var/log/nginx/ 2>/dev/null
# Série de 500 depuis une même IP
awk '$9 == 500' /var/log/apache2/access.log* /var/log/nginx/access.log* 2>/dev/null \
| awk '{print $1}' | sort | uniq -c | sort -rn | head -10
Croisez les horodatages des requêtes suspectes avec les plantages de l'étape 3 : si une IP envoie des POST multipart et que PHP-FPM plante dans la même seconde, c'est un signal fort.
Étape 5 : vérifier les adresses IP connues
Plusieurs rapports publics, notamment ceux d'Amazon Threat Intelligence et de Prophet Security, ont identifié des adresses IP sources d'exploitation. Voici celles publiées à ce jour :
| Adresse IP | Date d'observation | Source |
|---|---|---|
103.216.220.92 | 17 octobre 2026 | Amazon (MadPot) |
31.223.184.166 | 21 octobre 2026 | Amazon (MadPot) |
37.19.200.5 | 21 octobre 2026 | Amazon (MadPot) |
134.209.159.64 | 21 octobre 2026 | Amazon (MadPot) |
103.114.163.188 | 21 octobre 2026 | Amazon (MadPot) |
95.217.98.37 | 21 octobre 2026 | Amazon (MadPot) |
15.204.59.70 | 24 octobre 2026 | Prophet Security (honeypot) |
162.35.99.94 | 24 octobre 2026 | Prophet Security (honeypot) |
204.76.203.7 | 24 octobre 2026 | Palo Alto (honeypot) |
# Chercher ces IP dans vos access logs
grep -hE '103\.216\.220\.92|31\.223\.184\.166|37\.19\.200\.5|134\.209\.159\.64|103\.114\.163\.188|95\.217\.98\.37|15\.204\.59\.70|162\.35\.99\.94|204\.76\.203\.7' \
/var/log/apache2/access.log* /var/log/nginx/access.log* 2>/dev/null
L'absence de ces IP ne prouve rien : d'autres attaquants utilisent des adresses non encore publiées. Ces indicateurs sont un filet, pas une preuve d'absence.
Étape 6 : détecter les processus suspects lancés par PHP
Si l'exploitation a réussi, l'attaquant obtient l'exécution de commandes avec les droits de l'utilisateur du serveur web (www-data sous Debian/Ubuntu, apache ou nginx sous Red Hat). Le signe le plus direct de compromission est un shell ou un outil de téléchargement lancé en tant que processus fils de PHP :
# Processus actifs de l'utilisateur web (arborescence)
ps -o pid,ppid,lstart,cmd -u www-data --forest 2>/dev/null
ps -o pid,ppid,lstart,cmd -u apache --forest 2>/dev/null
ps -o pid,ppid,lstart,cmd -u nginx --forest 2>/dev/null
# Chercher spécifiquement des shells et outils de téléchargement
ps aux | grep -E '^(www-data|apache|nginx)' | grep -vE 'php-fpm|apache2|nginx|grep' | head -20
Les processus suspects à repérer : sh, bash, dash, python, perl, curl, wget, nc (netcat). Si auditd est installé sur votre serveur, vous pouvez chercher les exécutions passées. Consultez notre guide auditd sur VPS Linux pour la configuration :
# Si auditd est actif, chercher les exécutions par l'utilisateur web
ausearch -ua www-data --start 10/17/2026 --end 10/30/2026 -i 2>/dev/null | grep -i execve | head -30
Les commandes de reconnaissance que les attaquants lancent en premier, d'après les observations sur les pots de miel : whoami, id, uname -a, hostname. Cherchez-les dans les journaux d'audit ou d'historique shell.
Étape 7 : chasser les web shells dans les dossiers web
Après une première intrusion, l'attaquant dépose souvent un web shell : un fichier PHP qui lui donne un accès permanent au serveur, même après la mise à jour de PHP. Cherchez les fichiers PHP créés ou modifiés pendant votre fenêtre d'exposition :
# Fichiers PHP créés ou modifiés depuis le 17 octobre 2026
find /var/www -name '*.php' -newermt '2026-10-17' -ls 2>/dev/null
find /var/www -name '*.phtml' -newermt '2026-10-17' -ls 2>/dev/null
find /var/www -name '*.phar' -newermt '2026-10-17' -ls 2>/dev/null
# Fichiers appartenant à l'utilisateur web dans des dossiers inattendus
find /var/www -user www-data -name '*.php' -not -path '*/cache/*' -not -path '*/uploads/cache/*' -ls 2>/dev/null | head -20
Examinez le contenu des fichiers trouvés. Les web shells utilisent typiquement des fonctions d'exécution de commandes :
# Fonctions suspectes dans les fichiers PHP récents
find /var/www -name '*.php' -newermt '2026-10-17' -exec grep -lE \
'eval\s*\(|base64_decode|system\s*\(|passthru|shell_exec|proc_open|popen|exec\s*\(' {} \; 2>/dev/null
Si vous utilisez un gestionnaire de versions (Git), comparez l'état actuel du dossier web avec le dernier commit connu sain :
# Depuis la racine de votre projet
git diff --stat HEAD
git status --short
Pour les applications installées sans Git (WordPress, Nextcloud, Drupal), comparez avec une archive propre de la même version.
Étape 8 : vérifier les mécanismes de persistance
Un attaquant qui a obtenu l'exécution de commandes installe des portes dérobées durables pour revenir même après un redémarrage. Vérifiez chaque point :
Tâches planifiées
# Crontabs de l'utilisateur web et de root
crontab -u www-data -l 2>/dev/null
crontab -u apache -l 2>/dev/null
crontab -u root -l
# Fichiers cron système
ls -la /etc/cron.d/ /etc/cron.daily/ /etc/cron.hourly/
# Chercher des ajouts récents
find /etc/cron* -newermt '2026-10-17' -ls 2>/dev/null
# Timers systemd (les attaquants les utilisent aussi)
systemctl list-timers --all | grep -v systemd
Clés SSH
# Clés autorisées pour l'utilisateur web
cat /var/www/.ssh/authorized_keys 2>/dev/null
cat /home/*/.ssh/authorized_keys 2>/dev/null
cat /root/.ssh/authorized_keys
# Fichiers authorized_keys modifiés récemment
find / -name authorized_keys -newermt '2026-10-17' -ls 2>/dev/null
Comptes utilisateurs
# Comptes créés récemment
awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $3, $7}' /etc/passwd
# Comptes avec un shell et un UID élevé inhabituels
grep -E '/bin/(ba)?sh' /etc/passwd | grep -v -E '^(root|www-data|ubuntu|debian)'
Si votre serveur héberge un panel comme Pterodactyl ou un CMS comme WordPress, vérifiez aussi les comptes administrateurs dans la base de données. Pour un accès SSH déjà durci, consultez notre guide sécuriser SSH sur VPS Linux.
Étape 9 : évaluer le risque de fuite de mémoire
CVE-2026-109619 peut renvoyer à l'attaquant des fragments de mémoire libérée dans le cookie PHPSESSID. Cette mémoire peut contenir des mots de passe de bases de données, des jetons de session, des clés d'API ou d'autres secrets que PHP avait en mémoire. D'après Brandefense, un cookie PHPSESSID d'environ 819 Ko après décodage est un indicateur caractéristique de cette fuite.
# Chercher des cookies PHPSESSID anormalement longs dans les access logs
awk -F'"' '{print length($0), $0}' /var/log/apache2/access.log* 2>/dev/null \
| sort -rn | head -5
# Requêtes avec un cookie PHPSESSID très long (plus de 1000 caractères)
grep -oP 'PHPSESSID=[^;& ]{1000,}' /var/log/apache2/access.log* /var/log/nginx/access.log* 2>/dev/null | head -5
Si vous constatez des indices de fuite de mémoire, partez du principe que tous les secrets que PHP avait en mémoire pendant la fenêtre d'exposition ont pu être lus :
- Mots de passe des bases de données MySQL ou MariaDB (dans
wp-config.php,.env,config.php) ; - Clés d'API tierces (paiement, SMTP, stockage objet) ;
- Jetons de session des utilisateurs connectés ;
- Clés privées TLS si PHP les charge en mémoire.
Étape 10 : vérifier les conteneurs Docker
Les images Docker PHP ne se mettent pas à jour toutes seules. Si vous utilisez Docker, appliquez les mêmes vérifications à l'intérieur de chaque conteneur PHP. Notre guide auditer et patcher les conteneurs Docker PHP détaille la procédure complète. En résumé :
# Lister les conteneurs PHP et leur version
docker ps --filter "ancestor=php" --format "{{.Names}} {{.Image}} {{.Status}}"
docker compose exec app php -v # adaptez "app" au nom de votre service
# Chercher les journaux de crash dans un conteneur
docker logs mon-conteneur-php 2>&1 | grep -iE 'segfault|sigsegv|Unknown on line 0|zend_mm_heap'
Si vous êtes compromis : les gestes d'urgence
Si les vérifications précédentes montrent des signaux convergents (erreurs mémoire + plantages + processus suspects, ou fichiers PHP inconnus dans le dossier web), considérez le serveur comme compromis. Voici la marche à suivre :
- Isolez le serveur du réseau public. Coupez le serveur web mais gardez votre accès SSH. Ne redémarrez pas tout de suite : les processus en mémoire et les journaux sont des preuves.
# Couper le trafic web entrant (UFW) sudo ufw deny in 80/tcp sudo ufw deny in 443/tcp # Ou avec nftables / iptables sudo iptables -I INPUT -p tcp --dport 80 -j DROP sudo iptables -I INPUT -p tcp --dport 443 -j DROP - Préservez les journaux avant rotation. Copiez-les sur un support externe ou un autre serveur :
tar czf /tmp/logs-forensique-$(date +%Y%m%d).tar.gz \ /var/log/apache2/ /var/log/nginx/ /var/log/php* \ /var/log/syslog* /var/log/auth.log* 2>/dev/null - Changez tous les secrets que PHP pouvait lire : mot de passe de la base de données, clés d'API, clés SMTP. Renouvelez le certificat TLS si PHP chargeait la clé privée. Déconnectez toutes les sessions.
- Restaurez une sauvegarde saine, antérieure au 17 octobre 2026, sur un PHP déjà corrigé.
- Si vous soupçonnez un accès root (élévation de privilèges via une faille noyau comme CVE-2026-46300, chaînée avec PHP2Shell dans au moins une campagne documentée), réinstallez le système depuis une image propre. Un nettoyage à la main ne peut pas garantir l'intégrité du noyau ni des binaires système.
Pour limiter les dégâts la prochaine fois, mettez en place UFW et Fail2ban et automatisez les mises à jour de sécurité sur votre serveur.
Récapitulatif des vérifications
| Vérification | Ce qu'on cherche | Gravité si trouvé |
|---|---|---|
| Erreurs mémoire « line 0 » | Allowed memory size ... in Unknown on line 0 | Tentative d'exploitation probable |
| Corruption du tas | zend_mm_heap corrupted | Tentative d'exploitation probable |
| Plantages SIGSEGV / SIGABRT | Signal 11 ou 6 dans les journaux PHP-FPM / Apache | Exploitation ratée ou réussie |
| Requêtes multipart suspectes | POST multipart vers des URL sans formulaire d'envoi | Tentative ciblée |
| IP connues d'exploitation | Voir la table des adresses publiées | Tentative confirmée |
| Shell lancé par www-data | sh, bash, curl, wget sous l'utilisateur web | Compromission confirmée |
| Web shells dans le dossier web | Fichiers PHP récents contenant eval, system, base64_decode | Compromission confirmée |
| Persistance (cron, clés SSH, comptes) | Ajouts non autorisés depuis le 17 octobre | Compromission confirmée |
| Cookie PHPSESSID surdimensionné | Cookie décodé d'environ 819 Ko dans les access logs | Fuite de mémoire (secrets exposés) |
Conclusion
La mise à jour de PHP ferme la porte, mais ne dit pas si quelqu'un l'a franchie avant vous. Avec quatre jours d'exploitation active avant le correctif et plusieurs groupes d'attaquants documentés, la vérification après coup n'est pas optionnelle. Suivez les étapes de ce guide, croisez les signaux, et en cas de doute, partez du principe que le serveur a été touché : changez les secrets, restaurez depuis une sauvegarde saine et reconstruisez sur un PHP corrigé. Pour retrouver tous nos guides de sécurité et d'administration serveur, consultez la page guides.
FiveM
Minecraft
Rust
RedM
Hytale