PHP Vulnerabilidades CVE Wazuh Ciberseguridad Hardening

PHP2Shell (CVE-2026-109618): cómo mitigarlo sin parche

PHP2Shell (CVE-2026-109618 y 109619): qué versiones de PHP afecta, qué mitigaciones funcionan mientras no parcheas y cómo detectar explotación con Wazuh.

AI Security
9 min lectura

PHP2Shell son dos vulnerabilidades críticas de ejecución remota de código en el propio intérprete de PHP: CVE-2026-109618 y CVE-2026-109619, ambas con CVSS v4.0 9.2. Los detalles se filtraron antes del parche, y el 21 de octubre de 2026 ya se reportaba explotación activa. Este artículo resume qué reduce la exposición mientras no puedes parchear y cómo detectar si alguien ha entrado.

Actualizado el 21-oct-2026. PHP aún no ha publicado el aviso técnico completo; parte de lo que sigue (vector, versiones corregidas) viene de investigadores y puede cambiar. La fuente oficial es php.net.

Lo primero: no hay mitigación completa sin parchear

El equipo de PHP ha dicho que estos fallos no se pueden mitigar por configuración, y GMO Flatt Security retiró su workaround porque espera bypasses. Todo lo que verás aquí son capas que reducen la superficie, no sustitutos del parche. Si una aplicación PHP expuesta a internet no es crítica, lo más seguro hasta parchear es apagarla: varias organizaciones neerlandesas (Tweakers, la Tweede Kamer, el NCSC) lo hicieron por precaución.

Qué versiones de PHP afecta PHP2Shell

Rama Afectada Parche oficial
8.2 · 8.3 · 8.4 · 8.5SíSí (versiones esperadas: 8.2.35, 8.3.36, 8.4.27, 8.5.12; pendientes de confirmar)
8.1 y 8.0SíNo (fuera de soporte)
7.x y 5.4+Sí, según Sansec y FlareNo

Ojo con un error frecuente: no basta con estar en PHP 8.0 o superior. Las ramas actuales también son vulnerables hasta que instales la versión corregida, y 8.0 y 8.1 no recibirán parche. Si estás ahí, tus opciones son migrar a 8.2+, usar el paquete de tu distribución o proveedor de soporte extendido si confirma el backport de estos CVE, o aislar la aplicación. Un aviso práctico: el repositorio de Remi no publicará actualizaciones el día del parche; tira de los paquetes de la distribución o compila.

Cómo funciona el ataque (lo que se sabe)

Los análisis independientes apuntan a dos caminos: un use-after-free en el código de session upload progress (ext/session) y un segundo fallo en el parser de peticiones multipart/form-data. Lo importante para defenderse: PHP procesa el cuerpo multipart antes de ejecutar tu código. Un middleware de Laravel, un plugin de seguridad de WordPress o el modo mantenimiento de la aplicación llegan tarde. La defensa tiene que estar delante de PHP: en el servidor web, el WAF o la red.

Mitigaciones mientras no hay parche, por capas

1. Desactivar session.upload_progress

Cierra (con suerte) uno de los dos caminos y no rompe las subidas de archivos. Es INI_PERDIR: no vale ini_set(), hay que ponerlo en php.ini o en el pool de FPM y recargar.

; php.ini o /etc/php/8.x/fpm/conf.d/99-php2shell.ini
session.upload_progress.enabled = 0
session.use_strict_mode = 1

O por pool de PHP-FPM, que no puede sobrescribir la aplicación:

; /etc/php/8.x/fpm/pool.d/www.conf (en cada pool)
php_admin_flag[session.upload_progress.enabled] = off
php_admin_flag[session.use_strict_mode] = on

# después:
sudo systemctl reload php8.x-fpm      # con mod_php: sudo systemctl restart apache2

En hosting compartido sin acceso a php.ini: .user.ini en la raíz (tarda hasta 5 minutos en aplicarse) o php_flag en .htaccess con mod_php. session.use_strict_mode solo aplica a sesiones nativas en ficheros, no a Redis o base de datos.

2. Bloquear multipart/form-data en el servidor web

Es la capa más eficaz porque corta los dos caminos antes de PHP. El precio: rompe todas las subidas de archivos y cualquier formulario multipart (en algunas tiendas, también el checkout). Prueba los flujos críticos después de aplicarlo y quítalo al parchear.

nginx (el patrón de Flare; la regex sin anclar también caza mayúsculas, comas y espacios en la cabecera):

# contexto http {}
map $http_content_type $block_multipart {
    default        0;
    "~*multipart"  1;
}

# en cada server {} que sirva PHP
if ($block_multipart) {
    return 403;
}

Apache (usa %{HTTP:Content-Type}; %{CONTENT_TYPE} no coincide con nada en este contexto y falla en silencio):

# en cada <VirtualHost> que sirva PHP
RewriteEngine On
RewriteCond %{HTTP:Content-Type} multipart [NC]
RewriteRule ^ - [F]

Si necesitas subidas en rutas concretas, puedes permitir solo esas rutas, sabiendo que siguen expuestas. Para comprobar que tu regla no se salta con variantes de la cabecera (mayúsculas, comas, cabeceras duplicadas, cuerpo chunked), el repositorio open-source php2shell-guard incluye reglas para nginx, Apache, Caddy, HAProxy y ModSecurity con un test de esas variantes. Revisa el código antes de ejecutarlo.

3. ModSecurity: bloquear solo el campo de upload progress

Si no puedes renunciar a las subidas, una regla que deniega peticiones con el campo PHP_SESSION_UPLOAD_PROGRESS (propuesta por Pentestit, probada en ModSecurity 2 con Apache 2.4):

# Requiere SecRequestBodyAccess On
# Ajusta el nombre si cambiaste session.upload_progress.name
SecRule ARGS_POST_NAMES "@streq PHP_SESSION_UPLOAD_PROGRESS" \
    "id:1026109618,phase:2,deny,status:403,log,\
    msg:'PHP2Shell: campo de upload progress bloqueado'"

Solo cubre la vía de upload progress y depende de que ModSecurity interprete el multipart igual que PHP, cosa que no siempre ocurre. Es la capa más débil de las tres.

4. Cloudflare u otro WAF delante

Cloudflare desplegó reglas WAF de emergencia para todos los planes, incluido el gratuito. Comprueba que las managed rules están activas y, sobre todo, cierra el acceso directo al origen: si el servidor acepta tráfico de cualquier IP, quien conozca la IP real se salta el WAF. Limita el firewall del origen a los rangos de IP publicados por Cloudflare.

5. Medidas estructurales

  • Sacar de internet lo que no necesite estar expuesto: paneles de administración, intranets, herramientas internas detrás de VPN o lista de IPs permitidas.
  • Modo solo lectura: dejar pasar solo GET y HEAD mantiene la web visible y corta los cuerpos POST.
  • Apagar PHP-FPM y servir una página estática de mantenimiento en lo que no sea crítico.
# modo solo lectura: solo GET y HEAD llegan a PHP
if ($request_method !~ ^(GET|HEAD)$) {
    return 503;
}

Medidas de higiene como disable_functions (exec, system, shell_exec, proc_open…) dificultan la post-explotación, pero los exploits de corrupción de memoria ya se han saltado disable_functions antes. No cuentes con ello.

Comprueba qué tienes realmente

La configuración que importa es la del PHP que sirve la web, no la de la CLI (php -i lee otro php.ini):

# versión que usa la web (no la CLI): crea, consulta y BORRA
echo '<?php phpinfo();' | sudo tee /var/www/html/_pi.php
# abre https://tu-dominio/_pi.php y busca "session.upload_progress.enabled"
sudo rm /var/www/html/_pi.php

# paquetes instalados
php -v; dpkg -l | grep -E '^ii +php' ; rpm -qa | grep -i php

Cómo detectar explotación con Wazuh

Un RCE en PHP acaba casi siempre igual: el usuario web (www-data) lanza una shell, curl o wget, o aparece un fichero PHP nuevo en el webroot. Con Wazuh puedes vigilar las dos cosas.

1. Auditar los comandos que ejecuta el usuario web (ajusta el uid en distribuciones que no sean Debian/Ubuntu):

# /etc/audit/rules.d/php-exec.rules
# comandos ejecutados por el usuario web (uid 33 = www-data en Debian/Ubuntu)
-a always,exit -F arch=b64 -S execve -F euid=33 -k php_exec
-a always,exit -F arch=b32 -S execve -F euid=33 -k php_exec

sudo augenrules --load

2. En el agente: leer el log de auditd y vigilar el webroot con FIM en tiempo real

<!-- ossec.conf del agente -->
<localfile>
  <log_format>audit</log_format>
  <location>/var/log/audit/audit.log</location>
</localfile>

<syscheck>
  <directories realtime="yes" check_all="yes" report_changes="yes">/var/www</directories>
</syscheck>

3. En el manager: alerta de nivel alto cuando el usuario web ejecuta algo

<!-- /var/ossec/etc/rules/local_rules.xml (manager) -->
<group name="php2shell,audit,">
  <rule id="100260" level="12">
    <if_sid>80700</if_sid>
    <field name="audit.key">php_exec</field>
    <description>PHP2Shell?: el usuario web ha ejecutado $(audit.exe)</description>
    <mitre><id>T1505.003</id></mitre>
  </rule>
</group>

Valida la regla con /var/ossec/bin/wazuh-logtest antes de darla por buena y añade excepciones para lo legítimo (algunas aplicaciones llaman a convert, git o sendmail). Cualquier sh, bash, curl, wget o nc con el usuario web como origen merece mirarse.

Después de parchear

  • Reinicia todo lo que mantiene PHP en memoria: PHP-FPM, Apache con mod_php, Octane, workers de colas. Sin reinicio sigue corriendo el binario vulnerable.
  • Si tu proveedor hace backport, la versión upstream puede no cambiar: compara la revisión completa del paquete con su aviso de seguridad.
  • Busca compromiso, no solo vulnerabilidad: web shells, ficheros PHP nuevos o modificados, usuarios administradores nuevos y conexiones salientes raras. Aikido recomienda revisar desde junio de 2026.
  • Retira las reglas temporales (bloqueo multipart, solo lectura) cuando el parche esté aplicado y verificado.

Preguntas frecuentes

¿Qué versiones de PHP afecta PHP2Shell?

Según Sansec y Flare, todas desde PHP 5.4 hasta las actuales (8.5 incluida). PHP publicará la lista oficial con el parche. Solo las ramas con soporte (8.2, 8.3, 8.4 y 8.5) recibirán corrección oficial; PHP 8.1 y anteriores no tendrán parche.

¿Basta con desactivar session.upload_progress para protegerse de PHP2Shell?

No. Reduce exposición, pero el equipo de PHP ha dicho que no se puede mitigar por configuración, e investigadores apuntan a que el segundo fallo está en el parser multipart y se activa igual con el ajuste desactivado. Es una capa más, no una solución.

¿Me protege Cloudflare frente a PHP2Shell?

Ayuda: Cloudflare desplegó reglas WAF para todos los planes, incluido el gratuito. Pero solo filtra el tráfico que pasa por Cloudflare. Si la IP del servidor de origen acepta conexiones directas, el atacante se salta el WAF. Hay que limitar el origen a los rangos de IP de Cloudflare.

Tengo PHP 8.0 o 8.1, ¿qué hago?

No recibirás parche oficial. Las opciones son migrar a 8.2 o superior, usar los paquetes de tu distribución o proveedor de soporte extendido si confirman el backport de estos CVE, o aislar la aplicación (sacarla de internet, VPN o lista de IPs permitidas) mientras migras.


Fuentes:

Artículos relacionados:


Wazuh

Detecta la explotación, no solo la vulnerabilidad

Desplegamos Wazuh con auditoría de comandos, FIM sobre el webroot y reglas para tu stack, para que un RCE como este no pase desapercibido.