PHP2Shell (CVE-2026-109618 / CVE-2026-109619) : deux RCE critiques dans le cœur de PHP, divulguées avant le correctif
Dernière mise à jour : il y a 1 heure
21/10/2026 - article original
22/10/2026 - mise à jour de l'article suite aux publications des équipes PHP
Le 21 octobre 2026, PHP a publié en urgence les versions 8.5.12, 8.4.27, 8.3.36 et 8.2.35. Elles corrigent deux failles critiques (CVSS v4.0 9.2) qui permettent une exécution de code à distance sans authentification, avant toute ligne de code applicatif. La cause racine n'est pas celle que relayaient les fuites : il ne s'agit pas d'un bug isolé de l'upload progress, mais de la façon dont PHP abandonne une requête quand la mémoire manque. Des tentatives d'exploitation sont déjà observées.
Mettez à jour maintenant, puis redémarrez réellement chaque processus PHP.

Ce qui change dans cette mise à jour
La première version de cet article (21/10, avant 14:00 UTC) reposait sur des fuites et des analyses tierces. Les avis officiels sont depuis publiés. Quatre points changent :
Point | Version du 21/10 (avant patch) | Ce qui est établi au 22/10 |
Versions corrigées | Projetées, non confirmées | Confirmées : 8.5.12, 8.4.27, 8.3.36, 8.2.35, plus des backports source minimaux pour 7.4, 8.0 et 8.1 |
Cause racine | Un use-after-free supposé dans session.upload_progress | Un longjmp sur dépassement mémoire laisse un état partiel ou périmé. L'upload progress n'est qu'un consommateur confirmé de la seconde faille |
Mitigation session.upload_progress.enabled = Off | Mesure principale, jugée partielle | Ne couvre que des chemins de CVE-2026-109619. N'a aucun effet sur CVE-2026-109618 |
Exploitation | Aucune confirmée | Tentatives observées par Amazon, Cloudflare, watchTowr et Patchstack. Aucun exploit fonctionnel public identifié par PHP à la publication |
Si vous avez appliqué uniquement la désactivation de l'upload progress, vous restez exposé. Seule la mise à jour corrige les deux failles.
Synthèse rapide
Quoi : deux failles dans le cœur de PHP. CVE-2026-109618 (« RCE via OOM corruption ») et CVE-2026-109619 (« RCE via stale global state »). Toutes deux sont notées CVSS v4.0 9.2.
Mécanisme commun : quand une allocation dépasse memory_limit, PHP abandonne la requête par un longjmp. Une structure à moitié remplie, ou un pointeur vers de la mémoire déjà libérée, reste alors accessible.
Qui est exposé : toute installation PHP qui sert des requêtes web : PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, LiteSpeed. PHP juge le risque le plus élevé pour mod_php, où le RCE a été démontré avec la configuration par défaut et sans code applicatif.
Correctifs : 8.5.12, 8.4.27, 8.3.36, 8.2.35. Les branches 8.1 et antérieures ne reçoivent pas de version officielle, seulement des backports source minimaux pour 7.4, 8.0 et 8.1.
Exploitation : une première tentative a été vue le 17/10 sur les honeypots d'Amazon, avant la divulgation. Des attaques sont signalées depuis la publication. PHP n'avait identifié aucun exploit fonctionnel public à la publication.
Notre position : traitement P0. Mettez à jour sous 24 h tout PHP exposé à Internet, redémarrez complètement les processus, et isolez les versions sans correctif.
Références et chronologie
Référence | Intitulé officiel | Avis |
CVE-2026-109618 | RCE via OOM corruption | |
CVE-2026-109619 | RCE via stale global state | |
Annonce PHP | Security advisory: Critical remote code execution vulnerabilities, update now | |
CERT-EU | Security Advisory 2026-018 |
Date (UTC) | Événement |
22/10 | Plusieurs éditeurs (Cloudflare, watchTowr, Patchstack) signalent des tentatives d'exploitation. Amazon parle d'exploitation large |
21/10, 14:00 | Publication de PHP 8.5.12, 8.4.27, 8.3.36 et 8.2.35, des deux avis GHSA et des backports source 7.4, 8.0, 8.1 |
20/10, 19:33 | PHP avance la publication au 21/10 après la fuite |
20/10 | Fuite sur Hacker News, confirmation par watchTowr, vidéo de démonstration de Calif.io relayée par Kirill Firsov |
17/10 | Première tentative d'exploitation vue sur le honeypot MadPot d'Amazon, avant toute divulgation publique |
18/09 | OpenAI signale des vecteurs supplémentaires |
24/08 | Wiz signale des vecteurs supplémentaires |
11/08 | Ryan A (Calif) signale un RCE inconditionnel avec la même cause racine. OpenAI fait le même signalement le même jour |
Avant août | Un développeur du cœur PHP, financé par la PHP Foundation, repère les premiers signes du problème d'OOM en corrigeant un point de l'audit Quarkslab de 2024 |
Le score officiel CVSS est de 9.2 pour les deux failles.
Analyse technique
La cause commune : un abandon au mauvais moment
PHP n'a pas de chemin d'erreur propre quand la mémoire manque. Quand une allocation dépasse memory_limit, zend_mm_safe_error() (dans Zend/zend_alloc.c) appelle zend_bailout(). Cette fonction fait un longjmp hors de emalloc(). La fonction qui allouait ne termine pas son travail, et aucun nettoyage local ne s'exécute.
Le problème apparaît si cette fonction avait déjà publié un pointeur vers une structure pas encore remplie. L'avis parle de mémoire « temporarily reachable before it is fully initialized ». Pour un reverser, c'est l'équivalent d'un goto qui sort d'une section critique.
L'attaquant choisit le moment de l'OOM
Le parsing multipart (main/rfc1867.c) alloue de la mémoire avant tout code applicatif. Selon Alexandre Daubois, membre de l'équipe sécurité PHP, une requête offre trois leviers pour provoquer l'échec d'allocation :
un corps sans longueur déclarée exploitable ;
un nombre illimité d'en-têtes par part ;
des champs fichier vides, qui échappent à max_file_uploads.
En dosant ces éléments, l'attaquant fait échouer l'allocation à un endroit choisi, sans dépasser les limites apparentes.
CVE-2026-109618 : un état partiel lu au shutdown
L'état à moitié initialisé reste en place après le longjmp. Le shutdown de la requête parcourt ensuite cet état et le traite comme valide. mod_php exécute ce shutdown dans tous les cas. C'est pourquoi le RCE a été démontré sur Apache mod_php avec les réglages par défaut, par une requête non authentifiée, avant tout code applicatif.
Sous PHP-FPM, le worker meurt quand le démarrage de la requête échoue, ce qui bloque la variante la plus simple. PHP considère FPM comme exposé malgré tout : un RCE y a été confirmé par des chemins applicatifs.
CVE-2026-109619 : un pointeur périmé entre deux requêtes
Ici, c'est un use-after-free qui traverse deux requêtes. Une erreur fatale au mauvais moment, un OOM par exemple, laisse dans les variables globales du worker un pointeur vers la mémoire de la requête terminée. La requête suivante traitée par le même worker lit cet état avant sa réinitialisation normale.
Le consommateur confirmé est le chemin session.upload_progress. Il traite la session avant l'initialisation normale, sans appel à session_start(). PHP a confirmé un use-after-free et des crashs de worker. Le découvreur a démontré un RCE contre une application réelle sur mod_php. PHP 8.5 a un chemin supplémentaire via fatal_error_backtraces.
Imperva décrit d'autres effets du même type, non confirmés par PHP : un identifiant de session lu avec une longueur démesurée, qui fait fuiter environ 67 Ko de mémoire du processus dans l'en-tête Set-Cookie: PHPSESSID, et un emplacement de cache de classe OPcache réservé mais jamais publié.
Ce que change le correctif
Le correctif ne colmate pas des cas isolés. Il change la gestion de l'OOM, sur environ 390 fichiers selon Daubois :
memory_limit devient une limite souple. Son dépassement pose une erreur en attente, et PHP lève un UnwindExit au prochain point de contrôle, au lieu d'un longjmp immédiat.
Le parser multipart vérifie cette erreur entre les parts et les champs, libère ce qu'il a construit et s'arrête.
Un nouveau réglage, memory_hard_limit_percent, fixe une limite dure : 200 % de memory_limit par défaut sous 1 Go. Au-delà, le processus s'arrête par _exit(137).
Cela explique une mitigation contre-intuitive de l'avis : memory_limit=-1. Sans limite, l'échec contrôlable par l'attaquant disparaît.
Principe d'exploitation
Aucune source publique ne décrit la chaîne complète, et nous ne publions pas d'exploit. Les étapes ci-dessous combinent les avis officiels et ce que montre la vidéo de Calif.io. Les étapes 4 et 5 sont une inférence, pas un fait publié.
Reconnaissance : identifier un PHP servi en mod_php ou FPM, sa version et si possible le build de la distribution. La vidéo cible un paquet précis (Ubuntu, PHP 8.5.4, Apache prefork/mod_php).
Déclenchement de l'OOM : envoyer une requête multipart/form-data dosée (parts, en-têtes par part, champs fichier vides) pour faire échouer une allocation à un point choisi du parsing.
État corrompu : le longjmp laisse une structure partielle lue au shutdown (CVE-2026-109618), ou un pointeur périmé dans le worker que la requête suivante lit via l'upload progress (CVE-2026-109619).
Fuite d'adresses : la vidéo affiche des adresses du tas et du moteur Zend (main_heap, zval_ptr_dtor, zend_call_function) avant l'exécution. Cela correspond à un contournement de l'ASLR.
Détournement : la zone libérée est réoccupée par des données contrôlées, qui détournent un appel interne vers une fonction choisie.
Post-exploitation : la vidéo se termine par uid=33(www-data) en environ 23 secondes. Suivent typiquement un webshell, une persistance et un mouvement latéral.
La vidéo montre un compteur de tentatives (« exploit attempt :: 1/8 »). Imperva juge la complexité d'attaque élevée. L'exploitation dépend du build et de la disposition du tas, mais elle reste outillée.
Pour les pentesters : n'exécutez aucun « PoC php2shell » trouvé en ligne sans l'avoir lu (voir la section suivante). En audit, limitez-vous à la vérification de version, de configuration et du redémarrage effectif des processus.
Impact
Écosystème
La faille se trouve sous l'application. Mettre à jour WordPress, WooCommerce, Magento, PrestaShop, Drupal ou Laravel ne corrige rien. Le RCE donne les droits de l'utilisateur du serveur web, en général www-data. Cela suffit pour injecter un skimmer sur une page de paiement, voler wp-config.php ou un .env, ou rebondir vers la base de données.
L'hébergement mutualisé et les panneaux (cPanel, Plesk) concentrent le risque : un seul runtime vulnérable sert des milliers de sites. La longue traîne est importante. Selon Mallory (20/10), 883 717 hôtes exposés, soit 59 % de ceux qui affichent leur version PHP, tournent sur une branche sans version corrigée officielle (≤ 8.1).
Sévérité officielle vs risque pratique
Critère | Lecture officielle | Lecture Safercy |
Score | CVSS v4.0 9.2 | Critique |
Authentification | Aucune pour le chemin démontré | Aucune |
Configuration | Défaut vulnérable sur mod_php | Défaut vulnérable |
Exploit | Démontré par les découvreurs, aucun exploit public fonctionnel à la publication | Outil démontré en vidéo, tentatives observées avant et après le patch |
Urgence | « Update immediately » | P0 : patch sous 24 h des systèmes exposés, isolation des branches sans correctif |
Dimension réglementaire
RGPD : un RCE sur un serveur qui traite des données personnelles est une violation potentielle (art. 32). En cas de compromission avérée, notifiez la CNIL sous 72 h (art. 33) et les personnes concernées si le risque est élevé (art. 34). Documentez la date du patch : c'est une preuve de diligence.
PCI DSS v4.0 : les vulnérabilités critiques doivent être corrigées sous un mois (exigence 6.3.3), mais un RCE pré-authentification dans l'environnement de données de carte justifie un traitement immédiat. Renforcez la surveillance des pages de paiement (exigence 11.6.1).
NIS2 : pour les entités concernées, un incident significatif impose une alerte précoce sous 24 h et une notification sous 72 h (art. 23). La gestion des vulnérabilités relève de l'article 21.
Indicateurs de détection
Aucun IoC officiel n'est publié. La cause racine connue donne des pistes de chasse plus précises que les jours précédents.
Logs PHP et serveur web
Allowed memory size of ... bytes exhausted dans error.log, corrélé à des POST multipart, surtout vers des scripts qui n'acceptent pas d'upload (/, index.php, xmlrpc.php).
Crashs de workers : AH00052: child pid ... exit signal Segmentation fault (11) sous Apache, child ... exited on signal 11 (SIGSEGV) sous PHP-FPM. Un crash mémoire est souvent la trace d'une tentative ratée.
Rafales de requêtes multipart depuis une même source, avec beaucoup de parts, beaucoup d'en-têtes par part, ou des champs fichier vides.
Champ PHP_SESSION_UPLOAD_PROGRESS dans le corps des requêtes. Apache ne journalise pas le corps : il faut un WAF ou un proxy qui l'inspecte.
Signaux réseau
Réponses avec un en-tête Set-Cookie: PHPSESSID anormalement long (Imperva évoque une fuite d'environ 67 Ko).
Réponses 500, 502 ou 504 en série sur des requêtes multipart.
Cloudflare a déployé des règles WAF temporaires. Vérifiez qu'elles sont actives et que votre origine n'est pas joignable en direct.
Système et post-exploitation
Processus enfants lancés par php-fpm, php-cgi, apache2 ou httpd : sh, bash, curl, wget, nc, python, perl, et sous Windows cmd.exe, powershell.exe, certutil.exe.
Nouveaux fichiers .php dans la racine web ou dans wp-content/uploads, nouvelles tâches cron, nouveaux comptes administrateurs.
Pseudo-règle Sigma : ParentImage|endswith: [php-fpm, php-cgi, php-cgi.exe, httpd, apache2] ET Image|endswith: [/sh, /bash, /curl, /wget, /nc, cmd.exe, powershell.exe, certutil.exe].
# Erreurs mémoire et crashs de workers
grep -hE 'Allowed memory size|exit signal|SIGSEGV' /var/log/apache2/error.log* /var/log/php*-fpm.log*
# Fichiers PHP récents dans la racine web et les uploads
find /var/www -name '*.php' -newermt '2026-10-17' -ls
# Processus qui exécutent encore l'ancien PHP après la mise à jour
sudo grep -lE ' /(usr|lib|opt).*\(deleted\)$' /proc/[0-9]*/mapsRemontez les huntings au 17/10, date de la première tentative connue, et non au 21/10.
Correctifs et mitigation
Versions à déployer
Branche | Version corrigée | Remarque |
8.5 | 8.5.12 | Aussi : fatal_error_backtraces=0 tant que le patch n'est pas déployé |
8.4 | 8.4.27 | |
8.3 | 8.3.36 | |
8.2 | 8.2.35 | Fin du support de sécurité le 31/12/2026 |
8.1, 8.0, 7.4 | Aucune version officielle | Backports source minimaux dans php-src (branches PHP-8.1-CVE-2026-109618-109619, etc.), ou backport d'un fournisseur LTS |
≤ 7.3 | Aucune | Migration, ou isolation d'Internet |
Redémarrer pour de vrai
Un processus garde l'ancien PHP en mémoire jusqu'à son redémarrage. Sous Apache mod_php, apachectl graceful ou un reload ne suffisent pas : Mattias Geniar a vérifié qu'Apache garde l'ancien libphp après un graceful. Faites un arrêt et un démarrage complets (systemctl restart apache2). Pour PHP-FPM, un reload charge le nouveau binaire. Redémarrez aussi les workers de files d'attente, Octane et Supervisor. Contrôlez ensuite qu'aucun processus ne mappe un fichier (deleted) sous /usr, /lib ou /opt.
Si vous ne pouvez pas mettre à jour immédiatement
Ces mesures réduisent l'exposition à certains chemins. Elles ne rendent pas une installation sûre.
Contre CVE-2026-109618 :
memory_limit=-1, fixé là où l'application ne peut pas le modifier (php_admin_value dans le pool FPM ou la configuration Apache). Associez-le à une limite mémoire de conteneur ou de cgroup qui tue le seul worker fautif. N'utilisez pas RLIMIT_AS ni RLIMIT_DATA : ils provoquent justement l'échec d'allocation vulnérable.
Un post_max_size bas : 8M sans upload, 32M avec, et upload_max_filesize en dessous.
Des limites au niveau du serveur web ou du proxy : taille du corps décodé, nombre de champs, nombre de parts multipart, nombre d'en-têtes, y compris pour les requêtes sans Content-Length exploitable. Sans besoin d'upload, bloquez multipart/form-data.
Contre CVE-2026-109619 :
session.upload_progress.enabled=0 dans php.ini, le pool FPM ou la configuration serveur, puis redémarrage de chaque worker. Pas via ini_set() ni .user.ini FPM.
Sous PHP 8.5 : fatal_error_backtraces=0.
Supprimer les réglages invalides ou obsolètes appliqués par requête qui émettent un diagnostic avant l'initialisation. Masquer les erreurs (display_errors, @) n'aide pas.
Corriger les erreurs fatales et timeouts atteignables par un attaquant.
Après le patch
Gardez session.upload_progress.enabled=0 et session.use_strict_mode=1 si votre application ne s'en sert pas, ce qui est le cas de la plupart. Retirez les blocages de multipart et les règles Cloudflare qui cassent les uploads. Sur une branche sans correctif, gardez toutes les mesures jusqu'à la migration.
Recommandations par audience
Hébergeurs, éditeurs et intégrateurs
Inventoriez chaque runtime réellement servi : chaque pool FPM, mod_php, conteneur et appliance. php -v en ligne de commande ne prouve rien pour le site servi.
Déployez les versions corrigées, puis vérifiez la version vue par le web et l'absence de mappings (deleted).
Pour les images Docker, utilisez un tag de branche (php8.3-apache) plutôt que latest, et reconstruisez avec --pull. Une image construite localement sans --pull reste figée.
Hébergeurs : migrez ou isolez d'office les comptes en 8.1 et en dessous, et communiquez aux clients la version servie et l'heure du déploiement.
Éditeurs de logiciels ou d'appliances qui embarquent PHP : publiez un avis d'exposition sous 24 h, même négatif.
SOC et Blue Team
Alertez sur les erreurs Allowed memory size associées à des POST multipart et sur les crashs de workers.
Surveillez les processus enfants des workers PHP (EDR, auditd).
Vérifiez que les logs web et PHP sont centralisés et protégés. Sans eux, l'analyse d'impact sera impossible.
Mettez à jour vos règles WAF : limites de parts et d'en-têtes multipart, taille de Set-Cookie: PHPSESSID en sortie.
Réponse à incident
Considérez comme potentiellement compromis tout hôte exposé et non corrigé après le 17/10, jusqu'à preuve du contraire. La mise à jour n'efface pas une intrusion antérieure.
Préservez avant de patcher : la recréation d'un conteneur ou le redémarrage d'un worker efface la mémoire et les processus. Capturez les logs, la mémoire si possible, les fichiers de session, les tâches cron et la liste des comptes.
Cherchez webshells, fichiers PHP récents, comptes administrateurs ajoutés et, sur l'e-commerce, l'intégrité des scripts des pages de paiement.
Faites tourner les secrets accessibles à l'utilisateur web : wp-config.php, .env, clés d'API, identifiants de base de données.
Qualifiez tôt les obligations de notification : 72 h pour le RGPD, 24 h et 72 h pour NIS2, et l'acquéreur selon le contrat PCI DSS.
Points encore incertains
Lien entre les deux CVE : aucune source ne dit si l'OOM de CVE-2026-109618 sert à produire l'état périmé de CVE-2026-109619 dans une même chaîne. Elles partagent un déclencheur, c'est tout ce qui est établi.
Ampleur de l'exploitation : Amazon parle d'exploitation large et Patchstack d'incidents confirmés. Imperva n'a rien observé sur ses signatures. Amazon précise ne pas savoir si les tentatives du 21/10 auraient réussi sur des systèmes réels.
Exploitabilité sous FPM : RCE confirmé seulement par des chemins applicatifs, sans exploit complet pré-authentification public. FrankenPHP et LiteSpeed sont listés comme exposés, sans démonstration publique.
Détails Imperva (fuite d'environ 67 Ko via PHPSESSID, slot OPcache) : non confirmés par PHP.
Chaîne d'exploitation complète : non publiée. Notre description des étapes de fuite et de détournement est une inférence à partir de la vidéo.



