PHP2Shell: qué hacer cuando sale un CVE crítico en PHP

David TorresInformática y FPLeave a Comment

Los cinco pasos ante un CVE crítico: leer el aviso, comprobar si te afecta, actualizar, reiniciar y verificar

Qué ha pasado esta semana

El miércoles 21 de octubre de 2026, el equipo de PHP publicó las versiones 8.5.12, 8.4.27, 8.3.36 y 8.2.35. Corrigen dos vulnerabilidades que permiten a alguien ejecutar código en tu servidor sin necesidad de iniciar sesión: le basta con mandar peticiones HTTP preparadas a propósito.

  • CVE-2026-109618: cuando PHP se queda sin memoria a mitad de una petición, puede dejar datos internos a medio preparar, y la limpieza posterior los trata como si fueran válidos.
  • CVE-2026-109619: parte del estado interno de una petición puede sobrevivir después de liberarse su memoria, y la siguiente petición que atiende el mismo proceso puede usarlo.

No necesitas entender los detalles de memoria para actuar. Lo que importa del aviso oficial es esto: afecta a todo PHP que sirve páginas web (PHP-FPM, Apache con mod_php, php-cgi, FrankenPHP y LiteSpeed), y el riesgo más alto es el de mod_php, donde se ha demostrado un ataque que funciona antes de que llegue a ejecutarse el código de tu aplicación. Da igual lo bien escrita que esté tu web.

Lo que vas a leer sirve para este caso, pero sobre todo para el siguiente, sea de PHP, de Node, de Java o de tu sistema operativo. Son cinco pasos, siempre en este orden.

1 · Lee el aviso, no la noticia

Esta semana se han publicado decenas de artículos sobre PHP2Shell, y no dicen lo mismo. Unos aseguran que se está explotando de forma activa; CISA, la agencia de ciberseguridad de Estados Unidos, decía el día 21 que no lo había observado; Rapid7 hablaba de intentos de explotación y de pruebas de concepto públicas que solo funcionan en combinaciones concretas de sistema, versión y servidor web. Hay incluso alguna web que da rangos de versiones afectadas distintos de los oficiales.

La noticia sirve para enterarte. Para decidir, vete a la fuente: el aviso de quien mantiene el software. En este caso, la portada de php.net y los dos avisos de seguridad del repositorio de PHP en GitHub (GHSA-w8hw-w9fx-j79w y GHSA-6wmf-4rv5-p3rp).

Qué te dice cada parte

Un aviso de seguridad tiene casi siempre las mismas piezas. Estas son las del de PHP:

ParteEn este avisoPara qué te sirve
Identificador CVECVE-2026-109618, CVE-2026-109619Es el nombre común del fallo: con él lo buscas en tu distribución, en tu proveedor y en cualquier herramienta
Gravedad (CVSS)9.2 sobre 10, críticaCuánta prisa hay
Vector CVSSAV:N/AC:H/AT:P/PR:N/UI:N/…Por qué tiene esa nota
Versiones afectadasAnteriores a 8.2.35, 8.3.36, 8.4.27 y 8.5.12Si tienes que mirar tu sistema
Versiones corregidas8.2.35, 8.3.36, 8.4.27 y 8.5.12A qué tienes que llegar
Qué hacerActualizar; mitigaciones si no puedesTu lista de tareas

El vector merece medio minuto, porque explica la nota mejor que la nota. AV:N significa que el ataque llega por la red; PR:N, que no hacen falta credenciales; UI:N, que no necesita que nadie haga clic en nada. Eso es lo que lo hace grave. Lo que le impide llegar al 10 es AC:H y AT:P: el ataque es complicado y necesita que se den ciertas condiciones. Por eso las primeras pruebas de concepto solo funcionaban en entornos concretos. Pero que hoy sea difícil no quiere decir que mañana lo vaya a seguir siendo.

Fíjate también en lo que el aviso no dice. No dice «si no usas sesiones, estás a salvo», ni «si tu aplicación está bien programada, no te afecta». Lo que dice es que solo actualizar es una solución completa.

2 · ¿Te afecta? La versión que cuenta

La pregunta parece fácil: ¿qué versión de PHP tengo? Y la primera respuesta que da todo el mundo es esta:

$ php -v
PHP 8.4.25 (cli) (built: Sep 24 2026 09:20:04) (NTS clang 15.0.0)

Fíjate en (cli). Ese es el PHP de la terminal, el que ejecuta tus scripts cuando escribes php algo.php. No es necesariamente el que sirve tu web. En un mismo equipo puede haber varios PHP: en el Mac en el que se ha preparado este artículo, Herd tiene un PHP 8.4 y un PHP 8.5, y cada web local puede usar uno distinto. En un servidor Ubuntu es habitual que la terminal tenga una versión y PHP-FPM otra.

La versión que cuenta es la del proceso que atiende las peticiones, y la forma más directa de verla es preguntárselo a él. Crea un archivo temporal en tu web:

<?php echo PHP_VERSION, " ", PHP_SAPI, "\n";

Al abrirlo desde el navegador o con curl, te contesta el PHP que sirve la web, no el de tu terminal. En un servidor de prueba con Debian, nginx y PHP-FPM:

$ curl http://localhost/version.php
8.4.24 fpm-fcgi

fpm-fcgi confirma que ha contestado PHP-FPM. Bórralo en cuanto lo hayas visto: dejar en internet un archivo que publica tu versión exacta es darle a quien te ataca la mitad del trabajo hecho. Por la misma razón, phpinfo() no se deja nunca en un servidor público.

Con la versión en la mano, compárala con la tabla del aviso. 8.4.24 es anterior a 8.4.27: te afecta.

Hay una excepción que tienes que conocer: las distribuciones de Linux a veces corrigen sin cambiar el número de versión, porque aplican solo el parche sobre la versión que ya tenían (lo que se llama backport). En este caso Debian sí ha subido a 8.4.27 en trixie y a 8.2.35 en bookworm, pero no lo des por hecho: busca el CVE en el sistema de seguimiento de seguridad de tu distribución (el security tracker de Debian, los avisos USN de Ubuntu), que te dice qué paquete exacto lo corrige.

El PHP que no sabes que tienes

El inventario es la parte que más se subestima. CISA pide incluir expresamente el PHP que va dentro de otro software, y los avisos que publicó INCIBE-CERT el 23 de octubre lo dejan claro: fabricantes como QNAP, Synology, Cisco o Palo Alto Networks tienen productos afectados porque llevan PHP dentro. El panel web de un NAS o de un cortafuegos también es una web.

Y está el PHP de tu propio equipo:

  • XAMPP. El instalador más reciente que ofrece su web para Windows y Linux trae PHP 8.2.12, de finales de 2023, y el de macOS, PHP 8.2.4. No se actualiza solo, y el proyecto lo dice claramente en sus preguntas frecuentes: «XAMPP is not meant for production use but only for development environments», no está pensado para producción. Para aprender en tu portátil está bien; para dar servicio en internet, no.
  • Herd, MAMP, Laragon y compañía: entornos de desarrollo que tampoco se actualizan solos. En el Mac de este artículo, Herd seguía en 8.4.25 y 8.5.10 dos días después del aviso.
  • Imágenes Docker: cada contenedor lleva su propio PHP, que no tiene nada que ver con el del equipo que lo ejecuta.

3 · Actualiza

Cómo se actualiza depende de cómo lo instalaste, y por eso conviene saberlo antes de que haga falta.

Con paquetes de la distribución (Debian, Ubuntu), es apt update y apt upgrade. Antes, apt list --upgradable te enseña qué hay pendiente:

$ apt list --upgradable
php8.4-fpm/stable-security 8.4.27-1~deb13u1 arm64 [upgradable from: 8.4.24-1~deb13u1]

stable-security es el repositorio de seguridad de Debian. Si no lo tienes configurado, la actualización no te llega.

En los demás casos:

  • Docker: se cambia la imagen. Si tu compose.yaml dice image: php:8.4-fpm, docker compose pull descarga la versión nueva de esa etiqueta.
  • Hosting compartido: lo actualiza el proveedor. Varios grandes proveedores lo anunciaron esos mismos días; si el tuyo no dice nada, pregúntale.
  • XAMPP, Herd y similares: con su propio actualizador o descargando la versión nueva.
  • PHP 8.1 o anterior: el proyecto PHP ya no publica versiones para ellas. Para 7.4, 8.0 y 8.1 ha dejado parches mínimos para quien compila desde el código fuente, y algunas distribuciones y proveedores de soporte extendido ofrecen los suyos. Pero la solución de verdad es pasar a una versión con soporte. Aunque 8.2 está corregida, recibe solo parches de seguridad hasta el 31 de diciembre de 2026: si estás en ella, este es buen momento para planificar el salto.

4 · Reinicia: el paso que se olvida

Actualizar cambia los archivos que hay en el disco. El proceso que ya estaba en marcha sigue con el código que cargó al arrancar. Es la diferencia entre programa y proceso: el programa es el archivo, el proceso es ese programa ejecutándose, con su copia en memoria.

Con Docker se ve muy claro, y es donde más gente cae. En esta sesión, el contenedor se creó con una imagen php:8.4-fpm descargada antes del parche, en la que venía PHP 8.4.24:

$ docker compose exec php php -v
PHP 8.4.24 (cli) (built: Aug 25 2026 00:36:30) (NTS)

$ docker compose pull
 Image php:8.4-fpm Pulled

$ docker compose exec php php -v
PHP 8.4.24 (cli) (built: Aug 25 2026 00:36:30) (NTS)

Has descargado la imagen nueva y tu contenedor sigue en 8.4.24. docker compose ps te da la pista: la columna de la imagen ya no dice php:8.4-fpm, sino un sha256:…, porque la etiqueta ahora apunta a la imagen nueva y tu contenedor sigue usando la anterior. Hay que recrearlo:

$ docker compose up -d
 Container tienda-php-1 Started

$ docker compose exec php php -v
PHP 8.4.27 (cli) (built: Oct 21 2026 15:58:17) (NTS)

En un servidor Debian o Ubuntu con paquetes, el propio paquete de PHP-FPM reinicia el servicio al actualizarse: lo hace su script de instalación. Pero no todo lo que usa PHP se reinicia solo, y el aviso de PHP insiste en reiniciar todos los procesos de PHP. La herramienta que te lo dice sin tener que adivinar es needrestart: después de actualizar, lista los servicios que siguen ejecutando código viejo. En una prueba con PHP-FPM sin reiniciar:

$ needrestart -r l
…
Services to be restarted:
 invoke-rc.d php8.4-fpm restart

5 · Comprueba

No des la tarea por terminada hasta que el proceso que sirve la web te diga la versión nueva. Repite la comprobación del paso 2, con el archivo temporal o dentro del contenedor, y bórralo después.

Si comparas con lo que dice la terminal, puedes encontrarte esto. Es el mismo servidor de prueba del paso 2, después de actualizar PHP y sin reiniciar PHP-FPM:

$ php -v
PHP 8.4.27 (cli) (built: Oct 21 2026 07:53:46) (NTS)

$ curl http://localhost/version.php
8.4.24 fpm-fcgi

La terminal ya está en la versión corregida y la web sigue en la vulnerable. Si solo hubieras mirado php -v, habrías dado por arreglado algo que seguía abierto. Al reiniciar PHP-FPM, la misma petición devuelve 8.4.27 fpm-fcgi.

Si hoy no puedes actualizar

A veces no se puede actualizar en el momento: un servidor de un cliente, una aplicación que no arranca con la versión nueva… El aviso de PHP propone unas medidas temporales, y avisa de que reducen la exposición, pero no hacen segura la instalación:

  • Contra CVE-2026-109618: poner memory_limit=-1 de forma que la aplicación no pueda cambiarlo, bajar post_max_size (8M si la web no recibe archivos, 32M si los recibe) y limitar en el servidor web o en el proxy el tamaño y la complejidad de las peticiones.
  • Contra CVE-2026-109619: session.upload_progress.enabled=0 en php.ini o en la configuración del pool, no con ini_set(), y reiniciar todos los procesos. En PHP 8.5, también fatal_error_backtraces=0.
  • Si un sistema no se puede actualizar, quitarle el acceso desde internet. CISA va más allá: recomienda desconectar los sistemas con PHP 8.1 o anterior.

Esconder la versión no es una mitigación. Ocultar la cabecera X-Powered-By (que la imagen oficial de Docker envía por defecto, porque no trae php.ini y expose_php vale On) le complica algo la vida a quien busca objetivos, pero el fallo sigue ahí.

No es solo PHP: tus dependencias

El PHP de tu servidor es una pieza más. Las bibliotecas que instalas con Composer o con npm también tienen CVE, y lo normal es que un proyecto tenga decenas de ellas. Lo que funcionaba el año pasado puede tener hoy un aviso publicado.

Hay un comando para cada gestor de paquetes que compara lo que tienes instalado con la base de datos de avisos. Este es un proyecto con la biblioteca Guzzle en la versión 7.4.4, instalada cuando todavía no tenía avisos:

$ composer audit
Found 11 security vulnerability advisories affecting 1 package:
+-------------------+----------------------------------------------------------+
| Package           | guzzlehttp/guzzle                                        |
| Severity          | high                                                     |
| CVE               | CVE-2022-31090                                           |
| Title             | CURLOPT_HTTPAUTH option not cleared on change of origin  |
| Affected versions | >=7,<7.4.5|>=4,<6.5.8                                    |
+-------------------+----------------------------------------------------------+
…

Once avisos en una sola biblioteca, desde 2022 hasta agosto de 2026. Se arregla actualizándola:

$ composer require -W guzzlehttp/guzzle:^7.15.2
  - Upgrading guzzlehttp/guzzle (7.4.4 => 7.16.0)
  - Upgrading guzzlehttp/promises (1.5.3 => 2.6.0)

$ composer audit
No security vulnerability advisories found.

La -W no está ahí por decorar. Sin ella, Composer se niega, porque la versión nueva de Guzzle necesita también una versión nueva de guzzlehttp/promises, otra biblioteca de la que depende, y sin -W no la toca. Con -W actualiza las dos.

En JavaScript, lo mismo con npm audit, y npm audit fix aplica las actualizaciones que no rompen la compatibilidad:

$ npm audit
lodash  <=4.17.23
Severity: high
Command Injection in lodash - https://github.com/advisories/GHSA-35jh-r3h4-6jhm
…
fix available via `npm audit fix`

Dos detalles que te van a servir:

  • Las versiones recientes de Composer ya se niegan a instalar una versión con avisos conocidos. Eso te protege al instalar, pero no avisa de lo que ya tenías: un aviso nuevo no cambia tu proyecto, cambia la base de datos. Por eso hay que volver a auditar de vez en cuando.
  • composer audit termina con un código de error cuando encuentra avisos. Eso permite meterlo en tu pipeline de integración continua para que el build salga en rojo si hay una dependencia vulnerable, igual que sale en rojo si no compila.

Cómo enterarte antes

Esta semana te has enterado por las noticias. La próxima vez, mejor que te llegue el aviso sin tener que buscarlo:

  • La lista de anuncios del propio proyecto. PHP tiene php-announce, una lista de muy poco volumen (solo anuncia versiones) a la que te suscribes desde php.net/mailing-lists.php. El día 20, un día antes de publicar las versiones, avisó de que venía una versión de seguridad crítica para que la gente se preparara. Casi todos los proyectos importantes tienen algo parecido: sigue el de lo que uses.
  • INCIBE-CERT, el equipo de respuesta de INCIBE: publica avisos de seguridad en español y puedes suscribirte a ellos. Para la gente de Sistemas, es la referencia en España.
  • El catálogo KEV de CISA (Known Exploited Vulnerabilities): lista las vulnerabilidades que se sabe que se están explotando. Si algo tuyo aparece ahí, la prioridad pasa por delante de todo lo demás.
  • Los avisos de tu distribución: la lista de seguridad de Debian, los USN de Ubuntu…
  • En tus repositorios: GitHub avisa de las dependencias vulnerables con Dependabot. Actívalo en tus proyectos de clase y te llegará el correo antes que la noticia.

No hace falta leerlo todo. Hace falta saber qué usas y seguir las fuentes de eso.

Hábitos que te ahorran el próximo susto

Lo que de verdad cambia las cosas no es reaccionar rápido el día del aviso, sino tener hecho el trabajo antes:

  • Saber qué tienes. Una lista de tus servidores y tus proyectos, con qué versión de cada cosa usan. El día del aviso, el inventario es lo que más tiempo hace perder.
  • Usar versiones con soporte. Consulta las fechas de fin de soporte del lenguaje, del sistema operativo y de la base de datos (php.net/supported-versions.php en el caso de PHP). Una versión sin soporte no recibe el parche, y el aviso te llega igual.
  • Actualizar a menudo, de poco en poco. Pasar de 8.4.24 a 8.4.27 es un trámite. Pasar de 7.4 a 8.4 con prisa porque hay un CVE crítico es un proyecto.
  • Activar las actualizaciones de seguridad automáticas en los servidores donde tenga sentido (en Debian y Ubuntu, unattended-upgrades).
  • Exponer solo lo que tiene que estar expuesto. El panel de administración, la base de datos o el XAMPP de pruebas no tienen por qué ser accesibles desde internet.
  • Mínimo privilegio. Si alguien consigue ejecutar código, lo hace con los permisos del proceso de PHP. Un PHP que se ejecuta con un usuario sin privilegios limita lo que puede hacer.
  • Secretos fuera del código y copias de seguridad que se han probado a restaurar. Si el día del aviso llegas tarde, son lo que limita el daño.
  • Auditar las dependencias en el pipeline, para que el aviso te llegue con el commit y no con la noticia.

Errores frecuentes

Los que más se oyen en clase cuando sale un CVE:

  • «Mi código está bien escrito, no me afecta.» Este fallo está en PHP, no en tu aplicación, y con mod_php se puede explotar antes de que se ejecute una sola línea tuya.
  • «He mirado php -v y ya tengo la versión buena.» Ese es el PHP de la terminal. La versión que cuenta es la del proceso que sirve la web.
  • «He actualizado, ya está.» Actualizar sin reiniciar o sin recrear el contenedor deja en marcha el código viejo. Comprueba siempre en el proceso que sirve.
  • «Un CVE crítico quiere decir que me han atacado.» Quiere decir que se puede atacar. Si te han atacado, lo dirán tus registros. Aun así, en este caso no ver nada en los registros no prueba que estés a salvo, como advierte Rapid7.
  • «Es mi XAMPP de clase, me da igual.» En tu portátil y sin acceso desde fuera, el riesgo es bajo. El problema empieza cuando ese XAMPP acaba sirviendo el proyecto de alguien en internet.
  • Fiarse del titular. Las noticias se contradicen en si hay explotación activa o qué versiones se ven afectadas. Decide con el aviso oficial.

Ejercicio: audita tu equipo en cinco minutos

Hazlo en tu propio equipo, con lo que uses para clase. Apunta cada resultado.

  1. Inventario. Haz una lista de dónde tienes PHP: ¿XAMPP, Herd, MAMP, Docker, un servidor de prácticas? No olvides el NAS o el router si tienen panel web.
  2. La terminal. Ejecuta php -v y apunta la versión, fijándote en (cli).
  3. La web. Crea el version.php de este artículo en tu web local y ábrelo. ¿Coincide con la de la terminal? Bórralo al terminar.
  4. Compara. ¿Tu versión es anterior a 8.2.35, 8.3.36, 8.4.27 u 8.5.12? Si estás en 8.1 o antes, apúntalo aparte: esa versión ya no recibe parches.
  5. Tus dependencias. En un proyecto tuyo con composer.json, ejecuta composer audit; en uno con package.json, npm audit. Apunta cuántos avisos salen.
  6. Suscríbete a una fuente de avisos de algo que uses de verdad.

Si en el paso 3 te ha salido una versión distinta de la del paso 2, ya has entendido lo más importante del artículo.

Por dónde seguir

Cada paso tiene su vídeo de un minuto:

  • Cómo leer un aviso de seguridad: CVE, CVSS, versiones afectadas y corregidas, sobre el aviso real de PHP.
  • docker pull no actualiza tu web: la imagen nueva y el contenedor viejo.
  • Tus dependencias también tienen CVE: composer audit y npm audit, con salida real.

Y si quieres llevar al día tus proyectos de clase o preparar los módulos de despliegue y seguridad, en SASE Formación damos clases de informática y programación, presenciales y online. La primera clase de prueba es gratuita, y en informática las clases son individuales. Escríbenos y nos cuentas por dónde vas: saseformacion.es.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *