← Volver al blog
// Post

"php2shell — RCE pre-autenticación en PHP mod_php"

Dos vulnerabilidades críticas fueron divulgadas recientemente en PHP, afectando servidores Apache ejecutando mod_php: CVE-2026-109618 y CVE-2026-109619. Cuando se encadenan, permiten ejecución remota de código sin autenticación — sin credenciales, sin bugs a nivel de aplicación, sin acceso a /proc o GDB. Solo peticiones HTTP contra el propio runtime de PHP.

En DeepLook Labs, dedicamos tiempo analizando ambas vulnerabilidades, entendiendo la cadena de explotación y construyendo un exploit funcional que logra RCE en condiciones controladas. En este post compartimos un resumen de lo que estas CVEs representan y lo que pudimos confirmar.

Las Vulnerabilidades

CVE-2026-109618 — Bucket stale en HashTable por OOM

Esta vive en _zend_hash_str_add_or_update_i dentro del Zend Engine. Cuando una condición de Out-of-Memory ocurre durante el procesamiento de una petición HTTP, la HashTable puede dejar buckets stale — punteros a memoria ya liberada. En la siguiente petición manejada por el mismo worker prefork de Apache, esos punteros se reutilizan. Use-After-Free clásico.

CVE-2026-109619 — Globales de sesión colgantes

Esta apunta al mecanismo de upload progress de sesión en ext/session. Cuando OOM ocurre antes de PG(modules_activated)=1 — durante el parsing de multipart/form-data con session upload progress habilitado — la función de shutdown del módulo (zend_deactivate_modules()) se salta por completo. El RSHUTDOWN de sesión nunca se ejecuta, dejando PS(id) y PS(http_session_vars) apuntando a memoria per-request que ya fue liberada.

La siguiente petición que llega al mismo worker accede a esos punteros colgantes. UAF controlable.

Lo que confirmamos

Tras analizar la cadena de vulnerabilidades descrita por los investigadores originales, desarrollamos un exploit funcional que logra RCE en tres etapas:

  1. Heap Leak — disparar el OOM para dejar PS(id) colgante, luego hacer spray de campos de formulario para reclamar la memoria y filtrar punteros del heap vía Set-Cookie. Los punteros alineados a 2 MB revelan la base del chunk ZendMM.

  2. Code Leak — un segundo OOM con padding diferente para superponer buckets de la HashTable. El campo pDestructor (un puntero a zval_ptr_dtor dentro de libphp) se filtra, dándonos la dirección base de libphp. Bypass de ASLR puramente in-band.

  3. Ejecución de código — reclamar PS(http_session_vars) con una HashTable falsa cuyo pDestructor apunta a zif_system. Cuando PHP limpia la HashTable, llama pDestructor(arg1) → system("cmd").

El exploit fue confirmado contra PHP 8.4.26 en x86_64, logrando ejecución de comandos como www-data. El setup del laboratorio usó memory_limit=8M, session.upload_progress.enabled=On y un solo worker Apache (MaxRequestWorkers=1).

También confirmamos crash/DoS confiable (10/10 SIGABRT) en PHP 8.2.34 (arm64) y PHP 8.4.26 (x86_64).

Técnica de trigger del OOM

El OOM se dispara mediante uploads con nombres de archivo vacíos y nombres de campo profundamente anidados:

Content-Disposition: form-data; name="f[0_0][0_1]...[0_14]"; filename=""

Cada upload con nombre de archivo vacío consume ~35 KB durante el parsing multipart, pero el cuerpo del upload puede estar vacío — manteniendo el POST total bajo 1 MB mientras agota el memory limit. Con memory_limit=8M, aproximadamente 200–400 uploads son suficientes.

Versiones corregidas

La corrección se incluyó en el commit 3584a98bda y está disponible en:

  • PHP 8.2.35
  • PHP 8.3.36
  • PHP 8.4.27
  • PHP 8.5.12

Mitigación

Si no puedes actualizar de inmediato:

  • Deshabilita el upload progress de sesión: session.upload_progress.enabled=Off
  • Usa PHP-FPM en lugar de mod_php (el aislamiento de proceso impide la reutilización del worker)
  • Mantén max_file_uploads en su valor por defecto (20)
  • Nunca ejecutes display_errors=On en producción

Créditos

El writeup original detallado y la técnica de amplificación por nombre de archivo vacío fueron documentados por Calif / Wiz (Ryan Austin — @lcfr_hax), incluyendo el análisis del flujo BG(unserialize) y la extensión a entornos WordPress.

Análisis, desarrollo del exploit y validación en laboratorio realizados por Walleson Moura (@ph0r3nsic) en DeepLook Labs.

Peace!