El 21 de octubre PHP publicó de urgencia cuatro versiones que corrigen dos vulnerabilidades del intérprete. Permiten ejecutar código en el servidor sin usuario ni contraseña, afectan a todo PHP desde la 5.4 y ya hay sondeos en curso. Esto es lo que hay que saber, en el orden en que hay que actuar.
Si solo vas a leer un párrafo: actualizá PHP a 8.5.12, 8.4.27,
8.3.36 u 8.2.35 y reiniciá todos los workers. Si estás en 8.1 o anterior, no hay
versión nueva para vos: dependés del paquete de tu distribución o de migrar. Mientras tanto,
desactivá session.upload_progress en el php.ini. Un WAF no alcanza.
El lunes 20 el equipo de seguridad de PHP avisó que al día siguiente, a las 14:00 UTC, saldría una versión de seguridad para dos fallos críticos, sin dar detalles. La divulgación coordinada estaba prevista para el jueves 22, pero los detalles se filtraron antes de tiempo y el embargo se vino abajo: el parche salió el martes 21 a las 13:52 UTC, unos minutos antes de lo anunciado.
Los fallos los encontró Wiz Research, que les puso el apodo php2shell. Los dos llevan CVSS 9.2 en la escala v4.0 y el mismo impacto: ejecución remota de código sin autenticación y lectura de la memoria del proceso, donde viven contraseñas de base de datos, tokens y claves de API. Wiz demostró el ataque contra las imágenes oficiales más recientes de WordPress, Drupal y Joomla, y aclaró que ni hace falta una aplicación: un Hello World sobre una versión vulnerable alcanza.
Antes de que saliera el parche, varios servicios públicos europeos y japoneses prefirieron apagarse. A la mañana del 21, Cloudflare reportaba los primeros ataques y hostings asociados a Patchstack informaban incidentes; al mediodía la telemetría mostraba sobre todo sondeos, sin evidencia firme de intrusiones. Eso va a cambiar: el código de explotación circula y la ventana de exposición es de cientos de miles de servidores.
No es un error de una aplicación ni de una biblioteca. Está en el núcleo del intérprete, en cómo PHP arma y desarma el estado interno de cada petición HTTP.
Si una petición hace que PHP se quede sin memoria en el momento justo, la inicialización de los datos internos de esa petición se interrumpe a medias. La limpieza que viene después trata esos datos incompletos como si fueran válidos, y a partir de ahí la memoria del proceso queda a disposición del atacante. El detonante es una petición que cualquiera puede mandar, con el tamaño justo.
Parte del estado que pertenece a una petición puede quedar vivo después de que su memoria fue
liberada. La petición siguiente que atiende el mismo worker puede usar ese
estado viejo antes de que la inicialización normal lo pise. Es la clase de fallo que se conoce
como use-after-free, y el camino confirmado pasa por el seguimiento de progreso de
subidas de archivos de las sesiones (session.upload_progress), activo por defecto
desde PHP 5.4.
PHP considera que el riesgo es mayor con Apache y mod_php, donde una petición sin autenticar dispara el ataque antes de que se ejecute una sola línea de la aplicación. Pero PHP-FPM, php-cgi, FrankenPHP y LiteSpeed también están alcanzados: toda instalación que sirva peticiones web.
Es la única corrección completa. Estas son las versiones publicadas el 21 de octubre, por rama:
| Rama | Versión corregida | Soporte |
|---|---|---|
| PHP 8.5 | 8.5.12 | Activo |
| PHP 8.4 | 8.4.27 | Activo |
| PHP 8.3 | 8.3.36 | Solo seguridad |
| PHP 8.2 | 8.2.35 | Solo seguridad; termina el 31 de diciembre de 2026 |
| PHP 8.1, 8.0, 7.4 y anteriores | — | Fin de vida: el proyecto no publica versión |
Después de instalar, reiniciar todos los workers (el servicio de PHP-FPM, o Apache entero si usás mod_php). Un worker viejo que sigue vivo sigue siendo vulnerable. Las distribuciones y los proveedores de soporte extendido recibieron los parches con antelación; el repositorio de Remi para RHEL y derivados avisó que no tendría paquetes el mismo día.
Una parte enorme del parque corre PHP 7.4, 8.0 u 8.1, y para esas ramas no hay versión oficial. Las salidas, de mejor a peor:
PHP-7.4-CVE-2026-109618-109619 y equivalentes). No son versiones publicadas: son un favor, sin pruebas completas.
Reducen la superficie pero no dejan la instalación segura, en palabras del
propio proyecto. Sirven para las horas o los días que tarde la actualización, no para
reemplazarla. Van en el php.ini, en el pool de FPM o en la configuración del
servidor, nunca con ini_set() ni en un .user.ini, porque tienen que
aplicarse antes de que PHP empiece a leer la petición. Y después de cambiarlas, reiniciar los
workers.
; Contra CVE-2026-109619: corta el camino confirmado por sesiones
session.upload_progress.enabled = 0
; Solo en PHP 8.5: corta el camino por el backtrace de errores fatales
fatal_error_backtraces = 0
; Contra CVE-2026-109618: en el pool de FPM, para que la app no lo pise
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M ; 32M si la app necesita subir archivos
php_admin_value[upload_max_filesize] = 8M ; siempre menor que post_max_size
La idea detrás de memory_limit = -1 es contraintuitiva: el fallo se dispara cuando
PHP falla al pedir memoria, así que se le saca el límite propio y se pone el tope en
otro lado, con un cgroup o el límite del contenedor, que mata el proceso en vez de dejarlo a
medio inicializar. No usar RLIMIT_AS ni RLIMIT_DATA para eso: provocan
justamente el fallo de asignación que se quiere evitar.
Dos cosas más que recomienda PHP y que suelen pasarse por alto: sacar de la configuración por
petición las directivas inválidas u obsoletas que emiten avisos antes de la inicialización
(esconderlos con error_reporting, display_errors o @ no
ayuda), y arreglar los errores fatales y los tiempos de espera que un atacante pueda provocar
desde afuera: subidas, importaciones, callbacks.
Antes de dar por cerrado el tema conviene mirar los registros de los últimos días. Las señales que están reportando los equipos de respuesta:
multipart/form-data inusuales, seguidas de errores de límite de memoria o de respuestas 500 repetidas.PHPSESSID anormalmente largas o malformadas.Si ves alguna de estas señales, el orden es: actualizar, tratar el servidor como potencialmente comprometido y rotar las credenciales que ese servidor tenía en memoria o en disco: la de la base de datos, las claves de API, los secretos de sesión.
Hoy la pregunta que más se repitió en los equipos no fue «¿cómo actualizo?» sino «¿qué versión de PHP está corriendo cada uno de nuestros sitios?». Quien tenía un inventario respondió en diez minutos y actualizó a la tarde. Quien no, pasó el día entrando por SSH a servidores uno por uno, y todavía no sabe si le quedó alguno.
Un fallo de esta escala no se repite todos los meses, pero tampoco es el último. Lo que cambia el resultado no es la velocidad de reacción el día del parche; es saber de antemano qué hay instalado, qué versión, y qué cambió en cada sitio sin que nadie lo pidiera. De eso hablo en cómo trabajo sobre sitios y flotas web, y es lo primero que mira una auditoría técnica.