🚨 PHP2Shell — RCE Crítico en PHP (CVE-2026-109618 / CVE-2026-109619)
Acción inmediata
Actualizar PHP hoy a 8.5.12, 8.4.27, 8.3.36 o 8.2.35 (publicadas el 21 de octubre de 2026). Son dos RCE sin autenticación en el núcleo de PHP. Si no puedes actualizar, aplicar las mitigaciones temporales de esta nota — reducen exposición, no arreglan la raíz.
Ficha rápida
- CVEs: CVE-2026-109618 · CVE-2026-109619
- Tipo: ejecución remota de código (RCE), sin autenticación
- Severidad reportada: 10.0 inicialmente; el aviso oficial de PHP la califica CVSS v4.0 9.2
- Afecta: PHP 5.4.0 – 8.5.11 (todas las versiones con
session.upload_progress)- Corregido en: 8.2.35 · 8.3.36 · 8.4.27 · 8.5.12 (21-oct-2026)
- Backports: PHP 7.4, 8.0 y 8.1
- Sin fix oficial: PHP 5.x y 7.1 (EOL)
- Nombre público: PHP2Shell — ya hay PoC y exploits
🕒 Cronología
| Fecha | Evento |
|---|---|
| 20-oct-2026 | Filtración en HN/X/Mastodon; Florian Roth (@cyb3rops) confirma dos RCE. Anuncio oficial en php.net. |
| 20-oct (23 hs) | Se rumorea que una de las fallas son dos primitivas use-after-free en el heap del gestor session.upload_progress. |
| 21-oct (8 hs) | Exploits circulando. Se recomienda desactivar session.upload_progress. Cloudflare publica reglas WAF de emergencia. |
| 21-oct (11 hs) | Parches publicados: 8.5.12, 8.4.27, 8.3.36, 8.2.35. |
| 21-oct | Sury.org publica backports para Debian/Ubuntu desde PHP 5.6. |
🧬 Las dos vulnerabilidades
CVE-2026-109618 — RCE por corrupción en falta de memoria (OOM)
El manejo de una situación de out of memory puede interrumpir PHP mientras los datos internos de la request están parcialmente inicializados. La limpieza posterior de la request interpreta esos datos inválidos como válidos.
CVE-2026-109619 — RCE por estado global obsoleto
Un estado interno asociado a una request persiste después de que la memoria que lo contenía fue liberada. Una request posterior, atendida por el mismo worker, usa ese estado obsoleto antes de que la inicialización estándar lo restablezca.
La cadena de ataque de PHP2Shell
El gestor
session.upload_progressguarda el nombre de archivo proporcionado por el usuario en los datos de sesión sin validación. Además, un session ID enviado en el cuerpo de un POST puede eludirsession.use_only_cookies. Combinado con una vulnerabilidad de LFI (local file inclusion) en la aplicación, se llega a ejecución de código.No es un exploit universal: el investigador describe varias vías. El riesgo sube en apps que ya tienen LFI o deserialización insegura.
📦 Versiones afectadas y corregidas
| Rama | Estado | Versión |
|---|---|---|
| PHP 8.5 | ✅ Corregido | 8.5.12 |
| PHP 8.4 | ✅ Corregido | 8.4.27 |
| PHP 8.3 | ✅ Corregido | 8.3.36 |
| PHP 8.2 | ✅ Corregido | 8.2.35 |
| PHP 8.1 | ⚠️ Backport mínimo | — |
| PHP 8.0 | ⚠️ Backport mínimo | — |
| PHP 7.4 | ⚠️ Backport mínimo | — |
| PHP 5.x / 7.1 | ❌ EOL, sin fix oficial | — |
Afectadas: PHP 5.4.0 – 8.5.11, es decir, todas las versiones que incluyen session.upload_progress.
Backports no oficiales
Sury.org (Ondřej Surý) publicó protección para Debian/Ubuntu desde PHP 5.6, incluidas ramas EOL. Son parches de terceros, no del proyecto PHP. Leer el changelog del paquete antes de implementarlos: un backport puede cambiar comportamiento (por ejemplo, desactivar una funcionalidad de la que dependes).
🎯 ¿Quién está en riesgo?
Toda instalación de PHP que procese requests web:
- PHP-FPM
- Apache con
mod_php← mayor riesgo: hay exploit demostrado donde una request no autenticada dispara el ataque antes de ejecutar código de la aplicación php-cgi- FrankenPHP
- LiteSpeed
Contexto de exposición: VirusTotal identifica ~5.1 millones de IPs ejecutando PHP y ~3.8 millones con al menos un puerto abierto.
✅ Solución: actualizar
Actualizar a la versión corregida de tu rama. En Debian/Ubuntu el paquete depende de la versión instalada:
# Ver versión actual
php -v
# Actualizar paquetes (ejemplo con la rama 8.3)
sudo apt update
sudo apt install --only-upgrade php8.3-fpm php8.3-cli
# Reiniciar el servicio correspondiente
sudo systemctl restart php8.3-fpmProbar antes de producción
Validar la actualización en staging primero. Un cambio de patch de PHP puede romper extensiones o comportamiento específico. En producción, actualizar por olas y con rollback listo.
🛡️ Mitigaciones temporales (si no puedes actualizar ya)
No son un parche
Reducen vectores conocidos, no garantizan seguridad. Aplicar y luego actualizar igual.
General (ambas CVEs)
Desactivar el gestor de progreso de carga en php.ini global y en .user.ini por sitio:
session.upload_progress.enabled = Off
session.upload_progress.cleanup = OffReiniciar todos los workers después del cambio. No confiar en ini_set() ni en .user.ini de FPM para esta directiva.
CVE-2026-109618 (OOM)
| Medida | Detalle |
|---|---|
memory_limit = -1 | Mejor vía php_admin_value en el pool FPM o en Apache, para que la app no lo sobrescriba. Asegurar que ningún pool/directorio/app imponga un límite finito. |
post_max_size bajo | 8M si no hay uploads; 32M si se necesitan. |
upload_max_filesize | Siempre menor que post_max_size. |
| Límites de proxy/servidor | Tamaño de request decodificada, número de campos, partes multipart, headers y profundidad de anidamiento. Aplicar también sin Content-Length válido. |
| Contenedor / cgroup | Control de disponibilidad; ideal terminar solo el worker afectado. No usar RLIMIT_AS ni RLIMIT_DATA: provocan el mismo fallo de asignación. |
CVE-2026-109619 (estado obsoleto)
| Medida | Detalle |
|---|---|
session.upload_progress.enabled = 0 | En php.ini, pool FPM o config del servidor, antes de procesar requests. Reiniciar todos los procesos. |
fatal_error_backtraces = 0 | Solo PHP 8.5. Bloquea la ruta confirmada de backtraces en errores fatales. |
| Limpiar configs inválidas | Eliminar configuraciones obsoletas aplicadas por request que generen diagnósticos antes de la inicialización. Ocultarlas con error_reporting, display_errors o @ no sirve. |
| Corregir fatales y timeouts | Incluye callbacks, cargas/importaciones de archivos y tareas de limpieza accesibles por un atacante. |
WAF
Cloudflare publicó reglas WAF de emergencia para los vectores conocidos, disponibles para todos los planes, incluido el gratuito. Son una capa extra, no un reemplazo del parche.
🔍 Detección
process-watcher de Florian Roth:
- Binario estático para Linux (amd64 / arm64), sin dependencias.
- Usa eBPF para mostrar todos los procesos que PHP lanza, con líneas de comando completas.
- Permite terminar procesos específicos (con prueba previa).
- Es detección, no parche. No ve lo que ocurre dentro de PHP si no se inicia un proceso externo.
La explotación probablemente se manifieste como subprocesos sospechosos. Aislamiento por contenedores + monitorización estricta son las mejores opciones mientras no haya parche.
🧠 Lecciones
- No confiar en rumores: el CVSS inicial de 10.0 vino de una filtración; el aviso oficial lo fijó en 9.2. Igual de crítico, pero la fuente importa.
- Los EOL no desaparecen: PHP 5.x/7.1 no tienen fix oficial. Sury.org salvó a muchos con backports, pero son de terceros.
- Defensa en profundidad: el parche es la solución; WAF, límites de request, cgroups y monitoreo son capas.
- La cadena importa: PHP2Shell necesita
session.upload_progress+ una vía de inclusión/deserialización. Revisar también la app, no solo el runtime.