La solicitud llega a PHP antes de que su controlador pueda intervenir
Su aplicación puede rechazar una subida después de que PHP ya haya procesado parte de su cuerpo. Ese orden es la razón por la que una vulnerabilidad del runtime merece una investigación distinta de la de un controlador de subidas inseguro. En CVE-2026-109618 y CVE-2026-109619, conocidas públicamente como PHP2Shell, la pregunta central es dónde cruzan los datos de la solicitud controlados por el atacante un límite dentro del intérprete.
Abordo este incidente en tres capas: la versión de seguridad confirmada, el comportamiento del runtime visible en el código fuente y el mecanismo que proponen las implementaciones revisadas. Esta evaluación tiene fecha de 21 de octubre de 2026 y se basa en el análisis estático de PHP 8.4.4, en el código pertinente de PHP 8.5.4, en las implementaciones de prueba de concepto revisadas para este análisis, en la información publicada y en una demostración revisada. No se realizó ninguna explotación independiente.
Lo que PHP ha confirmado
El aviso de PHP del 20 de octubre confirma dos vulnerabilidades críticas, cada una con una puntuación CVSS v4.0 de 9,2, y anuncia una versión de seguridad prevista para el 21 de octubre a las 14:00 UTC. El aviso previo consultado reserva para esa publicación las versiones afectadas, las versiones corregidas y todos los detalles técnicos. Por sí mismo, no identifica los números CVE ni señala el upload progress como causa raíz. Los dos identificadores y la descripción de RCE aparecen en la información pública asociada. [1, 2]
El informe de Huntress atribuye a watchTowr la reproducción de CVE-2026-109618 y afirma que, a 20 de octubre, Huntress no había reproducido los fallos ni observado ataques relacionados en los entornos de sus socios. La demostración revisada muestra efectos compatibles con la ejecución de comandos y la creación de archivos, también en WordPress y Drupal. La implementación de la demostración no se ha podido cotejar con las revisadas aquí. Estas observaciones justifican la urgencia, pero dejan por establecer la causa exacta y los requisitos previos del despliegue. [2]
Por qué el upload progress forma parte de la investigación
PHP puede mantener información de progreso respaldada por la sesión mientras analiza una subida multipart. El estado correspondiente incluye un contador de bytes de toda la solicitud, contadores por archivo, metadatos e indicadores de finalización. La función opera dentro del procesamiento de la solicitud, de modo que el formulario de subida visible de un sitio no describe por completo su superficie de ataque en el runtime. El manual de PHP documenta la función y su frecuencia de actualización configurable. [3]
En el código revisado, FILE_START establece el estado del archivo, FILE_DATA actualiza los contadores y END completa o limpia el seguimiento. El puntero al total de bytes se guarda durante la construcción inicial; el puntero al archivo actual se renueva para cada archivo. Las escrituras de los contadores preceden a la función auxiliar que actualiza la sesión. Ese orden importa, porque no puede suponerse que la autenticación dentro del código de la aplicación proteja automáticamente un análisis anterior. Un control de acceso en un proxy, antes de que PHP reciba la solicitud, impone un límite distinto. [4]
La cadena propuesta, etapa por etapa
Una de las pruebas de concepto revisadas asocia el primer identificador con el contador del total de bytes; otra asocia el segundo con el contador del archivo actual. Ambas proponen un problema de puntero obsoleto durante actualizaciones repetidas del progreso. Son las correspondencias que proponen esas implementaciones, no correspondencias establecidas por el aviso del fabricante consultado. Su modelo causal exige que la solicitud sea alcanzable, que se cree estado de progreso, que una asignación se libere antes de un acceso posterior y que se obtenga un control útil sobre el estado corrompido resultante.
Convertir ese estado en RCE exige además una ruta de ejecución alcanzable y direcciones y estructuras compatibles con la compilación. La demostración muestra una etapa de revelación de direcciones; la extracción de símbolos del framework local no establece una revelación remota equivalente. Las asignaciones existentes en el heap no prueban la reutilización posterior de un hueco liberado concreto, y dos escrituras retardadas en el socket no prueban dos callbacks del servidor que coincidan. Son condiciones técnicas distintas. Una cadena que funcione tiene que conectarlas; las implementaciones revisadas dejan esa conexión incompleta.
La cuestión del ciclo de vida: ¿quién sigue siendo dueño del array?
Un use-after-free consiste en acceder a una asignación cuando su ciclo de vida ya ha terminado. Llamemos P al array de progreso activo y D a un array de sesión decodificado por separado. Que su contenido sea equivalente no los convierte en la misma asignación. En la ruta de actualización correcta habitual de PHP 8.4.4, la función auxiliar incrementa el contador de referencias antes de colocar P en el árbol de la sesión. Restablecer ese árbol libera su referencia, mientras que el estado de progreso sigue siendo dueño de P. Sustituir el D decodificado no libera P. El incremento correspondiente también existe en la función auxiliar revisada de PHP 8.5.4. [4, 5, 6, 7]
Este modelo de propiedad es el paso sin resolver de la explicación propuesta. No descarta ninguno de los dos CVE ni refuta la demostración independiente. Una explicación completa de la causa raíz debe identificar otra ruta u otra condición que anule la garantía sobre el ciclo de vida. Incluso la separación al final de la solicitud requiere analizar el orden: con la limpieza deshabilitada, el árbol de sesión antiguo sigue siendo dueño del array original en la escritura final del contador, antes de la actualización forzada y del desmontaje. Que un puntero quede obsoleto no basta si no hay un acceso posterior. [4]
El envenenamiento de la sesión cruza un límite distinto
Un informe de laboratorio de terceros revisado describe una segunda vía hacia la ejecución: contenido de sesión controlado por el atacante que llega a un punto de inclusión de archivos de la aplicación. El endpoint de prueba, deliberadamente vulnerable, pasa un parámetro de la solicitud directamente a include(). El perfil de persistencia desactiva la limpieza del progreso y modifica el ritmo de actualización y los límites de multipart. El informe registra la ejecución en esas condiciones. Demuestra el tipo de límite de la aplicación que conviene auditar, pero no establece por sí solo el mecanismo de corrupción de memoria de ninguno de los dos CVE.
El mismo informe describe la selección del ID de sesión sin cabecera Cookie, la fijación de sesión en un entorno de inicio de sesión deliberadamente permisivo y un fallo de serialización asociado a un carácter de barra vertical en una clave de progreso. Estas observaciones dependen del manejador, del serializador, de la política de sesiones y del comportamiento de la aplicación. El contenido de la sesión debe seguir siendo datos; las rutas de include/require deben proceder de una lista de permitidos controlada, y las transiciones de autenticación deben sustituir el identificador de sesión. Un registro de progreso no demuestra por sí solo que se reutilice una asignación liberada.
El resultado uid=0 de la transcripción describe la identidad de su proceso de laboratorio. El lanzador inicia el servidor de desarrollo de PHP sin especificar un usuario con privilegios reducidos; esa salida no demuestra una escalada de privilegios ni acceso root al host. La distinción importa en la práctica: corregir un punto de inclusión inseguro y la política de sesiones resuelve la exposición a nivel de aplicación, mientras que un fallo de seguridad de memoria en el runtime sigue necesitando su propia corrección del fabricante.
Versiones afectadas y compatibilidad del exploit son preguntas distintas
El aviso del fabricante consultado no ofrece un rango confirmado de versiones afectadas ni una matriz de versiones corregidas. Las afirmaciones generales que abarcan desde PHP 5 en adelante siguen siendo afirmaciones de la información publicada. PHP 8.4.4 es nuestra base para el análisis del código; PHP 8.5.4 aparece en el material revisado y en un perfil de la demostración. Ninguna de estas observaciones establece el rango completo de versiones afectadas. Trátelo como una instantánea de la evidencia y utilice el aviso detallado del fabricante para decidir sobre los paquetes. [1, 8]
Una vulnerabilidad puede abarcar varias ramas mientras que un exploit concreto solo admite una arquitectura, una compilación de paquete o una integración con el servidor. A la inversa, que una PoC falle no demuestra que un despliegue sea seguro. La demostración incluye perfiles descritos como Apache prefork con mod_php y PHP-FPM; no demuestra una cobertura universal de SAPI. Inventaríe la compilación y la integración que realmente sirven las solicitudes, en lugar de deducir la exposición solo a partir de la versión del CMS o de un binario de CLI.
El informe de experimentos de terceros revisado no registra ejecución de comandos en sus intentos de corrupción de memoria sobre php:8.4.4-cli, php:8.4.4-fpm y php:8.2.34-fpm, pese a haber resuelto las direcciones en local. Son resultados del autor del informe, no pruebas realizadas para este artículo. No establecen versiones seguras ni justifican extender el ejemplo exitoso del punto de inclusión a las instalaciones de PHP por defecto. El autor propone además una puntuación CVSS v3.1; es una valoración propia del autor, no una conversión de la puntuación v4.0 emitida por PHP.
¿Qué ajustes cambian las condiciones?
En la ruta propuesta, session.upload_progress.enabled controla el seguimiento; name selecciona el campo desencadenante; freq y min_freq controlan los umbrales de actualización; cleanup controla la eliminación de la información de progreso tras el procesamiento del POST. Los manejadores de sesión y la serialización influyen en la persistencia. Estos ajustes indican qué examinar, no una lista confirmada de requisitos previos para ambos CVE. En particular, no puede suponerse que la limpieza final impida un error de memoria anterior. [3, 9]
La SAPI, el búfer del servidor, los límites del cuerpo de la solicitud y los tiempos de espera influyen en el momento en que los datos llegan al analizador. Una pausa en el cliente puede no sobrevivir al búfer como pausa entre callbacks de PHP. Examine la configuración efectiva en el proceso que sirve la web y en cada pool de FPM; la configuración de la CLI puede ser distinta. Si no utiliza el upload progress, valore desactivarlo como refuerzo preventivo. La evidencia revisada aquí no establece ese cambio como una solución provisional completa para ninguno de los dos CVE. [3, 9]
La configuración de laboratorio revisada pone también de relieve un detalle que conviene comprobar: session.upload_progress.freq=1 es un intervalo absoluto en bytes, mientras que 1% es relativo al tamaño de la solicitud. Su perfil de prueba de corrupción de memoria usa el primero, aunque los comentarios describen la temporización estándar. Su perfil de prueba de persistencia establece cleanup=0 y min_freq=0. Compare los valores efectivos, unidades incluidas, antes de generalizar los resultados de estos entornos a producción. [4, 9]
El alcance del daño sigue a los permisos del worker
La ejecución como www-data ya puede exponer una aplicación. Según los permisos, el worker puede leer credenciales de bases de datos, modificar contenido con permiso de escritura o alcanzar servicios de backend. Las identidades de servicio compartidas y los directorios con permiso de escritura pueden extender el impacto a otras aplicaciones. Son consecuencias que deben evaluarse frente al despliegue, no afirmaciones de que todos esos efectos se produjeran en la demostración. La pregunta útil es qué puede leer, modificar y alcanzar el proceso.
Huntress recomienda vigilar los procesos hijos inesperados de PHP y de los workers del servidor web. Correlacione la ascendencia de los procesos con las solicitudes, las marcas de tiempo, los archivos nuevos y las conexiones salientes; las aplicaciones legítimas también lanzan herramientas, y la ejecución puede producirse sin un nuevo shell. Las comprobaciones de archivos obsoletos del disparador revisado y el tratamiento incompleto de las respuestas FastCGI del framework muestran además por qué el veredicto de una herramienta necesita atribución. El ejemplo anterior de incidente de PHP de Huntress no guardaba relación explícita con PHP2Shell y no debe reetiquetarse como explotación de estos CVE. [2]
Verifique la corrección donde realmente se ejecutan las solicitudes
Relacione los endpoints públicos con los paquetes o imágenes de PHP que los sirven, sus SAPI, sus extensiones y su configuración efectiva. Incluya el software de terceros y los entornos de hosting que integran PHP. Haga coincidir el aviso detallado de upstream con los paquetes de la distribución o del proveedor, teniendo en cuenta las correcciones retroportadas. Sustituya o reinicie los workers afectados y confirme después que la compilación corregida es la que atiende el tráfico. Para las instalaciones sin actualizaciones de seguridad mantenidas, establezca un plan de actualización o de reducción de la exposición.
El objetivo técnico es conectar la operación vulnerable, sus condiciones alcanzables y el código que restablece el invariante necesario. Un resultado limpio de estas pruebas de concepto no puede sustituir esa correspondencia. Mi enfoque de verificación de correcciones sigue la afirmación hasta el proceso desplegado y comprueba que la corrección pertinente está realmente en servicio. Si necesita esa evaluación para su parque de PHP, el trabajo empieza por el entorno en ejecución y sus evidencias.
Fuentes
- [1] PHP: aviso previo de la versión de seguridad
- [2] Huntress: informe de respuesta a PHP2Shell (texto aportado)
- [3] Manual de PHP: progreso de subida en la sesión
- [4] PHP 8.4.4: callbacks de sesión y propiedad
- [5] PHP 8.4.4: operaciones de las tablas hash
- [6] PHP 8.4.4: destrucción con conteo de referencias
- [7] PHP 8.5.4: implementación de las sesiones
- [8] watchTowr: preguntas frecuentes sobre la RCE de PHP
- [9] Manual de PHP: configuración de las sesiones
¿Necesita que se compruebe esto en su propio producto?
Corrección y verificación¿Quiere aprender a encontrar vulnerabilidades como esta por su cuenta?
Curso individual de revisión de código seguro