La requête atteint PHP avant que votre contrôleur ne puisse agir
Votre application peut rejeter un envoi de fichier après que PHP a déjà traité une partie de son corps. C’est cet ordre qui justifie d’enquêter sur une vulnérabilité du runtime autrement que sur un contrôleur d’envoi non sécurisé. Pour CVE-2026-109618 et CVE-2026-109619, désignées publiquement sous le nom de PHP2Shell, la question centrale est de savoir où les données de requête contrôlées par l’attaquant franchissent une frontière à l’intérieur de l’interpréteur.
J’aborde cet incident en trois couches : la version de sécurité confirmée, le comportement du runtime visible dans le code source, et le mécanisme proposé par les implémentations examinées. Cette évaluation est datée du 21 octobre 2026 et repose sur l’analyse statique de PHP 8.4.4, sur le code pertinent de PHP 8.5.4, sur les preuves de concept examinées pour cette analyse, sur les informations publiées et sur une démonstration examinée. Aucune exploitation indépendante n’a été réalisée.
Ce que PHP a confirmé
L’avis de PHP du 20 octobre confirme deux vulnérabilités critiques, notées chacune 9,2 en CVSS v4.0, avec une version de sécurité prévue le 21 octobre à 14 h 00 UTC. L’avis préalable consulté réserve à cette publication les versions concernées, les versions corrigées et l’ensemble des détails techniques. Il n’identifie pas lui-même les numéros CVE et ne désigne pas l’upload progress comme cause racine. Les deux identifiants et la description d’une RCE figurent dans les informations publiques associées. [1, 2]
Le rapport de Huntress attribue à watchTowr la reproduction de CVE-2026-109618, tout en indiquant qu’au 20 octobre Huntress n’avait ni reproduit les failles ni observé d’attaques liées dans les environnements de ses partenaires. La démonstration examinée montre des effets compatibles avec l’exécution de commandes et la création de fichiers, y compris sous WordPress et Drupal. L’implémentation de la démonstration n’a pas pu être rapprochée de celles examinées ici. Ces observations justifient l’urgence, mais la cause exacte et les prérequis de déploiement restent à établir. [2]
Pourquoi l’upload progress fait partie de l’enquête
PHP peut tenir à jour, dans la session, des informations de progression pendant l’analyse d’un envoi multipart. L’état concerné comprend un compteur d’octets pour l’ensemble de la requête, des compteurs par fichier, des métadonnées et des indicateurs de fin. La fonctionnalité opère au sein du traitement de la requête : le formulaire d’envoi visible d’un site ne décrit donc pas entièrement sa surface d’attaque au niveau du runtime. Le manuel de PHP documente cette fonctionnalité et sa fréquence de mise à jour configurable. [3]
Dans le code examiné, FILE_START établit l’état du fichier, FILE_DATA met à jour les compteurs, et END termine ou nettoie le suivi. Le pointeur vers le total d’octets est mémorisé lors de la construction initiale ; le pointeur vers le fichier courant est renouvelé pour chaque fichier. Les écritures des compteurs précèdent la fonction utilitaire de mise à jour de la session. Cet ordre compte, car on ne peut pas supposer que l’authentification dans le code applicatif protège automatiquement une analyse antérieure. Un contrôle d’accès sur un proxy, avant que PHP ne reçoive la requête, impose une frontière différente. [4]
La chaîne proposée, étape par étape
Une des preuves de concept examinées associe le premier identifiant au compteur du total d’octets ; une autre associe le second au compteur du fichier courant. Toutes deux proposent un problème de pointeur périmé lors de mises à jour répétées de la progression. Ce sont les correspondances proposées par ces implémentations, pas des correspondances établies par l’avis de l’éditeur consulté. Leur modèle causal exige que la requête soit atteignable, qu’un état de progression soit créé, qu’une allocation soit libérée avant un accès ultérieur, et qu’un contrôle utile soit obtenu sur l’état corrompu qui en résulte.
Transformer cet état en RCE exige en outre un chemin d’exécution atteignable ainsi que des adresses et des structures compatibles avec le build. La démonstration affiche une étape de divulgation d’adresses ; l’extraction de symboles par le framework local n’établit pas une divulgation à distance équivalente. Des allocations existantes dans le tas ne prouvent pas la réutilisation ultérieure d’un emplacement libéré précis, et deux écritures différées sur le socket ne prouvent pas deux callbacks serveur correspondants. Ce sont des conditions techniques distinctes. Une chaîne fonctionnelle doit les relier ; les implémentations examinées laissent ce lien incomplet.
La question de la durée de vie : qui possède encore le tableau ?
Un use-after-free consiste à accéder à une allocation après la fin de sa durée de vie. Appelons P le tableau de progression actif et D un tableau de session décodé séparément. Un contenu équivalent n’en fait pas la même allocation. Sur le chemin de mise à jour réussie habituel de PHP 8.4.4, la fonction utilitaire incrémente le compteur de références avant de placer P dans l’arbre de session. La réinitialisation de cet arbre libère sa référence, tandis que l’état de progression possède toujours P. Remplacer le D décodé ne libère pas P. L’incrément correspondant existe aussi dans la fonction utilitaire examinée de PHP 8.5.4. [4, 5, 6, 7]
Ce modèle de propriété est l’étape non résolue de l’explication proposée. Il n’innocente aucune des deux CVE et ne réfute pas la démonstration distincte. Une explication complète de la cause racine doit identifier un autre chemin ou une autre condition qui met en échec la garantie de durée de vie. Même la séparation en fin de requête exige une analyse de l’ordre : avec le nettoyage désactivé, l’ancien arbre de session possède toujours le tableau d’origine lors de l’écriture finale du compteur, avant la mise à jour forcée et le démontage. Un pointeur qui devient périmé ne suffit pas sans accès ultérieur. [4]
L’empoisonnement de session franchit une autre frontière
Un rapport de laboratoire tiers examiné décrit une seconde voie vers l’exécution : un contenu de session contrôlé par l’attaquant qui atteint un point d’inclusion de fichier de l’application. L’endpoint de test, volontairement vulnérable, transmet directement un paramètre de requête à include(). Le profil de persistance désactive le nettoyage de la progression et modifie la cadence de mise à jour et les limites multipart. Le rapport consigne une exécution dans ces conditions. Cela illustre le type de frontière applicative à auditer, mais n’établit pas, à lui seul, le mécanisme de corruption mémoire de l’une ou l’autre des CVE.
Le même rapport décrit le choix de l’ID de session sans en-tête Cookie, une fixation de session dans un banc de connexion volontairement permissif, et un échec de sérialisation lié à un caractère barre verticale dans une clé de progression. Ces observations dépendent du gestionnaire, du sérialiseur, de la politique de session et du comportement de l’application. Le contenu de la session doit rester une donnée ; les chemins d’include/require doivent provenir d’une liste d’autorisations maîtrisée, et les changements d’état d’authentification doivent remplacer l’identifiant de session. Un enregistrement de progression ne démontre pas, à lui seul, la réutilisation d’une allocation libérée.
Le résultat uid=0 de la transcription décrit l’identité de son processus de laboratoire. Le lanceur démarre le serveur de développement PHP sans préciser d’utilisateur aux droits réduits ; cette sortie ne démontre ni élévation de privilèges ni accès root à l’hôte. Cette distinction compte sur le plan opérationnel : corriger un point d’inclusion non sûr et la politique de session traite l’exposition au niveau applicatif, tandis qu’une faille de sûreté mémoire du runtime exige toujours son propre correctif de l’éditeur.
Versions concernées et compatibilité de l’exploit sont deux questions différentes
L’avis de l’éditeur consulté ne fournit ni plage confirmée de versions concernées ni matrice des versions corrigées. Les affirmations générales couvrant PHP 5 et au-delà restent des affirmations issues des informations publiées. PHP 8.4.4 est notre référence pour l’analyse du code ; PHP 8.5.4 apparaît dans les éléments examinés et dans un profil affiché par la démonstration. Aucune de ces observations n’établit la plage complète des versions concernées. Considérez ceci comme un instantané des preuves, et appuyez-vous sur l’avis détaillé de l’éditeur pour vos décisions sur les paquets. [1, 8]
Une vulnérabilité peut toucher plusieurs branches alors qu’un exploit donné ne prend en charge qu’une architecture, un build de paquet ou une intégration serveur. À l’inverse, l’échec d’une PoC ne prouve pas qu’un déploiement est sûr. La démonstration comprend des profils décrits comme Apache prefork avec mod_php et PHP-FPM ; elle n’établit pas une couverture universelle des SAPI. Inventoriez le build et l’intégration qui servent réellement les requêtes, plutôt que de déduire l’exposition de la seule version du CMS ou d’un binaire CLI.
Le rapport d’expériences tiers examiné ne fait état d’aucune exécution de commande lors de ses tentatives de corruption mémoire sur php:8.4.4-cli, php:8.4.4-fpm et php:8.2.34-fpm, malgré des adresses résolues localement. Ce sont les résultats de l’auteur du rapport, pas des tests réalisés pour cet article. Ils n’établissent pas de versions sûres et ne justifient pas d’étendre l’exemple réussi du point d’inclusion aux installations PHP par défaut. L’auteur propose aussi un score CVSS v3.1 ; c’est une évaluation propre à l’auteur, pas une conversion de la note v4.0 émise par PHP.
Quels réglages modifient les conditions ?
Pour le chemin proposé, session.upload_progress.enabled contrôle le suivi ; name sélectionne le champ déclencheur ; freq et min_freq contrôlent les seuils de mise à jour ; cleanup contrôle la suppression des informations de progression après le traitement du POST. Les gestionnaires de session et la sérialisation influent sur la persistance. Ces réglages indiquent quoi examiner, pas une liste confirmée de prérequis pour les deux CVE. En particulier, on ne peut pas supposer que le nettoyage final empêche une erreur mémoire antérieure. [3, 9]
La SAPI, la mise en mémoire tampon du serveur, les limites du corps de requête et les délais d’attente influent sur le moment où les données atteignent le parseur. Une pause côté client peut ne pas survivre à la mise en tampon sous forme de pause entre deux callbacks PHP. Examinez la configuration effective dans le processus qui sert le web et dans chaque pool FPM ; la configuration CLI peut différer. Si l’upload progress n’est pas utilisé, envisagez de le désactiver par précaution. Les éléments examinés ici n’établissent pas ce changement comme un contournement complet pour l’une ou l’autre des CVE. [3, 9]
La configuration de laboratoire examinée révèle aussi un détail à vérifier : session.upload_progress.freq=1 est un intervalle absolu en octets, tandis que 1% est relatif à la taille de la requête. Son profil de test de corruption mémoire utilise la première forme, alors que les commentaires décrivent la cadence par défaut. Son profil de test de persistance fixe cleanup=0 et min_freq=0. Comparez les valeurs effectives, unités comprises, avant de généraliser des résultats de ces bancs d’essai à la production. [4, 9]
L’étendue des dégâts suit les droits du worker
Une exécution sous www-data peut déjà exposer une application. Selon les droits, le worker peut lire des identifiants de base de données, modifier du contenu accessible en écriture ou atteindre des services backend. Des identités de service partagées et des répertoires accessibles en écriture peuvent étendre l’impact à d’autres applications. Ce sont des conséquences à évaluer au regard du déploiement, pas l’affirmation que chacun de ces effets s’est produit dans la démonstration. La question utile est de savoir ce que le processus peut lire, modifier et atteindre.
Huntress recommande de surveiller les processus enfants inattendus de PHP et des workers du serveur web. Corrélez l’ascendance des processus avec les requêtes, les horodatages, les nouveaux fichiers et les connexions sortantes ; les applications légitimes lancent aussi des outils, et une exécution peut avoir lieu sans nouveau shell. Les vérifications de fichiers périmés du déclencheur examiné et le traitement incomplet des réponses FastCGI par le framework montrent aussi pourquoi le verdict d’un outil exige une attribution. L’exemple d’incident PHP publié auparavant par Huntress était explicitement sans rapport avec PHP2Shell et ne doit pas être requalifié en exploitation de ces CVE. [2]
Vérifier le correctif là où les requêtes s’exécutent réellement
Rattachez les endpoints publics aux paquets ou images PHP qui les servent, à leurs SAPI, à leurs extensions et à leur configuration effective. Incluez les logiciels tiers et les environnements d’hébergement qui embarquent PHP. Faites correspondre l’avis amont détaillé aux paquets de la distribution ou de l’éditeur, en tenant compte des correctifs rétroportés. Remplacez ou redémarrez les workers concernés, puis vérifiez que le build corrigé traite bien le trafic. Pour les installations sans mises à jour de sécurité maintenues, établissez un plan de mise à niveau ou de réduction de l’exposition.
L’objectif technique est de relier l’opération vulnérable, ses conditions atteignables et le code qui rétablit l’invariant requis. Un résultat négatif obtenu avec ces preuves de concept ne peut pas remplacer cette mise en correspondance. Ma démarche de vérification de la remédiation suit l’affirmation jusqu’au processus déployé et contrôle que le correctif pertinent est réellement en service. Si vous avez besoin de cette évaluation pour votre parc PHP, le travail commence par l’environnement en fonctionnement et ses preuves.
Sources
- [1] PHP : avis préalable de la version de sécurité
- [2] Huntress : rapport de réponse à PHP2Shell (texte fourni)
- [3] Manuel PHP : suivi de la progression des envois en session
- [4] PHP 8.4.4 : callbacks de session et propriété
- [5] PHP 8.4.4 : opérations sur les tables de hachage
- [6] PHP 8.4.4 : destruction avec comptage de références
- [7] PHP 8.5.4 : implémentation des sessions
- [8] watchTowr : FAQ sur la RCE de PHP
- [9] Manuel PHP : configuration des sessions
Besoin de vérifier ce point dans votre propre produit ?
Remédiation et vérification des correctifsVous voulez apprendre à trouver vous-même ce type de vulnérabilité ?
Formation individuelle à la revue de code sécurisé