# Kévin GUIOT — /blog/php2shell-deux-failles-critiques-rce-sur-php

# PHP2Shell : deux failles critiques ouvrent l'exécution de code à distance sur PHP

Kévin GUIOT · 2026-10-21 · 8 min · Sécurité

**Deux vulnérabilités critiques (CVE-2026-109618 et CVE-2026-109619), regroupées sous le nom PHP2Shell, permettent l'exécution de code à distance sans authentification sur quasiment toutes les versions de PHP. Analyse des mécanismes, versions corrigées et mesures à prendre.**

# PHP2Shell : deux failles critiques ouvrent l'exécution de code à distance sur PHP

* * *

## Ce qui vient de se passer

Le 21 octobre 2026, l'équipe PHP publiait les versions 8.2.35, 8.3.36, 8.4.27 et 8.5.12. Pas une release de routine : ces versions corrigent deux failles critiques d'exécution de code à distance — CVE-2026-109618 et CVE-2026-109619, collectivement surnommées **PHP2Shell** par la presse technique.

CVSS 9,2 chacune. Exploitable à distance. Sans authentification. Et les PoC fournis dans les advisories visent directement Apache + mod\_php en configuration par défaut — c'est-à-dire des millions de serveurs dans le monde.

Le point marquant — et ce que beaucoup de médias ont souligné — c'est que la faille s'exploite sans authentification ni même accès à l'application. Une simple requête HTTP vers un site suffit. C'est le premier cas complet documenté dans l'histoire moderne de PHP.

En pratique, la première question est « mon serveur tourne-t-il sous PHP, et en quelle version ? » — et la réponse tient souvent en une seconde : un [test de la version PHP de votre site](/php2shell) lit les indices publics que le serveur laisse aux visiteurs.

> « Ce ne sont pas des bugs dans votre site : c'est le moteur PHP lui-même qui est attaquable, avant même l'exécution de votre code. »

## CVE-2026-109618 : la corruption mémoire par OOM

**Root cause.** Quand PHP atteint sa limite mémoire (`memory_limit`), il effectue un `longjmp()` pour abandonner la requête. Or dans certains contextes, la mémoire de requête est atteignable **avant d'être complètement initialisée** : si une allocation échoue à ce moment précis, le `longjmp` laisse la mémoire dans un état invalide, et la procédure de shutdown la lit puis branche dessus — corruption, puis exécution de code.

**Le déclencheur sans authentification.** Le parsing multipart (RFC 1867, les formulaires avec upload) permet d'atteindre l'OOM tout en restant dans les limites apparentes : une requête multipart sans `Content-Length` exploitable passe le contrôle `post_max_size` tout en continuant à alimenter le body décodé ; le nombre de headers par part est illimité, ce qui fait exploser la mémoire interne par rapport à la taille réseau ; les contrôles de fichiers vides ne comptent pas dans `max_file_uploads` mais créent quand même les tableaux associés.

**Sensibilité par SAPI.** mod\_php est confirmé exploitable : il invoque le shutdown même si le startup a échoué, et l'exploit a été démontré sur une configuration prefork standard. FrankenPHP est probablement dans le même cas. PHP-FPM tue le worker si le startup échoue — susceptible via des chemins applicatifs, mais sans exploit complet démontré. LSAPI se comporte comme FPM.

**Le correctif.** Suppression pure et simple du `longjmp` OOM : OOM gracieux au prochain safe point de la VM, puis `_exit()` brutal au-delà d'un second seuil.

## CVE-2026-109619 : l'état global qui survit entre deux requêtes

**Root cause.** Les workers PHP conservent des états globaux (erreurs, session, objets, extensions) qui pointent vers la mémoire d'une requête. Si cet état n'est pas réinitialisé avant la libération du heap de requête, les opérations qui s'exécutent **avant l'initialisation de la requête suivante** consomment un pointeur périmé — use-after-free, exploitable par heap grooming.

**Le modèle producer → consumer**, souvent sur deux requêtes successives du même worker : une fatal error (parse error, `require` qui échoue, `Maximum execution time exceeded`, OOM) abandonne le reset et laisse l'état stale ; puis un consumer le lit avant que la requête suivante soit prête.

Deux consumers confirmés :

*   **Les diagnostics INI** exécutés avant `php_request_startup()` : `.htaccess`/`php_value` sous mod\_php, ou les paramètres `PHP_VALUE`/`PHP_ADMIN_VALUE` transmis par nginx ou mod\_proxy\_fcgi sous FPM. Une simple deprecation (`auto_detect_line_endings`, `filter.default`, `mbstring.http_input`…) suffit — et `display_errors=Off` ou `error_reporting` ne protègent pas : l'état est lu avant la décision de reporting.
*   **Le suivi d'upload de session** (`session.upload_progress`), activé par défaut, qui atteint le code session avant l'initialisation — `session_start()` n'est même pas requis.

## Qui est concerné

*   Tout site web exécutant PHP 8.2 avant 8.2.35, 8.3 avant 8.3.36, 8.4 avant 8.4.27 ou 8.5 avant 8.5.12
*   Toutes les versions encore plus anciennes : PHP 5, 7, 8.0 et 8.1 sont en fin de vie et ne recevront jamais le correctif officiel
*   Sites en hébergement mutualisé, WordPress, PrestaShop, applications métier Laravel ou Symfony, sites vitrines et vieux CMS
*   L'attaquant n'a besoin que de pouvoir envoyer une requête HTTP au serveur — pas d'accès à l'application, pas d'accès admin

Pour savoir si votre serveur est en ligne de mire, commencez par [vérifier la version PHP exposée par votre site](/php2shell) : les indices publics répondent en quelques secondes.

## Pourquoi la simple mitigation ne suffit pas

Les réglages de contournement existent mais aucun ne ferme réellement la porte :

*   `memory_limit=-1` casse le déclencheur OOM prévisible de la première faille — incomplet
*   `fatal_error_backtraces=0` (PHP 8.5) et `session.upload_progress.enabled=0` retirent des consumers de la seconde — mais n'importe quelle extension peut publier ou consommer du stale state
*   `pm.max_requests=1` empêche le chaînage entre requêtes d'un même worker — au prix des performances
*   Bloquer le multipart au niveau du frontend aide si les uploads sont inutilisés

**Attention au piège** : `RLIMIT_AS` et `RLIMIT_DATA` provoquent précisément le chemin `longjmp` vulnérable — ne les utilisez pas comme palliatif.

## Ce qu'il faut faire maintenant

*   **Inventorier** : pour chaque serveur, la version PHP et le SAPI (mod\_php vs FPM vs FrankenPHP). Le risque diffère selon le SAPI, mais tous les chemins exposés doivent être couverts.
*   **Patcher** : passer en 8.2.35, 8.3.36, 8.4.27 ou 8.5.12 selon votre branche — les paquets Debian/Ubuntu, RHEL et les images Docker officielles sont déjà à jour.
*   **Migrer les versions EOL** : si vous tournez en 7.4, 8.0 ou 8.1, aucun patch officiel n'arrivera. C'est le cas le plus urgent — et le plus fréquent en production.
*   **Vérifier les extensions** : une mise à jour PHP peut toucher plusieurs sites et certaines extensions doivent être contrôlées avant la bascule.

Vous ne connaissez pas la version PHP de votre serveur ? [Testez-la en quelques secondes](/php2shell) — puis faites le point : seule la mise à jour corrige vraiment ces deux failles.

## En synthèse

*   Deux failles RCE non authentifiées ont été publiées le 21 octobre 2026 dans PHP — un cas sans précédent dans l'histoire récente du langage.
*   Pratiquement toutes les versions PHP en production sont concernées ; seuls les patchs sortis aujourd'hui (8.2.35 à 8.5.12) corrigent réellement.
*   La mitigation par configuration est incomplète — surtout pour la seconde faille, où n'importe quelle extension peut republier du stale state.
*   Si vous exploitez PHP en production, l'heure est à la mise à jour. Pas la semaine prochaine : maintenant — [vérifiez votre version PHP en 10 secondes](/php2shell).
