Accueil

php2shell (CVE-2026-109618/109619) : corriger PHP et se protéger en attendant

Sécurité 12 min de lecture

Deux failles PHP notées 9.2, exploitables sans authentification avant tout code applicatif : versions corrigées, parades temporaires, vérification et lab.

L'essentiel

  • Deux CVE notées 9.2, corrigées en 8.2.35, 8.3.36, 8.4.27 et 8.5.12 ; PHP 8.1 et antérieurs n'ont plus de correctif officiel.
  • Exploitables sans authentification, avant tout code applicatif ; mod_php est la configuration la plus exposée.
  • Parade immédiate : session.upload_progress.enabled = Off dans php.ini ou le pool (pas .user.ini), puis remplacement de tous les workers.
  • Vérifiez chaque binaire (FPM, mod_php, CGI, panneau) : le php du terminal ne dit rien de PHP-FPM.
  • Le lab github.com/ikow/php2shell-poc sert à éprouver sa parade sur une machine isolée, jamais sur le système d'un tiers.

Le 20 octobre 2026, l'équipe sécurité de PHP a annoncé une publication d'urgence pour deux vulnérabilités notées 9.2 en CVSS v4.0, programmée le lendemain à 14:00 UTC, sans aucun détail technique. Les détails ont fuité avant l'heure, un outil baptisé « PHP2Shell » a circulé, et les versions corrigées sont sorties le 21 octobre : 8.2.35, 8.3.36, 8.4.27 et 8.5.12.

La meilleure lecture technique du moment est l'analyse de Qi Deng, « php2shell: Dissecting PHP's Critical Session Upload Progress Vulnerabilities », accompagnée d'un laboratoire reproductible publié sur GitHub : github.com/ikow/php2shell-poc. Cet article en reprend l'essentiel côté défense : ce qui est touché, ce qu'il faut mettre à jour, ce qu'on peut couper en attendant, et comment vérifier que la parade est réellement active.

Ce que dit l'avis officiel

L'avis publié sur php.net décrit deux défauts du moteur :

  • CVE-2026-109618 — exécution de code par corruption sur manque de mémoire. Une erreur d'allocation peut interrompre PHP alors que des données internes de la requête ne sont que partiellement initialisées ; le nettoyage de fin de requête les traite ensuite comme valides.
  • CVE-2026-109619 — exécution de code par état global périmé. Un état interne propre à une requête peut survivre à la libération de sa mémoire ; une requête suivante, traitée par le même worker, peut l'utiliser avant sa réinitialisation.

Le périmètre est large : toute installation PHP qui sert des requêtes web, qu'il s'agisse de PHP-FPM, d'Apache avec mod_php, de php-cgi, de FrankenPHP ou de LiteSpeed. L'avis juge le risque le plus élevé pour mod_php, avec une exploitation démontrée dans laquelle une requête non authentifiée déclenche l'attaque avant que le moindre code applicatif ne s'exécute.

La phrase à retenir de l'avis : il n'existe pas de parade complète, la mise à jour est le seul choix sûr. Les réglages temporaires réduisent l'exposition, ils ne rendent pas une installation sûre.

La surface d'attaque : session.upload_progress

La recherche publique converge vers une fonction que peu d'administrateurs connaissent : session.upload_progress. Introduite en PHP 5.4 et activée par défaut, elle enregistre dans la session l'avancement des envois de fichiers en multipart/form-data, pour alimenter une barre de progression.

Le danger tient à l'endroit où elle s'exécute : dans la couche SAPI, pendant l'analyse du corps de la requête, avant que l'application ne prenne la main. Concrètement :

  • aucune authentification n'est nécessaire ;
  • aucun code de l'application n'intervient ;
  • n'importe quel point d'entrée PHP qui accepte un POST suffit, même s'il n'a jamais prévu de recevoir un fichier ;
  • la fonction écrit dans les fichiers de session des données en partie choisies par l'émetteur de la requête.

Ce que la recherche de Qi Deng a mis au jour

En auditant le code source, le chercheur relève plusieurs défauts dans le traitement de la progression d'envoi :

  1. Fixation de session : l'identifiant de session peut être lu dans le corps de la requête sans tenir compte de session.use_only_cookies (correctif proposé dans la PR #22113).
  2. Nom de fichier non filtré : le nom du fichier envoyé est stocké tel quel dans les données de session.
  3. Use-after-free dans la désérialisation : un destructeur d'objet appelé pendant le nettoyage peut réentrer dans unserialize() et relire de la mémoire déjà libérée, ce qu'AddressSanitizer confirme.
  4. Sérialiseur de session non réinitialisé d'une requête à l'autre sous FPM.
  5. Destructeurs exécutés même quand le décodage de la session échoue.

Il en tire trois chaînes menant à l'exécution de code. Leurs prérequis sont ce qui compte pour évaluer votre exposition :

ChaîneCe qu'il faut côté application
Injection dans le fichier de session + inclusion de fichier localUne faille d'inclusion de fichier local (LFI), cas classique des extensions WordPress mal écrites
Use-after-free de la désérialisationUn appel à unserialize() sur des données atteignables (Laravel et Magento sont cités)
Désaccord de sérialiseur entre répertoiresDeux répertoires aux session.serialize_handler différents (via .user.ini, fréquent quand plusieurs applications cohabitent) et une classe exploitable déjà chargée, typiquement par opcache.preload

Les trois chaînes ont été vérifiées en laboratoire sous PHP 8.2.34. Une nuance s'impose : ces descriptions sont celles du chercheur, écrites avant la publication des détails officiels. Elles recoupent en partie l'avis : le sérialiseur qui survit d'une requête à l'autre rappelle « l'état global périmé » de la CVE-2026-109619. En revanche, l'avis décrit aussi un chemin par manque de mémoire que la recherche n'aborde pas. Tout le monde s'accorde sur deux points : mettre à jour, et désactiver session.upload_progress en attendant.

Suis-je concerné ?

Selon la recherche, toutes les versions depuis PHP 5.4.0 sont touchées, jusqu'à 8.5.11. Les versions corrigées sont 8.2.35, 8.3.36, 8.4.27 et 8.5.12. Les branches 8.1 et antérieures ne reçoivent plus de correctif du projet PHP.

Le piège classique consiste à vérifier le mauvais PHP. La commande php du terminal est le binaire CLI : elle ne dit rien de PHP-FPM, d'un mod_php chargé dans Apache ni des versions qu'un panneau d'hébergement embarque. Il faut interroger chaque binaire qui sert réellement du web :

# Quels PHP servent réellement des requêtes ?
ps -eo args | grep -E 'php-fpm|php-cgi|lsphp|frankenphp' | grep -v grep | sort -u
apache2ctl -M 2>/dev/null | grep -i php     # un mod_php chargé ?

# Version et réglages effectifs, binaire par binaire
php-fpm8.3 -v
php-fpm8.3 -i | grep -E 'session.upload_progress.enabled|session.use_only_cookies'

# Paquets corrigés disponibles (Debian/Ubuntu)
apt list --upgradable 2>/dev/null | grep -i php

Deux angles morts : -i ne montre pas les surcharges posées par pool FPM (php_admin_value) ou par virtual host, qu'il faut relire dans leurs fichiers ; et sur Ubuntu, le numéro amont ne bouge pas, puisque le correctif arrive comme une nouvelle révision du paquet. Un PHP « 8.3.6 » peut donc être corrigé ou non : seul l'avis de sécurité de la distribution permet de trancher. Debian 13, de son côté, a reçu directement la 8.4.27.

Mettre à jour : la seule correction

  • Paquets de distribution : installez la révision corrigée dès sa publication, puis redémarrez PHP-FPM, et Apache si mod_php est chargé. Tant que les anciens workers tournent, l'ancien code reste en mémoire.
  • Panneaux d'hébergement (Plesk, cPanel…) : ils livrent leurs propres builds, une par branche installée. Mettez-les toutes à jour, y compris celles qu'aucun site n'utilise plus.
  • Conteneurs : reconstruisez les images à partir d'une base corrigée ; redémarrer un conteneur ne change pas le PHP qu'il contient.
  • PHP 8.1 et antérieurs : l'équipe sécurité a publié des rétroportages minimaux en source pour 7.4, 8.0 et 8.1. Sinon, il faut passer par un fournisseur LTS ou migrer. Si vous dépendez du dépôt bénévole remirepo, l'avis prévient qu'il n'aurait pas de mise à jour le jour de la publication et conseille les paquets de la distribution ou la compilation.
  • PHP 8.2 arrive en fin de vie le 31 décembre 2026 : c'est le moment de planifier la suite.

En attendant le paquet : les parades temporaires

L'avis donne une parade par CVE. Celle de la CVE-2026-109619 est la plus simple et la plus utile :

; /etc/php/8.3/fpm/conf.d/99-php2shell.ini
; (même fichier dans cgi/conf.d et apache2/conf.d si ces SAPI sont utilisées)
session.upload_progress.enabled = Off
session.use_only_cookies = On
  • Le réglage doit s'appliquer avant l'analyse de la requête : dans php.ini, un pool FPM ou la configuration du serveur. Ni ini_set() ni un .user.ini ne conviennent, parce qu'ils interviennent trop tard.
  • Tous les workers doivent être remplacés : rechargement ou redémarrage de chaque service PHP-FPM, redémarrage d'Apache pour mod_php.
  • Sous PHP 8.5, ajoutez fatal_error_backtraces = 0, qui ferme un autre chemin confirmé.
  • La recherche recommande aussi un session.serialize_handler identique dans tous les répertoires d'un même serveur.

La parade de la CVE-2026-109618 est plus délicate : memory_limit = -1 imposé par php_admin_value (donc non surchargeable par l'application), un post_max_size bas (8M sans envoi de fichiers, 32M sinon, upload_max_filesize en dessous), des limites de taille et de nombre de parties côté serveur web, et une limite mémoire par cgroup ou conteneur comme filet. L'avis précise de ne pas recourir à RLIMIT_AS ni RLIMIT_DATA, qui peuvent justement provoquer l'échec d'allocation vulnérable.

Mon arbitrage : la première parade se pose en quelques minutes, sans effet de bord quand aucune application n'utilise la progression d'envoi. La seconde ne doit pas s'improviser en production : retirer la limite mémoire de PHP sans limite cgroup en face, c'est échanger une faille contre un risque d'épuisement de la RAM du serveur. Je ne l'ai pas posée à ce stade.

Vérifiez ensuite, au lieu de supposer :

# Une application utilise-t-elle la progression d'envoi ?
grep -rn --include='*.php' -E 'upload_progress|PHP_SESSION_UPLOAD_PROGRESS' /var/www

# Configuration valide, rechargement, valeur effective
sudo php-fpm8.3 -t && sudo systemctl reload php8.3-fpm
php-fpm8.3 -i | grep session.upload_progress.enabled   # attendu : Off => Off

Retour d'expérience : ce qu'on oublie en durcissant un parc

J'ai posé cette parade le jour même sur une quinzaine de binaires PHP répartis sur plusieurs serveurs. Le réglage lui-même tient en une ligne. Le vrai travail a été de savoir où le poser :

  • Le mod_php oublié. Un Apache chargeait encore mod_php alors que les sites passaient par PHP-FPM. Il n'apparaît pas sous le nom « php » dans la liste des processus, et c'est pourtant la configuration que l'avis juge la plus exposée.
  • Les versions fantômes des panneaux. Un panneau d'hébergement installe plusieurs branches en parallèle, dont des branches en fin de vie que plus aucun site n'utilise. Chacune a son propre répertoire de configuration, et chacune doit recevoir la parade.
  • Les machines oubliées. Une VM en PHP 8.1, sans correctif possible, redémarrait automatiquement avec son hôte et tournait depuis plusieurs jours. Elle a été arrêtée plutôt que durcie : la meilleure parade reste de ne pas exposer ce qu'on n'utilise pas.
  • Le test avant/après. Pour chaque serveur, une liste d'URL représentatives, avec le code HTTP et la taille de la réponse relevés avant et après. C'est ce test qui a montré qu'un durcissement voisin, cgi.fix_pathinfo=0, cassait les routes /index.php/wp-json/ de WordPress sous Plesk : il a été retiré. La désactivation de session.upload_progress, elle, n'a changé aucune réponse.
  • La note de sortie. Chaque fichier de parade est consigné, avec la décision à prendre après la mise à jour : le retirer ou le garder. Pour session.upload_progress, aucune application ne s'en sert : le garder ne coûte rien.

Repérer une tentative

Quelques signaux à surveiller, sans illusion : leur absence ne prouve rien.

  • Des POST multipart/form-data vers des URL qui ne reçoivent jamais de fichier.
  • Le champ nommé par session.upload_progress.name (PHP_SESSION_UPLOAD_PROGRESS par défaut) dans des requêtes vers des applications qui ne l'utilisent pas. Si votre WAF inspecte les corps de requête, c'est une règle simple à poser, partielle comme les autres parades.
  • Des workers PHP-FPM qui meurent sur signal : grep -E 'exited on signal (6|11)' /var/log/php8.3-fpm.log.
  • Des fichiers au contenu inattendu dans le répertoire session.save_path.
  • Des processus lancés par l'utilisateur du serveur web (un shell, curl, wget), que l'observabilité eBPF avec Falco détecte au moment où ils démarrent.

Le lab : tester sa parade, pas la production des autres

Le dépôt ikow/php2shell-poc fournit un environnement Docker (PHP 8.2.34 volontairement vulnérable, en FPM derrière nginx), trois scripts de vérification (un par chaîne, sans charge offensive selon son auteur) et une analyse technique détaillée dans docs/ANALYSIS.md.

Pour un administrateur, son intérêt est de rejouer le scénario sur une machine isolée, d'appliquer la parade, puis de constater ce qu'elle couvre et ce qu'elle ne couvre pas. Le chercheur comme l'avis officiel le disent : couper la progression d'envoi ne ferme pas tous les chemins. Le lab permet de le constater concrètement, et c'est la meilleure preuve que seule la mise à jour corrige tout.

Précautions : une VM jetable, aucun port du lab exposé (il est vulnérable par construction), et jamais les scripts pointés vers un système qui n'est pas le vôtre ou pour lequel vous n'avez pas d'autorisation écrite.

La leçon

Une fonction activée par défaut depuis 2012, dont presque personne ne se sert, a fourni toute la surface d'attaque. C'est l'argument le plus concret qui soit pour désactiver ce qu'on n'utilise pas, avant qu'une CVE ne le rappelle. Le second enseignement est moins visible : le jour d'une faille critique, appliquer le correctif prend dix minutes, alors que savoir quels PHP tournent, et où, peut prendre la journée. Cet inventaire se prépare à froid, comme le reste de la checklist de sécurité d'un serveur Linux.

Le scénario n'est pas nouveau : une couche de framework atteignable avant toute authentification, comme pour React2Shell côté Next.js. Plus le code exécuté avant votre application est volumineux, plus il compte dans votre surface d'attaque.

Sources

Questions fréquentes

Désactiver session.upload_progress suffit-il ?
Non. L'avis officiel précise que ce réglage bloque les chemins confirmés passant par la session, pas les autres variantes, et qu'aucune parade ne rend une installation sûre. Il réduit l'exposition le temps d'installer 8.2.35, 8.3.36, 8.4.27 ou 8.5.12, ou la révision corrigée de votre distribution.
Mon application utilise-t-elle la progression d'envoi de PHP ?
Rarement. Les barres de progression modernes s'appuient sur les événements de progression côté navigateur, pas sur cette fonction. Un grep -rn -E 'upload_progress|PHP_SESSION_UPLOAD_PROGRESS' sur vos sources tranche la question : sans résultat, la désactivation n'a aucun effet visible.
Ma distribution affiche toujours PHP 8.3.6 : suis-je corrigé ?
Le numéro amont ne suffit pas. Ubuntu rétroporte les correctifs dans une nouvelle révision du paquet sans changer la version affichée. Fiez-vous à l'avis de sécurité de la distribution et à la révision installée, puis vérifiez que les services PHP ont bien été redémarrés.
Je suis encore en PHP 8.1 ou plus ancien : que faire ?
Le projet PHP ne publie plus de version pour ces branches. L'équipe sécurité a mis à disposition des rétroportages minimaux en source pour 7.4, 8.0 et 8.1 ; à défaut, passez par un fournisseur LTS ou migrez. En attendant, appliquez les parades temporaires, et arrêtez ce qui n'a pas besoin de tourner.
Peut-on lancer les scripts du lab contre sa production ?
Mieux vaut s'en abstenir. Le lab est conçu pour un environnement Docker isolé, et ses scripts écrivent des données de session sur la cible pour solliciter du code dont les défauts relèvent de la corruption mémoire. Reproduisez votre configuration dans une VM jetable, appliquez la parade et observez le résultat. Ne visez jamais un système sans autorisation écrite.

Cet article vous a plu ?

Cet article vous a-t-il été utile ?

Merci pour votre retour !

Commentaires

MR
Morgann Riu

Expert en cybersécurité et administration Linux. J'aide les entreprises à sécuriser et optimiser leurs infrastructures critiques.

CVE PHP RCE PHP-FPM session.upload_progress sécurité durcissement

Recommandé pour vous

Retour au blog
↑