22 octobre 2026 CVE Nicolas D

PHP2Shell : 2 failles RCE critiques dans PHP (CVSS 9.2)

CVE-2026-109618 / CVE-2026-109619 / Critique · 9.2 / Corrigé en octobre 2026

Le score est de 9.2, pas le 10.0 parfait que certains annonçaient avant le correctif. Mais l'impact est bien réel : Wiz a utilisé ces failles pour exécuter du code sur des installations par défaut de WordPress, Drupal et Joomla, sans authentification, et nous avons été en mesure de reproduire une chaîne d'exploitation fonctionnelle durant nos recherches.

Chaîne d’exécution de code PHP2Shell reproduite dans notre laboratoire (PHP 8.4.26, Apache mod_php).

EN BREF

PHP a corrigé deux bugs critiques, surnommés « PHP2Shell ». Les deux viennent de mémoire laissée dans un état incohérent quand PHP interrompt une opération trop tôt. Un attaquant peut faire crasher un worker ou, selon la configuration, exécuter du code sans s'authentifier. Wiz l'a démontré sur des installations WordPress, Drupal et Joomla par défaut, et nous avons reproduit une chaîne d'exécution de code fonctionnelle durant nos recherches. La seule vraie réponse : mettre à jour maintenant.

À RETENIR

  • Deux CVE regroupent plusieurs bugs connexes, corrigés dans PHP 8.2.35, 8.3.36, 8.4.27 et 8.5.12.

  • CVE-2026-109618 peut laisser des données de requête à moitié construites après un dépassement de la limite mémoire ; CVE-2026-109619 laisse un état global résiduel que la requête suivante réutilise.

  • Wiz a démontré une exécution de code non authentifiée sur des installations WordPress, Drupal et Joomla par défaut. De notre côté, nous avons reproduit un crash de worker (CVE-2026-109619) et, séparément, enchaîné la fuite mémoire et le use-after-free de CVE-2026-109618 jusqu'à l'exécution de code, durant nos recherches.

  • L'exécution de code est bien plus délicate qu'un simple crash et dépend fortement de la cible. Mettre PHP à jour reste le seul correctif complet.

De la spéculation au risque confirmé

Avant la publication des détails techniques, de nombreuses vidéos et prétendus PoC circulaient. Beaucoup ne montraient pas réellement d'exploit fonctionnel, et les annonces d'un score parfait de 10 relevaient encore de la spéculation. Au delà de la spéculation, les CVE divulguées sont tout de même graves : un chemin d'exécution de code non authentifié, en pré-authentification, dans le cœur de PHP, avec un score CVSS de 9.2. Exploiter un grand nombre de serveurs différents est plus difficile, car les attaques doivent être adaptées en fonction des cibles. Mais une exploitation à grande échelle n'est pas impossible.

Deux causes profondes, plusieurs bugs

Il y a deux numéros de CVE, mais plus de deux bugs : PHP a regroupé plusieurs problèmes connexes sous chacun.

Dans les deux cas, PHP laisse de la mémoire dans un état incohérent quand une opération est interrompue, le plus souvent par un dépassement de la limite mémoire. Le correctif en tire son nom : « Avoid state corruption via bailout ».

La différence tient au type de défaillance :

CVE-2026-109618, OOM corruption. PHP s'interrompt alors que des données de requête sont à moitié construites ; le nettoyage réutilise ensuite ces données invalides, parfois au sein de la même requête.

CVE-2026-109619, stale global state. PHP garde un pointeur vers la mémoire de la requête après l'avoir libérée, et une requête suivante, sur le même worker, peut le déréférencer.

Le second groupe est un use-after-free ; le premier peut aussi corrompre la mémoire sans suivre exactement cette séquence. Wiz estime que d'autres variantes existent probablement sous ces deux numéros.

CVE-2026-109618: Corruption par OOM

Chaque requête PHP dispose d'un budget mémoire (memory_limit, 128 Mo par défaut). Dès qu'une requête le dépasse, PHP l'interrompt et bascule sur le nettoyage. Le souci, c'est que cela peut arriver pendant que PHP lit encore le corps de la requête : les données restent à moitié construites, et le nettoyage de cette même requête les réutilise. Cette CVE couvre plusieurs défaillances de ce genre.

Les données d'upload à moitié construites

Si PHP manque de mémoire pendant la construction de la liste des fichiers envoyés, celle-ci reste à moitié construite, et le nettoyage lit alors des champs jamais initialisés. Sous mod_php, cela peut survenir avant même l'exécution de votre code. PHP-FPM gère différemment les démarrages en échec : ce chemin pré-applicatif n'y est donc pas confirmé. Nous n'avons pas obtenu de crash isolé sur ce chemin durant nos recherches ; notre résultat concret confirmé est la chaîne complète d'exécution de code de la section 5, qui s'appuie sur le use-after-free du suivi d'upload.

Une fuite mémoire

C'est le second bug qui rend les attaques réelles possibles. Un dépassement de la limite mémoire (OOM) peut laisser l'état de session pointer vers de la mémoire déjà libérée. Une requête multipart ultérieure peut alors renvoyer un fragment de la mémoire du worker dans un cookie de réponse PHPSESSID, même si l'application ne démarre jamais de session. L'attaquant récupère ainsi les adresses mémoire dont il a besoin. Wiz a enchaîné cette fuite avec un use-after-free dans le désérialiseur du suivi d'upload (session.upload_progress) pour exécuter du code sur WordPress, Drupal et Joomla.

CVE-2026-109619: Des restes pour la requête suivante

PHP conserve certaines données dans chaque worker d'une requête à l'autre. Une erreur fatale peut laisser ces données pointer vers de la mémoire libérée tout en sautant la réinitialisation, si bien que la requête suivante les réutilise. Wiz a démontré deux cas ; le bulletin de sécurité de PHP en décrit un troisième, lié au cycle de vie de session.

Les backtraces d'erreur fatale (fatalerrorbacktraces)

Quand PHP enregistre une backtrace après une erreur fatale (fonctionnalité fatalerrorbacktraces, activée par défaut en 8.5), il peut conserver un pointeur vers de la mémoire libérée. Un diagnostic émis tôt dans la requête suivante y accède. Nous avons reproduit ce crash durant nos recherches.

Le suivi d'upload (session.upload_progress)

Le suivi d'upload de PHP (session.uploadprogress, activé par défaut) peut lui aussi lire un état de session résiduel laissé par une erreur antérieure ; PHP confirme ce chemin sous CVE-2026-109619. Il ne requiert aucun appel à sessionstart(), mais l'attaque validée sur PHP-FPM suppose en plus une opération applicative adaptée.

Confusion dans OPcache

Une allocation mémoire qui échoue peut désynchroniser le cache de code partagé de PHP : un autre worker confond alors un type interne avec un autre. Comme ce cache est partagé, le problème peut atteindre plusieurs workers.

Quelle exploitabilité ?

Nous avons reproduit une exécution de code complète

En enchaînant la fuite mémoire de PHP2Shell avec son use-after-free du suivi d'upload, nous avons obtenu une exécution de code à distance durant nos recherches, pas un simple crash. C'est la preuve directe et concrète que le risque est réel, et pas seulement théorique. Nous ne publierons pas notre exploit : du code d'attaque fonctionnel est déjà public, et un outil prêt à l'emploi de plus profiterait davantage aux attaquants qu'aux défenseurs. Nous avons préféré l'inverse : fiabiliser l'exploit pour le comprendre en profondeur, puis traduire cette compréhension en une détection plus rapide de cette vulnérabilité.

Le plus dur n'est pas la vulnérabilité. C'est la cible. Les deux obstacles qui arrêtent d'ordinaire ce type de bug sont déjà levés : la randomisation des adresses (ASLR) tombe grâce à la fuite mémoire, qui livre à l'attaquant de vraies adresses dans un cookie de réponse PHPSESSID ; et une fois la faille atteinte, la primitive est fiable.

Ce qui reste, c'est la précision propre à chaque cible. Un exploit fonctionnel est calé sur un build précis : les offsets exacts des symboles (par exemple zif_system etzvalptrdtor) et les « valeurs magiques » de heap grooming qui placent les données de l'attaquant là où le pointeur libéré ira les lire. Changez la version de PHP, la distribution, le compilateur, les extensions ou l'architecture CPU, et ces valeurs se décalent : la preuve de concept doit être réajustée. Certaines configurations ajoutent de la friction, surtout pour déclencher de façon fiable le dépassement mémoire, mais rien de tout cela n'est un mur.

La menace réaliste, ce sont donc des attaques ciblées, pas un exploit universel unique. Or les informations propres à chaque cible sont souvent servies à l'attaquant : une version de PHP dans une page d'erreur verbeuse, un en-tête X-Powered-By: PHP/8.x.y ou Server, un build reconnaissable. Une fois la version connue, faire correspondre les offsets devient mécanique : exactement le genre de tâche répétitive que l'automatisation rend bon marché.

ConstatDétail
Démontré Exécution de code non authentifiée. Nous avons enchaîné la fuite et le use-after-free de CVE-2026-109618 pour exécuter des commandes en pré-authentification dans notre laboratoire, sans identifiant et sans chaîne de gadgets.
Démontré Crashs de worker. Un déni de service reproductible ; des crashs répétés peuvent mettre un service hors ligne.
Plus difficile Un exploit pour chaque serveur. Les offsets propres au build et les valeurs de heap grooming diffèrent selon la cible, chacune demande donc un réglage : un ralentisseur que la divulgation de la version fait disparaître.

Que faire maintenant

Mettez PHP à jour. Les versions corrigées sont 8.2.35, 8.3.36, 8.4.27 et 8.5.12 (ou plus récentes). Les rétroportages des éditeurs peuvent aussi contenir le correctif. Redémarrez les workers PHP après la mise à jour. C'est le seul correctif complet.

Si vous ne pouvez pas mettre à jour aujourd'hui, les mesures suivantes réduisent le risque. Aucune ne remplace la mise à jour :

  • Définissez et imposez memory_limit=-1 dans la configuration serveur, puis redémarrez chaque worker et vérifiez qu'aucun réglage de pool, de répertoire ou d'application ne réintroduit une limite finie. Cela supprime un déclencheur, pas le bug.

  • Gardez postmaxsize bas et imposez la taille réelle des requêtes au niveau de votre proxy. Réduisez maxinputnesting_level et plafonnez les variables d'entrée, les parties, les en-têtes par partie et les uploads. Si votre application n'utilise pas d'upload, bloquez les requêtes multipart (y compris celles sans Content-Length) au niveau du proxy.

  • Mettez session.upload_progress.enabled=0 (à laisser désactivé même après le correctif si vous ne l'utilisez pas). Sur PHP 8.5, mettez aussi fatalerrorbacktraces=0. Sur PHP-FPM, pm.max_requests=1 aide si les performances le permettent. Redémarrez tous les workers.

  • Évitez les configurations qui affichent un avertissement avant le démarrage d'une requête : préférez les réglages appliqués au démarrage plutôt que des surcharges par requête, et supprimez les directives obsolètes.

  • Examinez votre application pour repérer les endroits où une entrée malveillante peut provoquer une erreur fatale ou un dépassement de délai (uploads, imports, sérialisation, gestionnaires d'erreur et d'arrêt).

  • Si vous passez par un CDN ou un WAF, consultez les mesures de contournement qu'il propose ; plusieurs éditeurs (Cloudflare, Fastly, Akamai, AWS et d'autres) en ont publié à titre provisoire.

À surveiller

  • Des requêtes multipart inhabituelles suivies d'erreurs de limite mémoire.

  • De nombreuses erreurs HTTP 500.

  • Des cookies de réponse PHPSESSID très volumineux ou malformés.

  • Des workers PHP qui crashent et redémarrent sans raison apparente.

Ces bugs opèrent sous votre application : ses journaux ne montreront souvent pas grand-chose. Fiez-vous aux journaux de votre serveur web et aux logs d'erreurs PHP.

D'autres failles de ce type à prévoir

Ce ne sont pas deux bugs isolés réglés par un patch net. Le correctif touche plusieurs pans de PHP, de la gestion du dépassement mémoire à l'état global conservé entre les requêtes. Wiz s'attend à d'autres variantes, ce qui ne signifie pas que les versions corrigées restent vulnérables. À ce jour, la mise à jour reste le correctif.

Pour les défenseurs, l'enjeu est de savoir quels systèmes exposés sur Internet font tourner une version de PHP vulnérable, et de le savoir en continu : ce type de bug appelle des attaques ciblées, et de nouvelles variantes continueront d'apparaître. Une version lue dans une page d'erreur ou un en-tête de réponse suffit à un attaquant pour commencer à aligner les offsets sur votre parc.

C'est exactement la faille que Patrowl est conçu pour combler. Notre surveillance continue de la surface d'attaque externe recense chaque actif exposé sur Internet, identifie la version de PHP qu'il expose (y compris via les erreurs verbeuses et les en-têtes qui offrent une longueur d'avance à l'attaquant) et signale ceux qui tournent encore sur une version vulnérable, en revérifiant à chaque nouveau bulletin de sécurité. Nos agents IA sondent cette surface chaque jour et un pentester humain valide chaque découverte : un serveur affecté et exposé, c'est donc vous qui le repérez et le corrigez en premier, pas un attaquant qui le trouve à votre place.

Questions fréquentes sur PHP2Shell

Qu’est-ce que PHP2Shell ?

PHP2Shell est le surnom de deux vulnérabilités critiques du cœur de PHP, CVE-2026-109618 et CVE-2026-109619, corrigées en octobre 2026. Elles laissent la mémoire dans un état incohérent quand PHP interrompt une opération, ce qui permet de faire crasher un worker ou d’exécuter du code à distance sans authentification.

Quelles versions de PHP corrigent PHP2Shell ?

Les versions corrigées sont PHP 8.2.35, 8.3.36, 8.4.27 et 8.5.12, ou plus récentes. Les rétroportages des distributions peuvent aussi contenir le correctif. Après la mise à jour, il faut redémarrer tous les workers PHP pour que le correctif soit actif.

PHP2Shell permet-il une exécution de code sans authentification ?

Oui. Wiz Research a démontré une exécution de code non authentifiée sur des installations par défaut de WordPress, Drupal et Joomla, et Patrowl a reproduit une chaîne d’exécution de code complète en laboratoire sur PHP 8.4.26 avec Apache mod_php.

Quel est le score CVSS de PHP2Shell ?

Le score CVSS est de 9.2, niveau critique. Le score de 10.0 annoncé avant la publication du correctif relevait de la spéculation, mais l’impact reste réel : exécution de code en pré-authentification dans le cœur de PHP.

Quelle est la différence entre CVE-2026-109618 et CVE-2026-109619 ?

CVE-2026-109618 couvre une corruption mémoire déclenchée par un dépassement de memory_limit pendant la lecture d’une requête. CVE-2026-109619 couvre un état global résiduel : un pointeur vers de la mémoire libérée réutilisé par la requête suivante sur le même worker (use-after-free).

WordPress, Drupal et Joomla sont-ils vulnérables à PHP2Shell ?

Oui, s’ils tournent sur une version de PHP non corrigée. La faille se situe dans PHP lui-même, pas dans le CMS : mettre à jour WordPress, Drupal ou Joomla ne suffit pas, c’est PHP

Sources & références

  • Avis de sécurité PHP : GHSA-w8hw-w9fx-j79w (CVE-2026-109618) et GHSA-6wmf-4rv5-p3rp (CVE-2026-109619).

  • Tous deux corrigés dans le commit 31c0ea7d7ff, « Avoid state corruption via bailout ».

  • Exécution de code démontrée et analyse des bugs : Wiz Research, « PHP2Shell ».

  • Tests pratiques : notre propre laboratoire. Reproductions de crashs sur PHP 8.4.x et 8.5.11 sous AddressSanitizer, et une preuve de concept publique d'exécution de code pour CVE-2026-109618 reproduite sur une cible PHP 8.4.26 standard (Apache mod_php).

Cet article décrit les vulnérabilités et leur impact. Il ne contient aucun exploit fonctionnel ni code d'attaque. Nos tests ont été réalisés sur des systèmes isolés.