← Back to Feed CACHED · 2026-06-30 01:53:10 · CACHE_KEY CVE-2025-3155
CVE-2025-3155 · CWE-601 · Disclosed 2025-04-03

Yelp (GNOME Help) arbitrary file read via Mallard XInclude + SVG JavaScript

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

A booby-trapped help file that turns the GNOME helper into a quiet file-exfil drone

Yelp is the GNOME desktop's built-in help viewer. It renders Mallard XML .page documents, and the renderer happily honors XInclude directives plus inline SVG with JavaScript. An attacker who can convince a user to open a crafted ghelp:// URL or a .page file gets the helper to pull arbitrary local files (anything the user's UID can read — ~/.ssh/id_rsa, ~/.config, /etc/passwd) into the rendered DOM, then ship them off via embedded JS to an attacker-controlled endpoint. Affected: Yelp ≤ 42.1 and corresponding yelp-xsl ≤ 42.1, shipped by default on Ubuntu 22.04/24.04, Fedora, Debian, RHEL 9, openSUSE, and other GNOME-based distros. Fixed in Yelp 42.3 / yelp-xsl 42.3 (June 12, 2025); distros have backports (RHSA-2025:4505, Fedora FEDORA-2025-e788608959, USN updates).

Vendor labeled this HIGH (7.4) based on CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N — confidentiality high, scope changed, user interaction required. The label is *defensible* for a Linux desktop where ssh keys, GPG keyring, and cloud credential files sit in $HOME. But for an enterprise fleet, the population that matters is narrow: only Linux desktops/workstations with GNOME and Yelp installed, where a human clicks a .page payload. Most enterprise Linux is headless server, where Yelp isn't installed or invoked. Net: vendor severity overstates the *fleet* risk for a typical defender; for a developer-heavy Linux-desktop shop it's about right.

"Vendor calls it HIGH; in reality it's a UI-required help-file phish that leaks files on Linux desktops only. Patch on the desktop track."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Craft a malicious Mallard .page help file

Attacker writes a Mallard XML document containing an <xi:include parse="text" href="file:///home/victim/.ssh/id_rsa"/> directive and an inline SVG with a <script> block. The PoC by parrot409 (GitHub gist) is the canonical weaponization. Payload is a single self-contained .page file.
Conditions required:
  • Attacker has authoring capability for the target's help-file delivery vector
Where this breaks in practice:
  • Trivial — public PoC available
Detection/coverage: AV/EDR signatures for Mallard .page with embedded <script> are minimal; YARA rules would have to be hand-written
STEP 02

Deliver via ghelp:// URI or attachment

Attacker hosts the .page file or wraps it in a ghelp:///help: URI delivered via phishing email, IM, or a malicious web page. On GNOME, clicking a ghelp:// link launches Yelp. Alternatively, the file is dropped to disk and a .desktop file or filemanager double-click invokes Yelp.
Conditions required:
  • Target uses a GNOME desktop with default URI handlers
  • Email/IM/browser delivers the link or attachment without stripping
Where this breaks in practice:
  • Most enterprise email gateways do not block .page or ghelp:// because they're obscure — this is friction *for the defender*, not the attacker
  • Modern browsers prompt before handing off custom schemes
Detection/coverage: Proofpoint/Mimecast/Microsoft Defender for O365 do not have default rules for ghelp://; you'll need a custom block
STEP 03

User opens the help file

Victim double-clicks or accepts the URI handoff. Yelp launches and begins parsing the Mallard XML. UI:R is satisfied here — no zero-click path.
Conditions required:
  • User is logged into a GNOME session
  • User opts to open the file
Where this breaks in practice:
  • Requires social engineering — .page is not a file type most users recognize as a help document, which paradoxically makes phishing pretexts harder
Detection/coverage: Process creation telemetry (sysmon-for-Linux, auditd, Falco) will show yelp spawning with a suspicious argv
STEP 04

XInclude reads sensitive local files

Yelp's XSLT pipeline (yelp-xsl) honors the XInclude directive and pulls the target file's contents into the rendered output as text. There is no sandbox — the read happens with the user's UID. Any file readable by that UID is in scope: SSH keys, browser cookie DBs, kube configs, AWS creds, GPG keyrings.
Conditions required:
  • Sensitive files exist in paths readable by the user
  • AppArmor/SELinux confinement for yelp is not enforced (default on most distros)
Where this breaks in practice:
  • Ubuntu's stock AppArmor profile for yelp is *not* enforced
  • On hardened distros (Qubes, Tails) the helper runs in a constrained context
Detection/coverage: auditd EXECVE/PATH watch on ~/.ssh/id_* opened by yelp is a high-fidelity signal
STEP 05

SVG JavaScript exfiltrates over HTTPS

The inline SVG <script> reads the now-included DOM text and POSTs it to an attacker server via fetch() or by setting img.src to a logging endpoint. WebKitGTK (Yelp's renderer) honors outbound network requests by default; there is no CSP applied to local Mallard content.
Conditions required:
  • Outbound HTTPS from the workstation to attacker infra is permitted
Where this breaks in practice:
  • Egress-filtered networks with a default-deny proxy block the POST
  • Zscaler/Netskope SWG with TLS inspection can flag the unusual outbound from WebKitWebProcess
Detection/coverage: SWG/EDR network telemetry tying WebKitWebProcess (child of yelp) to an unknown destination is the cleanest IOC
03 · Compensating Control

1
Disable the ghelp:// and help: URI handlers on managed Linux desktops — Remove or override /usr/share/applications/yelp.desktop's MimeType entries that register Yelp as the handler for x-scheme-handler/ghelp and x-scheme-handler/help, and unregister Yelp from application/vnd.gnome.mallard+xml. Ship via Ansible/Puppet/Salt within the MEDIUM mitigation window — there is no formal SLA for MEDIUM, so align with your next desktop config push (target ≤ 30 days for the developer cohort).
2
Block .page attachments and ghelp:// URIs at the email gateway — Add .page to the disallowed-attachment list in Proofpoint/Mimecast/Defender for O365, and have your SWG (Zscaler/Netskope/Cloudflare Gateway) refuse rewrites of ghelp: schemes. Roll out within the next mail-policy cycle.
3
Apply the distro backport immediately on developer/SRE workstations — RHEL: dnf update yelp yelp-xsl (RHSA-2025:4505). Ubuntu/Debian: apt-get install --only-upgrade yelp yelp-xsl. Fedora: dnf update --advisory FEDORA-2025-e788608959. Prioritize hosts where users hold cloud or SSH credentials in $HOME.
4
Enforce AppArmor/SELinux confinement on yelp — Ubuntu ships an unenforced AppArmor profile for yelp; set it to enforce (aa-enforce /etc/apparmor.d/usr.bin.yelp) and extend it to deny reads of ~/.ssh/, ~/.aws/, ~/.kube/, ~/.gnupg/. Reduces blast radius even if patch is missed.
5
Hunt for retroactive exploitation — auditd query: any yelp process that open()ed files under ~/.ssh, ~/.aws, ~/.kube, or ~/.config/gcloud in the last 90 days. Pair with EDR network telemetry on WebKitWebProcess child of yelp.
What doesn't work
  • Network segmentation — Yelp runs locally on the user's workstation; the exfil is outbound HTTPS from a trusted host, not lateral traffic.
  • Disabling JavaScript browser-wide — Yelp uses WebKitGTK as a library, not the browser; browser settings don't affect it.
  • Removing Yelp entirely — packagers often pull it back in as a dependency of gnome-control-center or gnome-shell. Mark/hold instead, or replace the binary with a stub.
  • MFA on the workstation login — does nothing once the user is logged in and clicks the help file.
04 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo confirmed campaigns. Not on CISA KEV. No exploitation telemetry from Mandiant, CrowdStrike, or Red Canary as of June 2026.
Proof-of-conceptPublic PoC by parrot409 at gist.github.com/parrot409; writeup by Michael Catanzaro (GNOME/Red Hat) on the GNOME blog.
EPSS0.10598 (~10.6%) — elevated for a desktop bug but reflects PoC availability, not active exploitation
KEVNot listed as of 2026-06-30
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N — UI:R is the load-bearing constraint; no integrity/availability impact
Affected versionsYelp ≤ 42.1 and yelp-xsl ≤ 42.1 on GNOME desktops (Ubuntu 22.04/24.04, Fedora 41/42, RHEL 9/10, Debian 12, openSUSE Leap/Tumbleweed)
Fixed versionsYelp 42.3 / yelp-xsl 42.3 (upstream, 2025-06-12). Backports: RHEL 9 via RHSA-2025:4505, Fedora 42 FEDORA-2025-e788608959, Ubuntu USN, Debian DSA
Exposure dataNot externally scannable — Yelp is a desktop binary, not a network service. Shodan/Censys/GreyNoise data is not applicable. Use internal MDM/Intune/JAMF/SCCM/Ansible inventory.
Disclosed2025-04-03 (NVD); originally surfaced 2025-03-18; fix landed 2025-06-12
Reporterparrot409 (research credit); coordinated with GNOME security via Michael Catanzaro

Sources.

  1. GitHub Advisory GHSA-h7mx-548v-cr9r
  2. Michael Catanzaro (GNOME) write-up
  3. PoC by parrot409
  4. Red Hat RHSA-2025:4505
  5. Fedora 42 yelp-xsl advisory
  6. Seqrite analysis
  7. Wiz vulnerability page
  8. CVE Details
05 · The Call

Final Verdict
↓ DOWNGRADED to MEDIUM (5.4/10)

Why this verdict

  • User interaction required (UI:R) — no zero-click vector. The chain dies at the inbox if your secure email gateway flags unknown attachments or your browser blocks custom-scheme handoff.
  • Affected population is narrow — Yelp is a desktop-only package; headless Linux servers (the bulk of most enterprise Linux estates) don't install it. EPSS 0.106 reflects PoC presence, not active campaigns.
  • Blast radius is single-user, single-host — confidentiality only, no integrity or availability impact, no code-execution primitive beyond reading what the user's UID already can. No privilege escalation, no persistence primitive included.
  • Role multiplier: developer/SRE workstation — chain succeeds, blast radius is *significant* (SSH keys, kubeconfigs, ~/.aws/credentials, GPG signing keys → potential pivot to production). For shops where Linux devs hold prod credentials in $HOME, this floats to the top of the MEDIUM bucket.
  • Role multiplier: general office workstation (Linux) — chain succeeds, blast radius is the user's documents and browser cookies. Local nuisance, not a fleet event.
  • Role multiplier: server / headless host — Yelp typically not installed; chain does not run. Zero impact for the dominant Linux deployment role.
  • No KEV listing, no observed campaigns — 14 months post-disclosure with a public PoC and no exploitation telemetry argues against a crash patch cycle.

Why not higher?

HIGH would require either active exploitation (none), a wider attack surface (server-side or zero-click — neither applies), or a credible chain to code execution and lateral movement (this is read-only file exfil within one UID). For Linux-developer-heavy shops where stolen SSH/cloud keys mean prod compromise, treat this as the *top* of MEDIUM and accelerate, but it does not meet the HIGH floor.

Why not lower?

LOW is wrong because the file-read primitive trivially captures secrets-in-home-directory (~/.ssh, ~/.aws, ~/.kube) on developer hosts, and PoC weaponization is public. The role multiplier on a dev workstation is too high to relegate to backlog hygiene.

06 · Verification

Crowdsourced verification payload.

Run on each Linux desktop/workstation as a non-root user (root not required to read package versions). Invoke as bash check-cve-2025-3155.sh; exits 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN/NOT_INSTALLED. Push via Ansible/Salt and aggregate exit codes.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# noisgate verification: CVE-2025-3155 (Yelp arbitrary file read)
# Patched in upstream yelp 42.3 / yelp-xsl 42.3 (or distro-backported equivalents).
set -u

VULN=1; PATCHED=0; UNKNOWN=2

# Step 1: is Yelp even installed?
if ! command -v yelp >/dev/null 2>&1; then
  # Could still be installed without being on PATH; check pkg DB
  if ! (dpkg -s yelp >/dev/null 2>&1 || rpm -q yelp >/dev/null 2>&1); then
    echo "NOT_INSTALLED (yelp absent — not vulnerable)"
    exit $PATCHED
  fi
fi

# Step 2: read installed version per package manager
get_ver() {
  local pkg="$1"
  if command -v dpkg-query >/dev/null 2>&1; then
    dpkg-query -W -f='${Version}\n' "$pkg" 2>/dev/null | head -n1
  elif command -v rpm >/dev/null 2>&1; then
    rpm -q --qf '%{VERSION}-%{RELEASE}\n' "$pkg" 2>/dev/null | head -n1
  fi
}

YELP_VER=$(get_ver yelp)
XSL_VER=$(get_ver yelp-xsl)

if [ -z "$YELP_VER" ] && [ -z "$XSL_VER" ]; then
  echo "UNKNOWN (could not determine yelp / yelp-xsl version)"
  exit $UNKNOWN
fi

# Step 3: compare. Upstream fix is 42.3. Distro backports advertise the same
# upstream version OR carry a patched suffix (e.g. RHEL: 42.1-2.el9_5).
# Strategy: if upstream >= 42.3, PATCHED. If upstream < 42.3, check for known
# backport release markers from RHEL/Ubuntu/Fedora advisories.

ver_ge() {
  # returns 0 if $1 >= $2 (semver-ish)
  [ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -n1)" = "$2" ]
}

check_one() {
  local name="$1" ver="$2"
  [ -z "$ver" ] && return 0   # not installed, skip
  local base=${ver%%-*}
  if ver_ge "$base" "42.3"; then
    echo "  $name $ver  -> upstream patched"
    return 0
  fi
  # Backport detection: distro advisories
  case "$ver" in
    *el9_5*|*el9_6*|*el10*) echo "  $name $ver  -> RHEL backport (RHSA-2025:4505)"; return 0;;
    *ubuntu*22.04*|*ubuntu*24.04*) 
      # Ubuntu USN-published versions carry +deb/ubuntu suffix; manual sub-check needed.
      echo "  $name $ver  -> AMBIGUOUS Ubuntu version, verify against USN"; return 2;;
    *fc42*|*fc41*) echo "  $name $ver  -> Fedora backport (FEDORA-2025-e788608959)"; return 0;;
  esac
  echo "  $name $ver  -> < 42.3 and no backport marker"
  return 1
}

rc1=0; rc2=0
check_one yelp "$YELP_VER"; rc1=$?
check_one yelp-xsl "$XSL_VER"; rc2=$?

if [ $rc1 -eq 1 ] || [ $rc2 -eq 1 ]; then
  echo "VULNERABLE (CVE-2025-3155): upgrade to yelp/yelp-xsl >= 42.3 or distro backport"
  exit $VULN
fi

if [ $rc1 -eq 2 ] || [ $rc2 -eq 2 ]; then
  echo "UNKNOWN: ambiguous version, cross-check against vendor advisory"
  exit $UNKNOWN
fi

echo "PATCHED (CVE-2025-3155)"
exit $PATCHED