PHP2Shell: cómo saber si explotaron tu servidor con un solo grep (y por qué tu access.log no lo encuentra)

Para saber si explotaron PHP2Shell en tu servidor, AWS publicó una sola firma: peticiones POST con PHP_SESSION_UPLOAD_PROGRESS en el cuerpo, desde el 17 de octubre. El problema es que en un servidor nginx o Apache con la configuración por defecto, ese grep no devuelve nada, aunque te hayan atacado.

Aquí tienes lo que encontró AWS, dónde vive realmente esa firma y qué puedes revisar hoy en un servidor que no guarda el cuerpo de las peticiones. Las vulnerabilidades son CVE-2026-109618 y CVE-2026-109619.

¿Qué vio AWS antes de la divulgación de PHP2Shell?

Amazon Threat Intelligence detectó un intento de explotación de PHP2Shell el 17 de octubre de 2026 en MadPot, su red de honeypots. Eso fue cuatro días antes de que el proyecto PHP levantara el embargo y publicara la corrección. La petición vino de una IP asociada a una red comercial de anonimización. Según AWS, eso indica un actor de amenazas real, aunque no sabe cómo obtuvo los detalles del exploit mientras el fallo seguía bajo embargo.

El resto de la cronología fue muy rápida:

  • Alrededor de las 02:00 UTC del 21 de octubre: AWS detectó las primeras pruebas de concepto públicas, doce horas antes de que se levantara el embargo.
  • Entre las 02:00 y las 08:00 UTC de ese día: llegaron más intentos contra MadPot desde otra infraestructura anonimizada. Los payloads eran distintos, lo que sugiere varios actores.
  • A las 14:00 UTC del 21 de octubre: se levantó el embargo y salieron los parches. En cuestión de horas aparecieron nuevas pruebas de concepto, que se usaron casi de inmediato contra los sensores de AWS.

AWS también aclara que su análisis sigue en curso y que no está claro si alguno de esos intentos habría tenido éxito contra sistemas reales. La aclaración importa: un intento contra un honeypot no es una intrusión confirmada.

La instrucción práctica, en cambio, es clara. AWS recomienda empezar la investigación desde la primera explotación conocida, no desde la fecha de divulgación, y para PHP2Shell eso significa el 17 de octubre.

¿Cuál es la firma de PHP2Shell en los logs?

Según AWS, la firma es una petición POST con la cadena PHP_SESSION_UPLOAD_PROGRESS dentro de un formulario de subida. Esa cadena es el valor por defecto de la directiva de PHP session.upload_progress.name: el nombre del campo de formulario que activa el seguimiento del progreso de subida.

Dos datos de la documentación oficial explican por qué esto afecta a casi cualquier servidor:

  • La función viene activada por defecto. El manual de PHP indica que session.upload_progress.enabled tiene el valor "1" por defecto. Si nadie lo desactivó, tu PHP está atento a ese campo.
  • Se ejecuta antes que tu código. El manual aclara que el progreso de subida se escribe en la sesión antes de que se ejecute cualquier script. Según AWS, las dos CVE están en cómo PHP procesa las peticiones multipart/form-data, antes de que llegue tu aplicación. La validación de tu framework nunca llega a ver esa petición.

AWS confirma la firma, pero no publica el mecanismo interno del exploit, así que este artículo no describe ninguno.

¿Por qué grep no encuentra PHP2Shell en tu access.log?

Porque la cadena va en el cuerpo de la petición, y los access logs por defecto no guardan el cuerpo.

El formato predefinido combined de nginx, que es el que se usa si no indicas otro, registra esto:

'$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"'

El formato combined de Apache registra los mismos campos: IP del cliente, identidad, usuario, hora, línea de la petición, código de estado, tamaño de la respuesta, referer y user agent.

Ninguno de los dos guarda el cuerpo, y ninguno guarda la cabecera Content-Type. Por eso este comando:

grep PHP_SESSION_UPLOAD_PROGRESS access.log

no devuelve nada en un servidor por defecto, te hayan atacado o no. Un resultado vacío aquí no te dice nada.

¿Dónde sí funciona el grep: AWS WAF y logs con el cuerpo de la petición?

El grep solo funciona sobre logs que guardaron el cuerpo de las peticiones:

  • AWS WAF. Si usas el grupo de reglas administradas para aplicaciones PHP (AWSManagedRulesPHPRuleSet), la versión 2.3 añade la regla PHP2Shell_BODY. Las peticiones que coinciden reciben la etiqueta awswaf:managed:aws:php-app:PHP2Shell_Body. Busca esa regla o esa etiqueta en tus logs de AWS WAF. AWS convirtió la versión 2.3 en la predeterminada el 22 de octubre. Si tu web ACL fija una versión estática anterior, la regla no está activa.
  • Un WAF o proxy inverso configurado para registrar el cuerpo de las peticiones. Si activaste ese registro a propósito, busca la cadena desde el 17 de octubre en adelante.

Hay un límite que conviene tener presente. AWS WAF solo inspecciona la primera parte del cuerpo: 8 KB en Application Load Balancer y AppSync, y 16 KB por defecto (hasta 64 KB) en CloudFront y API Gateway. Lo que supera ese límite no se inspecciona, así que un log de WAF limpio es una buena señal, pero no una prueba.

¿Cómo revisar un servidor que no guarda el cuerpo de las peticiones?

Si no puedes ver la firma, trabaja con el resto de la lista de AWS. Estos pasos van de menor a mayor esfuerzo.

Indicadores de compromiso (IOC): investiga, no bloquees

AWS publicó seis direcciones IP de origen:

Dirección IP Fecha observada
103.216.220.92 17 oct 2026
31.223.184.166 21 oct 2026
37.19.200.5 21 oct 2026
134.209.159.64 21 oct 2026
103.114.163.188 21 oct 2026
95.217.98.37 21 oct 2026

AWS aclara que pertenecen a infraestructura de anonimización compartida y que no son exclusivas de esta actividad, así que recomienda investigar cualquier coincidencia en lugar de bloquear las direcciones. En los dos formatos por defecto, la IP del cliente es el primer campo, así que una coincidencia es fácil de encontrar:

grep -E '^(103\.216\.220\.92|31\.223\.184\.166|37\.19\.200\.5|134\.209\.159\.64|103\.114\.163\.188|95\.217\.98\.37) ' access.log

Ejecútalo sobre el archivo que indique tu directiva access_log (nginx) o CustomLog (Apache), incluidas las copias rotadas que cubran el 17 de octubre. Una coincidencia en una petición POST merece una revisión más a fondo. Que no haya coincidencias no te descarta: al momento de publicar esta nota (23 de octubre), la lista solo refleja lo que AWS vio en sus propios sensores y probablemente crecerá.

Revisa las peticiones POST desde el 17 de octubre

En los dos formatos, la línea de la petición empieza por el método, así que "POST filtra el log a las peticiones que importan. Busca POST a rutas que normalmente no los reciben, ráfagas desde una misma IP y respuestas 500.

Lee los logs de errores de PHP y del servidor

AWS recomienda activar el registro de errores y revisar los logs de Apache, FPM o del proxy en busca de advertencias repetidas, avisos de funciones obsoletas, errores fatales y timeouts en rutas que un atacante puede alcanzar. También advierte que la ausencia de errores no es una prueba de seguridad.

Revisa el propio servidor

La última recomendación de AWS es buscar ejecuciones de procesos inesperadas o archivos modificados en tus servidores de aplicaciones web. Si algo no cuadra ahí, trata el servidor como comprometido, digan lo que digan los logs.

¿Qué hacer mientras investigas?

Actualiza primero. Las versiones corregidas son PHP 8.5.12, 8.4.27, 8.3.36 y 8.2.35. Según AWS, actualizar es la única solución completa para las dos CVE y no existe una solución basada solo en configuración que cubra ambas.

Mientras no puedas actualizar, AWS recomienda estos pasos:

  • Configura session.upload_progress.enabled=0 y reinicia todos los workers de PHP. AWS sugiere mantenerlo desactivado incluso después de parchear. Puedes confirmar el valor activo en la salida de phpinfo().
  • En PHP 8.5, configura fatal_error_backtraces=0 y reinicia todos los workers.
  • Valora bloquear las subidas multipart/form-data si tu aplicación no las necesita, después de comprobar qué dejaría de funcionar.

PHP 8.1 y versiones anteriores ya no reciben correcciones oficiales. Si usas una de ellas, consulta con tu distribución o con tu proveedor de soporte extendido si hay un backport, o planifica el paso a una rama con soporte.

Para confirmar qué versión necesitas, revisa nuestra guía CVE-2026-109618 y CVE-2026-109619: qué versión de PHP necesitas y cuándo sale el parche. Para comprobar si tu PHP es vulnerable, usa la skill oficial de Codex.

¿Por qué importa el 17 de octubre más allá de PHP?

AWS saca una lección más amplia de este caso. La divulgación coordinada sigue siendo el modelo correcto, pero un embargo solo protege a los defensores hasta que otra persona encuentra el mismo fallo. AWS también señala que el análisis asistido por IA abarata convertir un diff de un parche en un exploit funcional, así que el tiempo entre el parche y la explotación se está acortando.

Para sistemas expuestos a internet, AWS recomienda tres cambios:

  • Medir los plazos de parcheo de vulnerabilidades críticas en horas, no en SLA de 7 o 30 días.
  • Empezar las investigaciones desde la primera explotación conocida, no desde la fecha del aviso.
  • Invertir en detecciones que no dependan de una firma para una CVE concreta.

La firma de PHP2Shell llegó después de los primeros ataques. La pregunta que puedes responder hoy con tus logs es la misma que podías haber respondido el 17 de octubre: ¿qué peticiones POST llegaron a tu PHP que no deberían haber llegado?


¿Y tú? ¿Tu stack guarda en algún lugar el cuerpo de las peticiones: en el WAF, en el proxy o en ninguna parte?

¿Dónde podrías buscar PHP_SESSION_UPLOAD_PROGRESS hoy?
  • En los logs de AWS WAF
  • En otro WAF o proxy que guarda el cuerpo de las peticiones
  • En ningún sitio: solo tengo los access logs por defecto
  • Prefiero otra opción (cuéntanos cuál)
0 votantes

Comenta abajo o, si este artículo te llegó por correo, responde directamente al email: tu respuesta se publica aquí.

Relacionado: CVE-2026-109618 y CVE-2026-109619: qué versión de PHP necesitas y cuándo sale el parche