php2shell, parte 2: lo que el parche de octubre no cerró

El parche del 21 de octubre cerró CVE-2026-109618 y CVE-2026-109619, pero CVE-2026-115560, que PHP anunció dos días después, todavía no tiene corrección. Se mitiga con una línea de configuración y solo afecta a una combinación rara de código, así que el riesgo real es bajo. Lo que sí va a pesar en las próximas semanas es el ruido: van a circular demostraciones que prueban menos de lo que dicen sus títulos. Esta segunda parte cubre lo que sigue abierto, cómo leer esas afirmaciones de gravedad y cómo comprobar tu exposición sin conseguir una shell. No es una guía de explotación: no hay payloads ni pasos de reproducción.
Si llegas sin contexto, la primera parte explica las dos RCE críticas, a qué versión actualizar y las mitigaciones: php2shell: las dos vulnerabilidades críticas de PHP.
Por qué un parche puede dejar vecinos abiertos
Un parche arregla una ruta concreta. El de CVE-2026-109618, según su advisory, cambió cómo PHP maneja el out-of-memory: ya no sale con un salto largo (longjmp()) en medio de datos a medio inicializar, sino con un límite blando que se aplica en el siguiente punto seguro y un límite duro que termina el proceso. Eso cierra esa ruta. Lo que no toca es el código que no pasa por ahí, como el manejo del formato de las sesiones. Ahí vive el hueco que PHP ya reconoció y todavía no corrigió.
CVE-2026-115560 sigue sin parche
El 23 de octubre, PHP publicó un aviso informativo sobre CVE-2026-115560, otra RCE por el lado de session upload progress. Según el aviso, solo aparece con una combinación poco común: la aplicación define session.serialize_handler como php_binary o php_serialize en la configuración, después lo cambia a php con ini_set() antes de llamar a session_start(), y session upload progress sigue activo.
PHP no saca un release de emergencia por esto: lo corrige en el próximo release regular y publicará entonces el advisory completo. Hasta ese momento, la recomendación es session.upload_progress.enabled=0, la misma medida que el advisory de CVE-2026-109619 ya listaba. Según Picus Security, no hay prueba de concepto pública ni explotación activa de este CVE.
Para saber si tu código cambia el handler en tiempo de ejecución:
grep -rn "serialize_handler" --include=*.php .Si nunca lo cambias, puedes dejar la función encendida. Si lo cambias, apágala hasta el próximo release, aunque ya hayas instalado 8.5.12, 8.4.27, 8.3.36 u 8.2.35.
Parte de la cobertura mezcla este caso con CVE-2026-109619 porque comparten la mitigación. El aviso de php.net le pone número al escenario del cambio de serialize handler: es CVE-2026-115560.
"Tengo un PoC" no es "tengo una RCE"
Van a circular demostraciones con títulos fuertes: "RCE a root", "shell en WordPress", "exploit contra una URL pública". Muchas prueban menos de lo que parece. CrowdStrike lo dijo el 23 de octubre: muchas de las pruebas de concepto que analizó son "crash POCs that only prove a vulnerability exists", y convertirlas en una RCE confiable no es trivial ni está garantizado.
Señales para separar la clase de bug real del teatro de laboratorio:
- "RCE a root" (uid=0). CrowdStrike reprodujo la RCE "with the privileges of the running mod_php or the PHP-FPM worker processes". Si la demo muestra
uid=0, lo más probable es que el laboratorio corriera PHP como root: el exploit te da el usuario que ya corría. Contra un worker que corre comowww-data, dawww-data. Serio, pero no es una escalada de privilegios. - "Shell en WordPress". Mira si el acceso fue por HTTP o si el exploit se montó con acceso local al binario, por ejemplo copiando un archivo dentro del contenedor y ejecutándolo ahí. Lo segundo prueba que la cadena funciona sobre ese binario, no que el sitio sea explotable por la red.
- "Exploit contra una URL pública". Mira de quién es la URL. Un túnel propio que apunta a un contenedor propio demuestra que la petición atraviesa la red, no que alguien haya comprometido a un tercero.
- ASLR apagado. Una demo de corrupción de memoria con ASLR desactivado se salta la parte difícil. php2shell incluye una fuga de memoria, así que la clase tiene con qué resolverlo; la pregunta es si ese PoC lo hace.
- Configuración a medida. Un "RCE remoto" que necesita handlers de sesión divergentes o un gadget concreto en la aplicación no dice nada de una instalación por defecto.
Nada de esto quita que las clases de bug sean reales. El punto es distinguir "la clase es explotable en la vida real" de "este PoC demostró el exploit completo", porque esa distinción decide cuánto urge actuar. El vector CVSS de los dos CVE ya lo decía con su complejidad alta (AC:H), y CrowdStrike lo concreta: un exploit universal es poco probable, pero contra un perfil fijo, como una imagen de Docker sin modificar, uno confiable es viable.
El caso NativePHP
NativePHP es un ejemplo de por qué conviene mirar de dónde sale el binario. Las apps de escritorio de NativePHP no usan el PHP del sistema: traen el suyo, compilado en el repo NativePHP/php-bin, "The PHP binaries used by NativePHP Desktop". La clase de bug es la misma de PHP core, no un agujero nuevo del framework, pero la corrección no llega actualizando el servidor: llega cuando la app se reempaqueta con un binario corregido.
Cómo verificar tu exposición sin atacar a nadie
No hace falta conseguir una shell para saber si estás expuesto. Tres preguntas lo responden, y las tres se contestan sobre tus propios servidores o tus propias imágenes. Para las imágenes, --network none deja el contenedor sin red mientras revisas.
1. ¿Tu versión tiene el parche? Mira la versión del paquete, no solo php -v: las distros aplican el fix sobre su versión base. La parte 1 tiene las versiones corregidas y los paquetes de Debian y Ubuntu. En una imagen tuya:
docker run --rm --network none tu-imagen php -v2. ¿Tu configuración de sesión te expone? Revisa que session.serialize_handler sea el mismo en todo el sitio y si tienes upload progress activo sin usarlo:
docker run --rm --network none tu-imagen php -i | grep -Ei 'serialize_handler|upload_progress'En un servidor, corre lo mismo con el binario de FPM, que puede tener otro php.ini que la CLI.
3. ¿Tu código pasa datos no confiables a unserialize()? Esto se audita leyendo la aplicación, no la configuración. Busca unserialize( sobre entrada del usuario sin la opción allowed_classes, y los envoltorios de framework como maybe_unserialize() de WordPress.
Para decidir si parchear, el veredicto de exposición alcanza. Reproducir la cadena hasta la shell no agrega nada que necesites, y contra sistemas que no son tuyos es, directamente, un ataque.
Qué queda para después
Hay más hallazgos alrededor de este caso que siguen en divulgación coordinada con quienes tienen que corregirlos. Mientras no haya un parche público, señalarlos (aunque sea sin PoC) solo le indica a otros dónde hay un objetivo vivo. Cuando salgan los parches y cierren los plazos de divulgación, los voy a cubrir con el mismo criterio que estas dos partes: a nivel de clase y del lado de la defensa.
Qué hacer hoy, en orden
- Actualiza a la versión corregida de tu rama (ver la parte 1) y confírmalo con la versión del paquete.
- Apaga
session.upload_progress.enabledsi no lo usas, y mantenlo apagado si tu código cambia el serialize handler: es la única defensa contra CVE-2026-115560 hasta el próximo release. - No pases datos no confiables a
unserialize(); usaallowed_classescuando no puedas evitarlo. - Si distribuyes una app de NativePHP, reempaquétala cuando haya un binario de PHP corregido.
- Verifica tu exposición sobre tus servidores e imágenes, no contra sistemas de terceros.
Preguntas frecuentes
¿Qué es CVE-2026-115560?
Es una RCE en PHP por session upload progress que PHP anunció el 23 de octubre de 2026. Solo aparece si la aplicación cambia session.serialize_handler de php_binary o php_serialize a php con ini_set() antes de session_start(), con upload progress activo.
¿PHP 8.5.12 corrige CVE-2026-115560?
No. PHP lo corrige en el próximo release regular, y hasta entonces recomienda session.upload_progress.enabled=0. El release del 21 de octubre corrigió CVE-2026-109618 y CVE-2026-109619.
¿Cómo sé si mi aplicación está afectada por CVE-2026-115560?
Busca serialize_handler en tu código PHP. Si nunca lo cambias en tiempo de ejecución con ini_set(), el escenario no aplica y puedes dejar upload progress encendido.
¿Un PoC de php2shell significa que mi servidor es explotable?
No necesariamente. Según CrowdStrike, muchas pruebas de concepto solo hacen caer el proceso, y el exploit hay que ajustarlo a la versión de PHP y a la aplicación. El riesgo sube si corres una imagen común sin modificar.
¿php2shell da acceso root?
No por sí solo. La RCE corre con los privilegios del worker de mod_php o PHP-FPM. Una demo con uid=0 suele significar que PHP corría como root en ese laboratorio.
Fuentes
- Informational advisory: Session upload progress (aviso de PHP del 23 de octubre sobre CVE-2026-115560)
- Security advisory: Critical remote code execution vulnerabilities, update now (advisory oficial de PHP del 21 de octubre)
- RCE via OOM corruption, GHSA-w8hw-w9fx-j79w (advisory de php-src para CVE-2026-109618, el cambio en el manejo del out-of-memory)
- RCE via stale global state, GHSA-6wmf-4rv5-p3rp (advisory de php-src para CVE-2026-109619, fuga de memoria y RCE)
- Obstacles to Weaponizing PHP2Shell Likely Constrain Immediate Widespread Exploitation (CrowdStrike, pruebas de concepto que solo hacen caer el proceso y privilegios de la RCE)
- PHP2Shell: Critical PHP RCE Flaws (CVE-2026-109618 & CVE-2026-109619) Explained (Picus Security, estado de CVE-2026-115560)
- NativePHP/php-bin (binarios de PHP que empaqueta NativePHP Desktop)
¿Tienes un proyecto en mente?
Trabajo con Laravel, WordPress, SEO técnico y servidores MCP. El primer paso es una llamada de descubrimiento, sin costo ni compromiso, donde me cuentas qué necesitas y te digo con honestidad si puedo ayudarte.