
PoC privado de Fortbridge para la cadena RCE de CVE-2026-32740 Next.js/sharp leak-to-memcpy-GOT
Prueba de concepto funcional que convierte CVE-2026-32740 en la pila de imágenes Next.js/sharp con versiones fijas en una escritura en dirección elegida y una llamada de retorno validada a /usr/bin/id.
Este es un repositorio privado de investigación de Fortbridge. Úsalo únicamente contra el laboratorio incluido o contra otro sistema para el que tengas autorización explícita de prueba.
El exploit utiliza únicamente las rutas HTTP de subida y optimización de imágenes del objetivo:
g_module_open_full de GLib, una rutina de GModule que envuelve dlopen para cargar una biblioteca compartida nativa desde una ruta de archivo. El exploit calcula su dirección en tiempo de ejecución como la base de libvips recuperada más el desplazamiento fijo del perfil 0x3e995e. Cargar la biblioteca ejecuta su constructor./usr/bin/id. Envía ese ELF a través de la ruta pública de subida con el nombre de imagen x.jpg y el tipo MIME image/jpeg.memcpy@GOT - 16.uploads/x.jpg inmediatamente antes de la ranura GOT y reemplaza memcpy@GOT con el cargador GModule derivado. La siguiente fila llama a ese cargador con la ruta de la biblioteca ya en RDI./usr/bin/id por TCP. Un token por intento evita que una llamada de retorno obsoleta se cuente como éxito; la línea uid=... devuelta es la prueba.Las ejecuciones de validación originales relativas a libvips están registradas en
evidence/libvips-gmodule-rce-10x.json.
Los diez procesos nuevos devolvieron una salida válida de /usr/bin/id con diez bases aleatorizadas de libvips distintas y diez direcciones de cargador derivadas de forma independiente.
Los datos exactos de la ejecución en Ubuntu están en
evidence/libvips-gmodule-pie-rce-10x.json.
Los datos exactos de la ejecución en Debian 13 APT están en
evidence/debian13-apt-libvips-gmodule-rce-10x.json.
Probamos el exploit completo contra 10 procesos Node de Ubuntu recién iniciados
y 10 procesos Node de Debian recién iniciados. Las 20 ejecuciones alcanzaron la ejecución de comandos y devolvieron la salida de /usr/bin/id del objetivo. El exploit también calculó una dirección de cargador GModule distinta para cada base aleatorizada de libvips.
Antes de crear el payload final, el exploit puede necesitar varios intentos de fuga de ASLR. Cada intento sube el AVIF de fuga, lo envía por la ruta de optimización y comprueba el PNG devuelto en busca de cuatro punteros que identifiquen un perfil y una base de libvips. Ubuntu necesitó entre 3 y 23 intentos. Debian necesitó entre 3 y 15. Una evidencia incompleta o ambigua provoca otro intento en lugar de un perfil adivinado.
El proyecto también tiene 96 pruebas de regresión automatizadas. Estas son comprobaciones a nivel de código, no 96 ejecuciones adicionales del exploit. Cubren la validación de perfiles, la clasificación de punteros devueltos, la calibración del heap, el cálculo de direcciones, la construcción del payload y el fallo seguro cuando la evidencia no coincide con un objetivo soportado.
Ambos objetivos x86-64 usan Next.js 15.5.23, sharp 0.34.4, libvips 8.17.2 incluida, libheif 1.20.2 incluida, ASLR y NX. Sus runtimes nativos difieren:
| Perfil | Node | glibc | libstdc++ | Relación del selector |
|---|---|---|---|---|
| Ubuntu | 25.8.1, PIE ET_DYN | 2.43-2ubuntu2.4 | 6.0.35 | 0x6000 - 0x690 = 0x5970 |
| Debian 13 | Debian APT 20.19.2, PIE ET_DYN | 2.41-12+deb13u4 | 6.0.33 | 0x6000 - 0x3a0 = 0x5c60 |
Los build IDs completos y los valores SHA-256 están en
profiles/native_stack_profiles_pie.json.
Las mediciones del paquete Debian, artefacto, smoke de rutas y diseño de cinco ciclos de vida están capturadas en
evidence/debian13-profile-derivation.json.
El objetivo Debian usa el paquete estándar de la distribución
nodejs=20.19.2+dfsg-1+deb13u3; Node no está compilado desde el código fuente.
La cadena no necesita la base aleatorizada de Node. Su objetivo de control es la rutina interna g_module_open_full dentro de libvips, cuya base aleatorizada se recupera de los píxeles devueltos. Cada perfil versionado fija las constantes específicas de la compilación requeridas por el exploit:
memcpy@GOT y el desplazamiento de g_module_open_full más sus bytes de instrucción de validación;La base aleatorizada de libvips, la dirección resultante del cargador en tiempo de ejecución y el selector final de dos bytes no están fijos. Se derivan para cada proceso objetivo a partir de los píxeles devueltos y del perfil seleccionado.
El exploit falla de forma segura cuando un perfil está malformado o los píxeles devueltos no seleccionan exactamente un par perfil/base soportado. El perfil estándar también requiere su registro de heap devuelto completo. Un perfil es una afirmación de compatibilidad exacta, por lo que el operador debe verificar los artefactos del objetivo sin conexión antes de usarlo.
La ABI del cargador es importante. La llamada sobrescrita proporciona la ruta de la biblioteca en RDI, un puntero a fila de imagen en RSI y la longitud de copia de 58 bytes en RDX. Esta rutina GModule perfilada exacta usa solo bits de flag soportados de ESI y no desreferencia RDX en la ruta de carga exitosa. Un arnés sin conexión validó ese punto de entrada y la firma de instrucciones antes de que se usara en las ejecuciones medidas.
ffmpeg con el codificador libaom-av1cc en la máquina atacanteInstala la única dependencia de Python:```bash python3 -m venv .venv . .venv/bin/activate python3 -m pip install -r requirements.txt
Confirmar la compatibilidad con la codificación AV1:```bash
ffmpeg -hide_banner -encoders | grep libaom-av1