PHP corrigió el 21 de octubre de 2026 dos fallos críticos (CVSS 9,2) que permiten ejecutar código sin autenticación; la única mitigación completa es actualizar a la 8.5.12, 8.4.27, 8.3.36 u 8.2.35, y quien use 8.1 o anterior debe pedir un parche a su proveedor o migrar.
1. Qué pasó#
El 20 de octubre de 2026 el proyecto PHP avisó de un lanzamiento de seguridad urgente para el día siguiente. El 21 de octubre publicó las versiones 8.5.12, 8.4.27, 8.3.36 y 8.2.35, que corrigen dos vulnerabilidades críticas que permiten ejecución remota de código sin autenticación. Los medios las llaman PHP2Shell.
| Fecha (2026) | Hecho |
|---|---|
| 20 de octubre | PHP anuncia un lanzamiento de seguridad para el 21 de octubre a las 14:00 UTC, con dos fallos críticos (CVSS 9.2), y se reserva adelantarlo si los detalles se hacen públicos |
| Antes de los parches | El director técnico de Cloudflare dijo que ya veían ataques tempranos contra ambos fallos; sus reglas de WAF eran temporales |
| 21 de octubre | Salen PHP 8.5.12, 8.4.27, 8.3.36 y 8.2.35, con los avisos publicados en el repositorio de PHP |
| 21 a 22 de octubre | Qualys informa que a las 12 horas solo 199.000 de 155 millones de instalaciones con versión conocida estaban parcheadas |
Hay señales de explotación: Cloudflare dijo que ya veía ataques tempranos contra ambos fallos antes de los parches, y PHP describe un exploit demostrado en Apache con mod_php. El Centro Nacional de Ciberseguridad del Reino Unido pidió actualizar de inmediato o desconectar los sistemas afectados.
Las fuentes difieren en un detalle: los avisos y la página de noticias de PHP fechan los lanzamientos el 21 de octubre, mientras que el registro de cambios de php.net muestra la fecha del 22 para la 8.5.12 y la 8.4.27. Usamos el 21, que es la fecha de los avisos.
Documentación: PHP: archivo de noticias de 2026 ↗
2. Las dos vulnerabilidades#
Ambas tienen CVSS v4.0 de 9,2 (crítica). Según BleepingComputer, fueron descubiertas por Calif, OpenAI y Wiz. Se basan en el mismo problema de fondo: PHP deja memoria corrupta cuando una petición termina de forma anormal.
| Dato | CVE-2026-109618 | CVE-2026-109619 |
|---|---|---|
| Aviso | GHSA-w8hw-w9fx-j79w | GHSA-6wmf-4rv5-p3rp |
| Nombre del aviso | RCE por corrupción de memoria al quedarse sin memoria (OOM) | RCE por estado global obsoleto |
| CVSS v4.0 | 9,2 crítica (AV:N/AC:H/AT:P/PR:N/UI:N) | 9,2 crítica (AV:N/AC:H/AT:P/PR:N/UI:N) |
| Mecanismo | PHP sale de un error de memoria con longjmp() y deja datos de la petición a medio inicializar que el cierre trata como válidos | Estado global de una petición sobrevive a su memoria liberada y una petición posterior del mismo worker lo usa antes de reiniciarlo |
| Impacto confirmado | Ejecución remota de código sin autenticación con los permisos del worker web | Use-after-free, filtración de memoria y ejecución remota de código sin autenticación |
| Dónde se confirmó | Apache mod_php con límites por defecto | mod_php (trazas de pila fatales de PHP 8.5) y mod_php y PHP-FPM (progreso de subida de sesión) |
El primer fallo se activa al quedarse sin memoria. Los avisos describen varias formas de empujar una petición a ese estado sin salir de los límites aparentes: peticiones multipart sin una longitud declarada utilizable, un número ilimitado de encabezados por parte y campos de archivo vacíos con nombres anidados. El segundo necesita dos peticiones atendidas por el mismo worker: una que deja el estado obsoleto y otra que lo usa.
El aviso aclara que el éxito del exploit depende de la compilación de PHP, las extensiones y la disposición de la memoria, por lo que es más difícil en unos entornos que en otros. Eso no lo hace seguro: los investigadores demostraron ejecución de código con límites por defecto.
Documentación: Aviso GHSA-w8hw-w9fx-j79w (CVE-2026-109618) ↗ · Aviso GHSA-6wmf-4rv5-p3rp (CVE-2026-109619) ↗
3. A quién afecta#
Afectan a todas las versiones por debajo de 8.2.35, 8.3.36, 8.4.27 y 8.5.12. Qualys y la prensa incluyen en el riesgo a cualquier instalación que atienda peticiones web: PHP-FPM, Apache con mod_php, php-cgi, FrankenPHP y LiteSpeed.
| Rama de PHP | Versión corregida | Estado |
|---|---|---|
| 8.5 | 8.5.12 | Con soporte |
| 8.4 | 8.4.27 | Con soporte |
| 8.3 | 8.3.36 | Con soporte |
| 8.2 | 8.2.35 | Termina su vida útil el 31 de diciembre de 2026 |
| 8.1, 8.0 y 7.4 | Sin versión corregida oficial | Fuera de soporte; PHP ofrece parches de código fuente para 7.4, 8.0 y 8.1 |
El caso más urgente es Apache con mod_php: el aviso describe un exploit que se dispara antes de que corra el código de la aplicación. En PHP-FPM el primer fallo no está demostrado de forma completa, pero PHP validó ejecución de código por la vía de la sesión. La línea de comandos no se menciona.
Las ramas 8.1, 8.0 y 7.4 no reciben actualizaciones del proyecto; PHP ofrece parches de código fuente que debe aplicar tu proveedor. Qualys estima que de 167 millones de instalaciones de PHP que analizó, el 71 % usa versiones sin soporte (de 5.4 a 8.1). Es una cifra de un fabricante de seguridad: úsala como orden de magnitud.
Documentación: Qualys: PHP2Shell, cómo encontrar, mitigar y parchear ↗ · BleepingComputer: parches de emergencia para PHP2Shell ↗
4. Cómo saber si estás afectado#
Mira la versión que sirve las peticiones web, no la que ves en tu terminal: Qualys advierte que php -v puede mostrar una instalación distinta de la que atiende el sitio. Revisa también los contenedores, las ramas paralelas y el PHP que traen los paneles de hosting.
# Versión que sirve las peticiones web (no la del terminal): compara con la del proceso en ejecución
php -v
php-fpm -v || php-fpm8.4 -v
apache2ctl -M 2>/dev/null | grep -i php # ¿mod_php cargado?
ps aux | grep -E "php-fpm|apache2|httpd|frankenphp" | grep -v grep
# Contenedores: la imagen que corre, no la que tienes en el portátil
docker ps --format "{{.Names}} {{.Image}}"
docker exec NOMBRE php -v
# Tras actualizar: reiniciar de verdad los procesos que atienden peticiones
sudo systemctl restart php8.4-fpm # o apache2 si usas mod_phpEl script compara una versión con las corregidas. Si usas un paquete de una distribución, el número puede seguir siendo antiguo aunque tenga el parche: consulta el aviso de tu proveedor.
import re
CORREGIDAS = {(8, 5): 12, (8, 4): 27, (8, 3): 36, (8, 2): 35} # parche menor corregido por rama
def parsear(version):
return tuple(int(n) for n in re.findall(r"\d+", version)[:3])
def estado(version):
mayor, menor, parche = parsear(version)
if (mayor, menor) in CORREGIDAS:
minimo = CORREGIDAS[(mayor, menor)]
if parche >= minimo:
return "corregida"
return f"VULNERABLE: actualiza a {mayor}.{menor}.{minimo}"
if (mayor, menor) > (8, 5):
return "rama más nueva que las corregidas: revisa los avisos"
return "SIN VERSION CORREGIDA OFICIAL: pide un build con el parche a tu proveedor o migra"
for v in ["8.5.11", "8.5.12", "8.4.26", "8.4.27", "8.3.36", "8.2.34", "8.1.31", "7.4.33"]:
print(v, "->", estado(v))5. Cómo parchear#
- Actualiza a la 8.5.12, 8.4.27, 8.3.36 u 8.2.35 según tu rama. Si estás en 8.1 o anterior, pide a tu proveedor un build con el parche o migra a una rama con soporte.
- Reinicia los procesos que atienden peticiones (PHP-FPM, Apache, FrankenPHP): actualizar el paquete no basta hasta que los workers cargan la versión nueva.
- En contenedores, reconstruye la imagen sobre una base con la versión corregida y vuelve a desplegar.
- En hosting compartido o con panel (Plesk, cPanel), confirma con el proveedor que ya aplicó el parche.
- Si PHP 8.2 es tu rama, planifica la migración: llega al final de su vida útil el 31 de diciembre de 2026.
Para profundizarParches de seguridad de octubre de 2026: Next.js 16.4.1 y PgBouncer 1.26.1
6. Si no puedes parchear hoy: mitigaciones temporales#
PHP publica ajustes que reducen la exposición, pero insiste en que no hacen seguro un sistema sin parchear: la única mitigación completa es actualizar. Deben aplicarse de forma administrativa (php.ini, el pool de FPM o la configuración del servidor), no con ini_set() ni .user.ini, y hay que reiniciar los workers.
| Ajuste | Valor | Protege de |
|---|---|---|
| memory_limit | -1, fijado con php_admin_value | CVE-2026-109618 |
| post_max_size | 8M sin subidas; 32M con subidas | CVE-2026-109618 |
| upload_max_filesize | Menor que post_max_size | CVE-2026-109618 |
| session.upload_progress.enabled | 0 en php.ini, el pool de FPM o el servidor; no con ini_set() ni .user.ini | CVE-2026-109619 |
| fatal_error_backtraces | 0, solo en PHP 8.5 | CVE-2026-109619 (la vía confirmada de PHP 8.5) |
| pm.max_requests | 1, solo en FPM y con costo de rendimiento | CVE-2026-109619 |
import re
def a_mb(valor):
m = re.fullmatch(r"(\d+)\s*([KMG]?)", valor.strip().upper())
if not m:
return None
return int(m.group(1)) * {"": 1 / 1024**2, "K": 1 / 1024, "M": 1, "G": 1024}[m.group(2)]
def auditar(ini, version):
"""Revisa las mitigaciones temporales que PHP lista; no sustituyen al parche."""
cfg = dict(l.split("=", 1) for l in (x.split(";")[0].strip() for x in ini.splitlines()) if "=" in l)
cfg = {k.strip(): v.strip() for k, v in cfg.items()}
faltan = []
if cfg.get("memory_limit") != "-1":
faltan.append("memory_limit = -1 (fijado como php_admin_value)")
limite = a_mb(cfg.get("post_max_size", "8M"))
if limite is None or limite > 32:
faltan.append("post_max_size <= 32M (8M si no hay subidas)")
if cfg.get("session.upload_progress.enabled", "1").lower() not in ("0", "off"):
faltan.append("session.upload_progress.enabled = 0")
if parsear(version)[:2] == (8, 5) and cfg.get("fatal_error_backtraces", "1").lower() not in ("0", "off"):
faltan.append("fatal_error_backtraces = 0 (solo PHP 8.5)")
return faltan
def parsear(version):
return tuple(int(n) for n in re.findall(r"\d+", version)[:3])
parcial = "memory_limit = -1\npost_max_size = 8M ; sin subidas\nsession.upload_progress.enabled = 0\n"
print(auditar(parcial, "8.4.26"))
print(auditar(parcial, "8.5.11"))
print(auditar("memory_limit = 128M\npost_max_size = 64M\n", "8.3.35"))Dos advertencias del aviso: no uses límites de proceso RLIMIT_AS ni RLIMIT_DATA, porque pueden activar justo la ruta vulnerable, y ocultar errores con error_reporting, display_errors o el operador @ no ayuda. Además, memory_limit = -1 solo sirve si ninguna aplicación puede volver a fijar un límite finito.
7. Después de parchear#
Qualys informa que los investigadores demostraron la filtración de memoria de los workers sin autenticación, lo que puede exponer contraseñas, tokens y claves de API. Si tu servidor estuvo expuesto con una versión vulnerable, trata los secretos que pasaron por PHP como posiblemente comprometidos.
- Rota las credenciales de base de datos, las claves de API y los tokens de sesión que manejaba esa aplicación.
- Revisa los registros del servidor y de PHP desde el 20 de octubre: peticiones multipart extrañas o muy grandes, errores fatales repetidos y reinicios de workers.
- Busca archivos nuevos o modificados en el directorio web y en carpetas de subida.
- Mantén una regla de WAF que limite o bloquee multipart donde no lo uses, como medida adicional, no como sustituto del parche.
- Anota la fecha y la versión en que parcheaste cada servidor.
Estas recomendaciones son de buena práctica, no del aviso de PHP; adáptalas a tu entorno y a tu equipo de seguridad.
8. Mantén tus cifras al día#
Las versiones, los identificadores y las mitigaciones son las de los avisos publicados hasta el 22 de octubre de 2026. Pueden aparecer nuevos avisos y detalles de exploración; revisa el repositorio de seguridad de PHP y el aviso de tu distribución.
- Suscríbete a los avisos de seguridad de PHP y de tu proveedor.
- Mantén un inventario de qué versión de PHP corre en cada servidor y contenedor.
- Automatiza una alerta cuando una versión quede por debajo de la corregida.
Fuentes y alcance
Documentación consultada el 22 de octubre de 2026. Los ejemplos y criterios de decisión son propuestas editoriales; adáptalos al contrato de tu aplicación y valídalos en tu entorno de pruebas autorizado.