← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-46529 · CWE-77 · Disclosed 2026-05-19

Evince-family PDF `/GoToR` action argument injection leading to user-context code execution

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is a booby-trapped PDF that turns the viewer itself into the launcher

CVE-2026-46529 is an argument/command-injection bug in Linux desktop document viewers derived from the Evince code path ev_spawn(). A malicious PDF can smuggle attacker-controlled values from /GoToR actions into the viewer's command line, then abuse --gtk-module so GTK dlopen() loads attacker code from the same file. Upstream disclosures say Atril is fixed in 1.28.4 and 1.26.3, Xreader in 4.6.4 and 3.6.7, and that Evince and Papers are also affected; distro advisories show backported fixes in Debian and Ubuntu even when the package version stays below the upstream tag.

Technically this is nasty: it is reliable user-context code execution from a single malicious file, with public exploit-building details and no special configuration required on stock installs. But the blast radius is narrower than a server-side RCE because the attacker needs a Linux desktop target using one of these viewers, plus a user who opens the PDF and clicks the embedded link. That keeps it out of *CRITICAL* territory for most enterprises, but it is still solidly *HIGH* for Linux workstation fleets.

"Single-click user-context RCE with public exploit details, but it lives on Linux desktop viewers—not exposed servers."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Build a PDF/ELF polyglot with build_polyglot.py from the GHSA writeup

The published advisory includes a proof path for creating a single file that is both a valid PDF and a valid ELF shared object. The attacker embeds a constructor payload in the ELF and stamps valid PDF structures into the same artifact so the document viewer and the loader both accept it.
Conditions required:
  • Attacker can generate and deliver a malicious PDF attachment or download
  • Target architecture is known or the attacker ships the right build for the victim
Where this breaks in practice:
  • This is not a wormable network path; it starts with social engineering or file delivery
  • Architecture-specific payloads add some attacker prep work
Detection/coverage: Static mail detonation can catch obvious ELF+PDF polyglots, but many scanners will only classify the file as one format. YARA on ELF magic plus embedded %PDF- is useful.
STEP 02

Deliver to a Linux desktop user and get a click on the PDF link

The PDF carries a full-page or convincing link annotation using /A << /S /GoToR ... /D (...) >>. Exploitation requires the user to open the file in a vulnerable viewer and actively click the link inside the document.
Conditions required:
  • Victim uses Evince, Atril, Xreader, or Papers on a vulnerable build
  • Victim opens the file in that viewer rather than a browser sandbox or different renderer
  • Victim clicks the malicious link
Where this breaks in practice:
  • User interaction is mandatory
  • Many enterprises are Windows-heavy, so the exposed population is much smaller than the total fleet
  • Browser-based PDF viewing, sandboxing, or alternate readers break this chain
Detection/coverage: Email gateways and endpoint content inspection can look for /GoToR, suspicious /D fields, and link annotations spanning the full page. User-reporting telemetry matters here.
STEP 03

Abuse ev_spawn() to inject --gtk-module

The vulnerable code builds a command line from attacker-controlled PDF destination fields without proper quoting, then hands it to g_app_info_create_from_commandline(), which reparses it into argv. That turns the injected --gtk-module=/path/to/file into a real startup argument for the spawned viewer instance.
Conditions required:
  • Viewer build includes the vulnerable ev_spawn() path
  • The injected field reaches command construction unchanged
Where this breaks in practice:
  • This is a local application exploit path, not remotely reachable over the network
  • Some downstream builds may have already backported the fix without matching upstream version numbers
Detection/coverage: EDR can catch suspicious child process command lines containing --gtk-module, especially when pointed at user-writable paths like /tmp, ~/Downloads, or the opened PDF itself.
STEP 04

GTK dlopen() executes the embedded constructor payload

When GTK initializes, it loads the attacker-supplied module path and executes any constructor code inside it before normal document handling matters. The result is arbitrary code execution as the logged-in user, typically enough for data theft, credential access, persistence, or a second-stage implant.
Conditions required:
  • GTK honors the supplied module path
  • The payload library is readable and valid for the victim architecture
Where this breaks in practice:
  • Execution is in user context, not instant root
  • Application confinement such as AppArmor, SELinux policy, or aggressive EDR policy can reduce post-exploit options
Detection/coverage: Look for dlopen of unusual modules from user-writable locations, module loads from PDFs, or immediate shell/network activity from the viewer process.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo CISA KEV listing was found, and the searched primary sources do not claim active exploitation. This looks like public exploitability, not confirmed campaign use.
PoC availabilityPublic exploit-building details are available in the GitHub advisory and the follow-up oss-sec thread, including a build_polyglot.py workflow and a --gtk-module abuse path.
EPSSNo authoritative FIRST EPSS entry was located in the searched sources, so treat predictive exploit probability as unknown, not low.
KEV statusNot listed in CISA KEV per the user-supplied intel block; no contradictory source was found.
Published severity dataA GitHub security advisory for Atril published on 2026-05-21 labels the issue High 8.4 with CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N. I am not treating that as the noisgate comparison baseline because NVD/MITRE lacked a normalized record when requested.
Affected productsEvince, Atril, Xreader, and Papers are affected through the shared vulnerable code path in ev_spawn(). This is primarily a Linux desktop exposure, not a server exposure.
Fixed versionsUpstream disclosures say Atril 1.28.4 and 1.26.3, Xreader 4.6.4 and 3.6.7, and Evince has a fix upstream while Debian/Ubuntu shipped backports such as Debian bookworm 43.1-2+deb12u1, Debian trixie 48.1-3+deb13u1, Ubuntu noble 46.3.1-0ubuntu1.1, and Ubuntu jammy 42.3-0ubuntu3.2.
Version ambiguity to watchPrimary sources disagree on the raw upstream Evince fixed tag: oss-sec says 48.2, while the GHSA page lists 48.4. For enterprise verification, trust the distro advisory package version rather than upstream semver alone.
Exposure telemetryInternet scan data is largely not applicable here: these viewers are endpoint applications, not listening services. Shodan/Censys-style counts would understate risk because the exposure surface is local user workflow, especially email and downloads.
Disclosure and creditInitial public coordination appeared on 2026-05-19 via oss-sec; the GitHub advisory credits N1et as finder and shows the write-up published on 2026-05-21.
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to HIGH (7.8/10)

The decisive factor is that exploitation yields real user-context code execution on stock Linux desktops with public weaponization details, so this is not a harmless parser crash. It stays below *CRITICAL* because the attack chain still depends on a narrower population of Linux desktop viewers and explicit user interaction inside the document.

HIGH Exploit mechanics and technical impact
MEDIUM Population-level enterprise exposure

Why this verdict

  • Real code execution, not just a viewer crash: the --gtk-module path reaches dlopen() and runs attacker code as the logged-in user.
  • Public exploit path lowers attacker effort: the advisory and follow-up discussion disclose a practical polyglot PDF/ELF technique, shrinking the gap between bug and weaponization.
  • User interaction is the main downward pressure: the victim must open the PDF and click the embedded link, which makes this more phishing-driven than self-propagating.
  • Attacker position is effectively pre-initial-access content delivery: there is no unauthenticated network service to hit, so exposure is gated by email/download/USB workflows.
  • Reachable population is narrower than generic endpoint bugs: only Linux desktops running Evince-family viewers are meaningfully exposed, which is usually a minority slice of a 10,000-host enterprise.

Why not higher?

This is not an internet-facing service flaw and it is not zero-click. Every prerequisite narrows the real attack set: Linux desktop target, vulnerable viewer, successful file delivery, and a user click inside the PDF. Those compounding frictions keep it out of *CRITICAL* even though the impact after exploitation is serious.

Why not lower?

Downgrading to *MEDIUM* would ignore the quality of the exploit path. The bug is reliable enough for single-file user-context RCE on stock installs, and public write-ups remove a lot of engineering work for attackers. For Linux workstation fleets, that is above backlog-only risk.

05 · Compensating Control

What to do — in priority order.

  1. Force safer PDF handling paths — Route untrusted PDFs to a browser sandbox, remote viewer, or hardened alternate renderer instead of Evince-family local desktop apps. For a *HIGH* verdict, deploy this control where feasible within 30 days if patch rollout will lag.
  2. Block risky PDF features at ingress — Tune mail and web security controls to quarantine PDFs containing /GoToR, suspicious /D action fields, or obvious PDF/ELF polyglot indicators. This cuts the easiest delivery channel and should be in place within 30 days.
  3. Hunt for --gtk-module abuse — Add endpoint detections for document viewers spawning with --gtk-module or loading modules from user-writable paths such as /tmp, /var/tmp, ~/Downloads, or the opened file itself. Detection engineering is a viable temporary risk reducer within 30 days.
  4. Constrain desktop execution — Use AppArmor/SELinux, application control, or EDR prevention to block document viewers from loading arbitrary shared objects out of user-writable locations. This does not fix the bug, but it meaningfully reduces post-click reliability within 30 days.
What doesn't work
  • Perimeter vulnerability scanning does not help much; these are local desktop viewers, not remotely fingerprintable services.
  • Network segmentation is weak compensation here; the trigger is a local file open and click, not east-west network reachability.
  • Generic PDF attachment blocking alone is too blunt and often bypassed by downloads, chat attachments, or shared storage links.
06 · Verification

Crowdsourced verification payload.

Run this on the target Linux host as a normal user; root is not required. Invoke it with bash cve-2026-46529-check.sh. It checks installed package versions against known Debian/Ubuntu fixed builds where available, then falls back to upstream semver checks for Atril/Xreader; unsupported package layouts return UNKNOWN.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# CVE-2026-46529 verification helper
# Checks common Linux package installations of evince/atril/xreader/papers.
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -u

status="PATCHED"
notes=()
found_any=0

have() { command -v "$1" >/dev/null 2>&1; }

add_note() {
  notes+=("$1")
}

mark_vuln() {
  status="VULNERABLE"
  add_note "$1"
}

mark_unknown() {
  if [ "$status" != "VULNERABLE" ]; then
    status="UNKNOWN"
  fi
  add_note "$1"
}

check_dpkg_pkg() {
  local pkg="$1"
  dpkg-query -W -f='${Version}' "$pkg" 2>/dev/null || return 1
}

check_rpm_pkg() {
  local pkg="$1"
  rpm -q --qf '%{VERSION}-%{RELEASE}' "$pkg" 2>/dev/null || return 1
}

check_dpkg_threshold() {
  local pkg="$1" installed="$2" fixed="$3"
  if dpkg --compare-versions "$installed" ge "$fixed"; then
    add_note "$pkg $installed >= fixed $fixed"
  else
    mark_vuln "$pkg $installed < fixed $fixed"
  fi
}

check_semver_fallback() {
  local name="$1" version="$2" fixed1="$3" fixed2="${4:-}"
  if printf '%s\n%s\n' "$fixed1" "$version" | sort -V -C; then
    add_note "$name $version >= upstream fixed $fixed1"
    return 0
  fi
  if [ -n "$fixed2" ] && printf '%s\n%s\n' "$fixed2" "$version" | sort -V -C; then
    add_note "$name $version >= upstream fixed $fixed2"
    return 0
  fi
  mark_vuln "$name $version is below upstream fixed versions $fixed1${fixed2:+ / $fixed2}"
}

# Debian/Ubuntu path
if have dpkg-query && have dpkg; then
  if [ -r /etc/os-release ]; then
    . /etc/os-release
  fi
  os_id="${ID:-unknown}"
  version_id="${VERSION_ID:-unknown}"

  # evince on Debian/Ubuntu with distro-fixed package versions
  ev_ver=$(check_dpkg_pkg evince) || ev_ver=""
  if [ -n "$ev_ver" ]; then
    found_any=1
    case "$os_id:$version_id" in
      ubuntu:22.04) check_dpkg_threshold evince "$ev_ver" '42.3-0ubuntu3.2' ;;
      ubuntu:24.04) check_dpkg_threshold evince "$ev_ver" '46.3.1-0ubuntu1.1' ;;
      ubuntu:25.10) check_dpkg_threshold evince "$ev_ver" '48.1-3ubuntu2.1' ;;
      ubuntu:26.04) check_dpkg_threshold evince "$ev_ver" '49~alpha-2ubuntu2.1' ;;
      debian:11) check_dpkg_threshold evince "$ev_ver" '3.38.2-1+deb11u1' ;;
      debian:12) check_dpkg_threshold evince "$ev_ver" '43.1-2+deb12u1' ;;
      debian:13) check_dpkg_threshold evince "$ev_ver" '48.1-3+deb13u1' ;;
      *) mark_unknown "evince $ev_ver installed, but no authoritative distro threshold encoded for $os_id $version_id" ;;
    esac
  fi

  # atril: upstream fixed versions known; distro backports vary widely
  atril_ver=$(check_dpkg_pkg atril) || atril_ver=""
  if [ -n "$atril_ver" ]; then
    found_any=1
    upstream_guess=$(printf '%s' "$atril_ver" | sed 's/+.*$//' | sed 's/-.*$//')
    if [[ "$upstream_guess" =~ ^[0-9]+(\.[0-9]+)+$ ]]; then
      check_semver_fallback atril "$upstream_guess" '1.28.4' '1.26.3'
    else
      mark_unknown "atril $atril_ver installed; package version is distro-specific and cannot be safely mapped"
    fi
  fi

  papers_ver=$(check_dpkg_pkg papers) || papers_ver=""
  if [ -n "$papers_ver" ]; then
    found_any=1
    case "$os_id:$version_id" in
      ubuntu:25.10) check_dpkg_threshold papers "$papers_ver" '48.0-1ubuntu1.25.10.4' ;;
      ubuntu:26.04) check_dpkg_threshold papers "$papers_ver" '50.1-0ubuntu1.1' ;;
      *) mark_unknown "papers $papers_ver installed; searched sources did not provide a reliable fixed threshold for $os_id $version_id" ;;
    esac
  fi
fi

# RPM fallback for common package names; authoritative thresholds not encoded except via upstream semver best effort
if [ "$found_any" -eq 0 ] && have rpm; then
  for pkg in evince atril xreader papers; do
    ver=$(check_rpm_pkg "$pkg") || ver=""
    [ -z "$ver" ] && continue
    found_any=1
    base=$(printf '%s' "$ver" | sed 's/-.*$//')
    case "$pkg" in
      atril) check_semver_fallback atril "$base" '1.28.4' '1.26.3' ;;
      xreader) check_semver_fallback xreader "$base" '4.6.4' '3.6.7' ;;
      evince) mark_unknown "evince $ver installed via RPM; distro backport policy varies, confirm against vendor advisory" ;;
      papers) mark_unknown "papers $ver installed via RPM; confirm against distro advisory" ;;
    esac
  done
fi

if [ "$found_any" -eq 0 ]; then
  echo "UNKNOWN"
  echo "No supported package installation of evince/atril/xreader/papers was detected."
  exit 2
fi

echo "$status"
printf '%s\n' "${notes[@]}"

case "$status" in
  PATCHED) exit 0 ;;
  VULNERABLE) exit 1 ;;
  *) exit 2 ;;
esac
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning, identify Linux workstation pools that ship Evince, Atril, Xreader, or Papers, then push two workstreams in parallel: temporary controls on untrusted PDF handling and the actual package updates. Because this is a *HIGH* verdict, the noisgate mitigation SLA is within 30 days for compensating controls such as safer PDF routing, mail/web filtering for risky PDF actions, and EDR detections for --gtk-module abuse; the noisgate remediation SLA is within 180 days for the vendor or distro patch, using distro-fixed package builds rather than upstream version strings alone.

Sources

  1. GitHub Security Advisory GHSA-vgv2-m826-8f6f
  2. oss-sec initial disclosure
  3. oss-sec follow-up with full report links
  4. Debian Security Tracker
  5. Debian Security Advisory DSA-6286-1
  6. Debian LTS Advisory DLA-4596-1
  7. Ubuntu Security Notice USN-8295-1
  8. Ubuntu CVE page
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.