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:
-
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íaSet-Cookie. Los punteros alineados a 2 MB revelan la base del chunk ZendMM. -
Code Leak — un segundo OOM con padding diferente para superponer buckets de la HashTable. El campo
pDestructor(un puntero azval_ptr_dtordentro de libphp) se filtra, dándonos la dirección base de libphp. Bypass de ASLR puramente in-band. -
Ejecución de código — reclamar
PS(http_session_vars)con una HashTable falsa cuyopDestructorapunta azif_system. Cuando PHP limpia la HashTable, llamapDestructor(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_uploadsen su valor por defecto (20) - Nunca ejecutes
display_errors=Onen 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!