CrowdStrike protege a sus clientes frente a las amenazas asociadas a PHP2Shell
Se han divulgado dos vulnerabilidades críticas de corrupción de memoria que permiten la ejecución remota de código (RCE), registradas como CVE-2026-109618 y CVE-2026-109619, en el núcleo de PHP.
21 de octubre de 2026 • Amit Serper • Desde el frente
Se han divulgado dos vulnerabilidades críticas de corrupción de memoria que permiten la ejecución remota de código (RCE) en el núcleo de PHP, el motor que utiliza buena parte de la web. Ambas se conocen como PHP2Shell. La plataforma CrowdStrike Falcon® ofrece de serie visibilidad y cobertura de detección de las actividades posteriores a la explotación de ambas vulnerabilidades.
Registradas como CVE-2026-109618 y CVE-2026-109619, ambas vulnerabilidades tienen una puntuación CVSS 4.0 de 9,2 (crítica) y se clasifican como CWE-824: acceso a puntero no inicializado. Ambas permiten que un atacante remoto ejecute comandos arbitrarios del sistema en un servidor web afectado a través de HTTP sin autenticarse.
Ambos problemas comparten la misma causa de fondo. Cuando PHP se encuentra con un error irrecuperable (por agotar el memory_limit configurado, superar el tiempo máximo de ejecución o perder la conexión con el cliente), no propaga ningún error por la pila de llamadas. Genera un error fatal y utiliza longjmp() para abandonar por completo el contexto de ejecución actual y abortar la solicitud. Cualquier función de C interrumpida mientras construye una estructura de datos se descarta: no se ejecutan destructores, no se revierten los cambios de estado y no queda constancia de que la estructura está incompleta. Después, PHP inicia la secuencia habitual de limpieza, que presupone que todas las estructuras están en un estado coherente.
Las dos CVE describen dónde falla esa suposición:
- CVE-2026-109618 (RCE mediante agotamiento de memoria) abarca los contextos en los que la memoria de la solicitud queda sin inicializar. Una estructura de datos se marca como si contuviera una entrada antes de asignar la memoria correspondiente; si esa asignación rebasa el límite de memoria, la entrada conserva datos residuales de la memoria dinámica (heap). La propia rutina de limpieza de la solicitud fallida lee esos bytes y toma decisiones en función de ellos.
- CVE-2026-109619 (RCE mediante errores fatales) abarca situaciones en las que el estado global queda inválido. Una variable global del módulo sigue apuntando a la memoria dinámica de la solicitud que ya se liberó durante el cierre y sigue indicando que la operación está activa. Una solicitud posterior gestionada por el mismo proceso de trabajo lee esa memoria liberada o toma una decisión basándose en su contenido.
En ambos casos, el atacante controla el desencadenante y, sobre todo, los datos que PHP termina interpretando mal. CrowdStrike ha verificado de extremo a extremo que una única solicitud POST anónima puede hacer que un proceso de trabajo de Apache mod_php inicie un proceso de shell y ejecute un comando especificado por un atacante, sin autenticación, sin sesión y sin que intervenga código vulnerable de la aplicación.
Lo que hace que estas vulnerabilidades sean especialmente graves es su ubicación en la pila de ejecución. PHP analiza los datos de las solicitudes entrantes antes de inicializar las extensiones y de compilar una sola línea de código de la aplicación, por lo que la explotación no depende de lo que aloje el servidor. Una aplicación que recibe un mantenimiento riguroso está tan expuesta como otra descuidada. El ataque también pasa sorprendentemente desapercibido: el proceso de trabajo no falla, el servidor devuelve una respuesta HTTP 200 estándar y los registros de la aplicación no muestran nada inusual.
En nuestras pruebas, desarrollamos un código de exploit que funcionaba de forma consistente en las arquitecturas x86-64 y ARM, mantenía su fiabilidad tras los reinicios de los procesos de trabajo y, en los hosts basados en ARM —cada vez más habituales en las infraestructuras en la nube—, eludía la aleatorización del espacio de direcciones (ASLR, Address Space Layout Randomization) mediante el uso exclusivo de solicitudes HTTP.
Detalles técnicos
La causa de fondo común: longjmp() sin desapilar las llamadas
Ambas vulnerabilidades se encuentran en Zend, el motor central de PHP. Zend es la implementación en C que compila el código fuente de PHP en bytecode, lo ejecuta en una máquina virtual y gestiona todo lo que necesita el lenguaje durante la ejecución: variables, arrays, objetos, recuento de referencias y asignación de memoria. Desarrollado originalmente por Zeev Suraski y Andi Gutmans (el nombre es una contracción de sus nombres de pila) y presentado con PHP 4, no es un componente opcional ni un complemento de terceros: se incluye en todas las compilaciones de PHP, por lo que su código fuente se encuentra en el directorio Zend/ de php-src. En la práctica, esto significa que un fallo en Zend afecta al propio motor de PHP y a todas las aplicaciones, frameworks y extensiones que se ejecutan en el intérprete, independientemente de cómo estén programados.
Dos componentes clave del mecanismo interno de Zend son fundamentales para los problemas descritos aquí: la HashTable, que sirve de base para cada array de PHP, incluida la superglobal $_POST, y Zend Memory Manager (Zend MM), el propio asignador de memoria por solicitud de PHP que se sitúa entre PHP y el sistema operativo. La ruta de error fatal de PHP termina en zend_bailout(), que realiza un LONGJMP para salir de la pila de llamadas de C actual:
// Zend/zend.c
ZEND_API ZEND_COLD ZEND_NORETURN void _zend_bailout(const char *filename, uint32_t lineno)
{
...
CG(unclean_shutdown) = 1;
...
LONGJMP(*EG(bailout), FAILURE);
}
No se ejecuta nada entre el error fatal y el bloque zend_catch que lo engloba. Cualquier función llamante que modificara el estado y confiara en llegar a su propio código de limpieza deja ese estado sin restaurar. Esto solo es seguro si todas esas funciones llamantes están escritas teniendo en cuenta que la terminación puede ser abrupta, y varias no lo están.
CVE-2026-109618: memoria no inicializada que se utiliza durante la misma solicitud
El caso representativo es _zend_hash_str_add_or_update_i(), en Zend/zend_hash.c, la función auxiliar que interviene cada vez que se inserta una clave de cadena en un array de PHP. En la ruta de inserción, incrementa ambos contadores de elementos, obtiene un puntero al nuevo bucket y solo entonces reserva memoria para una copia de la clave:
// Zend/zend_hash.c
add_to_hash:
idx = ht->nNumUsed++;
ht->nNumOfElements++;
p = ht->arData + idx;
p->key = key = zend_string_init(str, len, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
Si zend_string_init() supera memory_limit, Zend MM genera un error fatal que termina en zend_bailout(). La ejecución nunca vuelve para completar el bucket. Ahora, la tabla indica que contiene una entrada más de las que realmente tiene. El valor, el hash y el puntero a la clave de esa entrada dependen de los bytes residuales que dejó el asignador en esa zona de memoria; el array de buckets se asigna mediante emalloc(), sin inicializar la memoria, y solo se restablece expresamente la región del hash.
Durante la limpieza, php_request_shutdown() destruye las variables superglobales y zend_array_destroy() recorre los buckets de nNumUsed. La entrada obsoleta se pasa a i_zval_ptr_dtor(), que trata los bytes residuales como un valor con recuento de referencias: desreferencia el puntero que contienen, decrementa el recuento de referencias en esa dirección y, si llega a cero, realiza una llamada a través de una tabla de punteros a funciones indexada por una etiqueta de tipo que también procede de los bytes residuales.
Para activar la condición, basta con un cuerpo POST codificado en URL, porque PHP rellena $_POST dentro de php_hash_environment(), que php_request_startup() invoca antes de zend_activate_modules() y antes de que se compile ningún script. Un campo con un nombre anidado como ZZZZ…[x]=1 hace que php_register_variable_ex() inserte el nombre externo como una nueva clave de array, por lo que el atacante controla directamente la longitud de la clave que debe reservarse. Al dimensionar ese nombre para superar memory_limit, la ejecución se interrumpe justo durante la asignación de la clave.
El control sobre los bytes sobrantes depende del determinismo del asignador. Zend MM atiende las asignaciones pequeñas mediante listas de bloques libres para cada tamaño, en estricto orden de último en entrar, primero en salir, y nunca pone a cero los bloques recuperados. Por tanto, se puede ajustar el tamaño de un campo anterior de la misma solicitud, cuyo valor PHP copia, descodifica y libera inmediatamente, para que el contenido liberado ocupe exactamente el bloque que posteriormente reclama el array corrupto. La entrada obsoleta pasa a contener un valor definido por completo por el atacante. Lo utilizamos para crear un array falsificado cuyo puntero al destructor apunta a la función system() de libc, con la cadena de comandos almacenada en el mismo bloque.
El único rastro que deja un intento satisfactorio en el servidor es un error fatal genérico en el registro de errores del servidor web:
PHP Fatal error: Allowed memory size of 16777216 bytes exhausted (tried to allocate 3670048 bytes) in Unknown on line 0
La expresión in Unknown on line 0 es la pista: confirma que no llegó a ejecutarse ningún archivo PHP.
CVE-2026-109619: estado global no válido utilizado por una solicitud posterior
La segunda CVE sigue el mismo mecanismo, pero el daño afecta al estado asociado al proceso, lo que desplaza la ventana de explotación a una solicitud posterior. En este caso, la operación interrumpida almacena el estado en una variable global y depende de que se llegue a su propia llamada de limpieza para borrarlo. PHP almacena en caché los contextos activos de serialización y deserialización en variables globales del módulo y reutiliza el puntero almacenado en caché siempre que el nivel de anidamiento sea distinto de cero. Solo la llamada de destrucción correspondiente decrementa ese nivel y borra el puntero, y solo la rutina de inicialización de solicitudes de una extensión lo restablece. La gestión de sesiones funciona de manera similar: guarda el identificador de sesión, el estado del controlador, las variables de sesión y el estado del progreso de carga en variables globales que se borran mediante rutinas específicas de cierre.
Cuando un error fatal interrumpe una de estas operaciones, se omite la limpieza correspondiente. La variable global conserva su puntero y el contador de anidamiento sigue indicando que la operación está activa. A continuación, el cierre de la solicitud libera todo el heap de la solicitud, pero la variable global sobrevive porque pertenece al proceso, no a la solicitud. PHP conserva un puntero de ámbito de módulo que apunta a memoria que ya ha liberado.
Dos factores permiten provocar y controlar esta situación:
- Reutilización de procesos de trabajo: En implementaciones con procesos de trabajo persistentes, como Apache
mod_php, el mismo proceso gestiona la siguiente solicitud. La variable global obsoleta permanece intacta y la operación parece continuar activa. Entonces se lee el contenido de la memoria liberada, se escribe en ella a través del puntero o se toman decisiones en función de su contenido. Lo que ocupe esa memoria para entonces estará bajo la influencia del atacante, porque la solicitud intermedia también es suya: elige su tamaño y contenido, y el comportamiento de asignación de Zend MM es lo bastante determinista como para colocarlos de forma fiable. PHP-FPM cierra el proceso de trabajo si falla el inicio de una solicitud. Esto impide que se reutilice tras una interrupción en esa fase. En cambio, si se interrumpe un script ya en ejecución, el proceso sigue activo e igual de expuesto. - Orden de inicio: php_request_startup() analiza las variables de la solicitud antes de ejecutar las funciones de inicialización de solicitudes de las extensiones. Por tanto, la gestión del progreso de las cargas multipart ejecuta código de sesión y serialización antes de que las extensiones responsables de esas variables globales hayan tenido ocasión de restablecerlas, lo que permite que se utilice un puntero obsoleto en la fase más temprana de una solicitud.
Además del agotamiento de memoria, esta situación puede producirse al superar el tiempo máximo de ejecución o si el cliente interrumpe la conexión. En ambos casos se sigue la misma ruta zend_bailout(). La explotación consta, por tanto, de dos etapas: una solicitud envenena el proceso de trabajo y una segunda provoca el uso de la memoria liberada.
Impacto de las vulnerabilidades
La gravedad de ambos problemas se debe a que se producen al principio del procesamiento de las solicitudes. La corrupción se produce mientras PHP aún prepara las variables internas de la solicitud, antes de inicializar las extensiones y de ejecutar código de la aplicación. Por eso, cualquier endpoint de PHP accesible en un servidor afectado constituye una superficie de ataque activa, incluidos los formularios estáticos, las rutas de inicio de sesión y las comprobaciones de estado. No hay autenticación que eludir, sesión que robar ni endpoint vulnerable que descubrir. La exposición es independiente del CMS, el framework o los plugins que se utilicen.
Además, el ataque apenas deja rastro. Los procesos de trabajo permanecen estables, el servidor devuelve respuestas HTTP 200 y los logs de la aplicación no registran anomalías porque nunca se ejecutó código de la aplicación. La única traza en el servidor es un error fatal por superar el límite de memoria, registrado en el log de errores del servidor web. Este suele descartarse como ruido, lo que priva a los equipos de defensa de telemetría web útil para identificar los endpoints comprometidos.
En servidores web multiproceso como Apache prefork, cada proceso de trabajo deriva de un único proceso principal, por lo que el diseño de memoria del que depende un atacante se mantiene constante tras los reinicios de los procesos de trabajo. Esa estabilidad permite al atacante perfeccionar los payloads sin coste alguno: los mecanismos de monitorización consideran rutinario el reinicio del proceso de trabajo tras un intento fallido.
Las dos CVE presentan diferencias de alcance y de momento de explotación que tienen consecuencias operativas. CVE-2026-109618 es autónoma: una sola solicitud corrompe sus propios datos y el efecto se produce durante la limpieza de esa misma solicitud. Esto hace que el ataque sea rápido, atómico y difícil de reconstruir a posteriori. CVE-2026-109619 abarca varias solicitudes: requiere un paso de envenenamiento y otro de activación contra el mismo proceso de trabajo, pero afecta a un conjunto más amplio de estados internos y puede activarse mediante tiempos de espera agotados y desconexiones del cliente, además del agotamiento de memoria. Las implementaciones con procesos de trabajo persistentes son las que presentan una exposición significativa a esta segunda categoría.
La explotación satisfactoria de cualquiera de los dos problemas permite ejecutar comandos con el contexto de usuario del servidor web, el punto de apoyo habitual para desplegar web shells, extraer credenciales de bases de datos y de API de los archivos de configuración y pivotar hacia redes internas. La aplicación de parches es la única medida correctiva completa. Cuando no sea viable aplicar parches de inmediato, la monitorización del comportamiento de los endpoints es la principal línea de defensa, porque la explotación no es visible en la capa HTTP, pero sus consecuencias sí.
¿Me afecta?
Ambas vulnerabilidades están presentes en todas las versiones de PHP de las series 7.x y 8.x. La disposición concreta de HashTable que origina CVE-2026-109618 se introdujo con una optimización del rendimiento en octubre de 2017 y apareció por primera vez en PHP 7.3.0 (diciembre de 2018). Las rutas de serialización y gestión de sesiones asociadas a CVE-2026-109619 son aún más antiguas: se remontan a PHP 7.1 y PHP 5.4, respectivamente. En otras palabras, las versiones publicadas de PHP llevan más de siete años expuestas a esta posibilidad de explotación. Todas las ramas con soporte están afectadas.
La versión por sí sola no determina la exposición. La condición de agotamiento de memoria debe poder alcanzarse mientras PHP analiza los datos de la solicitud, lo que requiere que el cuerpo de la solicitud pueda crecer lo suficiente como para agotar el límite de memoria antes de que se ejecute ningún script. En la práctica, debe cumplirse aproximadamente esta relación:
memory_limit < 4 × post_max_size
Los valores predeterminados de PHP (post_max_size = 8M, memory_limit = 128M) no cumplen esta condición, por lo que una instalación estándar no puede explotarse mediante la vía de datos codificados en URL que hemos demostrado. Sin embargo, muchas implementaciones en producción están ajustadas para aceptar cargas de archivos grandes, y combinaciones habituales como post_max_size = 64M con memory_limit = 128M, o post_max_size = 100M con memory_limit = 256M, sí la cumplen. Ten en cuenta que post_max_size se puede configurar por directorio en .htaccess o en un host virtual. Por tanto, en un alojamiento compartido, un solo sitio puede adoptar una configuración vulnerable sin necesidad de modificar la configuración de todo el servidor. CrowdStrike reprodujo el problema reduciendo memory_limit a 16M y aumentando post_max_size a 64M únicamente para mantener un payload de prueba pequeño; se llega al mismo error con payloads más grandes frente a los límites mayores indicados arriba.
Para ver qué versión de PHP y qué configuración están en uso en tu servidor, ejecuta php --version. Para ver los valores configurados en tu servidor para las opciones indicadas arriba, ejecuta el siguiente comando:
php -i | grep -E "^(memory_limit|post_max_size|upload_max_filesize|Loaded Configuration File)"
Cómo protege CrowdStrike Falcon a los clientes
CrowdStrike emplea un enfoque por capas para detectar actividades de explotación y postexplotación mediante aprendizaje automático y detecciones basadas en el comportamiento, incluidos indicadores de ataque (IOA). La plataforma Falcon ofrece de serie visibilidad y cobertura de detección de las actividades posteriores a la explotación de estas vulnerabilidades de PHP. Los IOA existentes detectan y previenen actividades habituales de postexplotación, como el despliegue de web shells, la aparición de procesos secundarios sospechosos iniciados por PHP o por procesos del servidor web y la ejecución de comandos maliciosos.
CrowdStrike sigue investigando las técnicas de explotación asociadas a esta vulnerabilidad.

Figura 1. Cadena de explotación de vulnerabilidades de PHP
Identificación de versiones vulnerables de PHP con Falcon Exposure Management
CrowdStrike Falcon® Exposure Management ofrece una visibilidad completa en todos los entornos afectados. Las funciones Network Scan (Netscan) y de gestión de la superficie de ataque externa (External Attack Surface Management, EASM) permiten detectar rápidamente, mediante banners, los recursos expuestos a Internet. Además, la visibilidad basada en sensores permite evaluar automáticamente los parches derivados que suministran los proveedores para las distribuciones de Linux compatibles y los sistemas operativos Windows y macOS.
Falcon Exposure Management detecta estas CVE en todas las versiones compatibles del sensor mediante las comprobaciones existentes del gestor de paquetes y de aplicaciones específicas. Estas comprobaciones no requieren una versión mínima del sensor.
CrowdStrike también ha añadido las siguientes detecciones basadas en sensores. Detectan instalaciones de PHP que las comprobaciones existentes podrían pasar por alto, como las instalaciones personalizadas o las realizadas fuera de un gestor de paquetes. Solo están disponibles en las versiones del sensor indicadas:
- Windows (sensor Falcon 7.40 o posterior): una comprobación en todo el sistema busca ejecutables de PHP en la unidad predeterminada. Esto permite detectar instalaciones adicionales de PHP que las comprobaciones existentes quizá no identifiquen, incluidas las instalaciones personalizadas.
- Linux (sensor Falcon 7.31 o posterior): una comprobación de los procesos en ejecución identifica las instalaciones de PHP realizadas fuera del gestor de paquetes previsto.
Los clientes que utilizan versiones anteriores del sensor siguen estando cubiertos por las comprobaciones del gestor de paquetes y las comprobaciones específicas de aplicaciones. No dispondrán de las comprobaciones adicionales de todo el sistema y de los procesos en ejecución hasta que actualicen.

Figura 2. Identificación de versiones vulnerables de PHP en Falcon Exposure Management
Identificación de amenazas con Next-Gen SIEM
Falcon Next-Gen SIEM: panel Emerging Threat Response
La cobertura de detección de CrowdStrike Falcon® Next-Gen SIEM para esta amenaza (la nueva sección "PHP Remote Code Execution Vulnerabilities (CVE-2026-109618/109619)" del panel CrowdStrike Falcon - Emerging Threat Response) se ha publicado en producción como parte del lanzamiento de los paquetes de contenido de NG-SIEM.
El panel Emerging Threat Response es una herramienta de respuesta especializada que ofrece a los clientes de CrowdStrike visibilidad inmediata de las campañas activas y las vulnerabilidades recién descubiertas que podrían afectar a sus entornos. Este panel combina la amplia telemetría de los módulos de la plataforma Falcon con datos de terceros incorporados a Falcon Next-Gen SIEM. Ofrece consultas de detección e investigación de amenazas listas para usar, con las que los equipos de seguridad pueden determinar rápidamente la exposición a amenazas e identificar recursos potencialmente comprometidos.
- Ver Next-Gen SIEM > Log management > Dashboards

Figura 3. Identificación del panel Emerging Threat Response en Falcon Next-Gen SIEM
Plantillas de reglas de correlación de Falcon Next-Gen SIEM
Hemos añadido una nueva plantilla de reglas de correlación (CRT, Correlation Rule Template) de Falcon Next-Gen SIEM para detectar y responder ante las vulnerabilidades CVE-2026-109618 y CVE-2026-109619. Estas reglas ofrecen cobertura frente a los intentos de explotación relacionados con estas vulnerabilidades.
Generic - Web - PHP Memory Exhaustion During Request Startup (CVE-2026-109618 & CVE-2026-109619)
Esta detección identifica errores fatales de PHP por agotamiento de memoria que se producen antes de que comience a ejecutarse un script de PHP. En concreto, detecta un patrón revelador que aparece en los registros de errores de los servidores web: "Allowed memory size of N bytes exhausted" junto con "in Unknown on line 0." El marcador "Unknown on line 0" distingue los fallos de la fase de inicio de los numerosos errores de memoria que se producen dentro de los scripts. Así, los equipos de defensa disponen de una señal más fiable de que algo está provocando que el intérprete de PHP agote la memoria antes de que se ejecute código de la aplicación. Patrones como este pueden indicar un intento de explotación del motor de PHP. Los clientes pueden consultar la plantilla aquí ([US-1] [US-2] [US-3] [EU-1] [US-GOV-1] [US-GOV-2]).
Conclusión y recomendaciones
CrowdStrike sigue de cerca esta situación y ofrecerá actualizaciones a medida que disponga de nueva información. Al mismo tiempo, seguimos desarrollando e implementando nuevos mecanismos de detección basados en el comportamiento en la plataforma Falcon para detectar y prevenir actividades maliciosas relacionadas con las vulnerabilidades de PHP divulgadas CVE-2026-109618 y CVE-2026-109619.
Recomendamos a los clientes que identifiquen cualquier sistema que ejecute versiones vulnerables de PHP y apliquen las últimas actualizaciones de seguridad de acuerdo con las directrices oficiales de PHP o del proveedor.