A length-math bug deep inside glib's URI escaper that only matters if you can already feed it gigabyte-scale strings locally
When g_escape_uri_string() walks a candidate string and counts how many bytes the percent-encoded output will need, the running total can wrap a 32-bit size_t on platforms where the worst-case expansion (×3 per byte) of a sufficiently large input overflows the allocation size calculation. The allocator then returns a buffer smaller than the encoder thinks it asked for, and the encoder writes past the end. This affects glib (glib2) before 2.86.3, with distro backports landing as 2.66.8-1+deb11u7 (Bullseye), 2.74.6-2+deb12u8 (Bookworm), 2.84.4-3~deb13u2 (Trixie), and the upstream fix in 2.86.3-1. The flaw lives in GNOME GitLab issue #3827.
Vendor severity is HIGH (7.7) under AV:L/AC:L/PR:N/UI:N/C:N/I:H/A:H — local, no privileges, integrity + availability impact. The score is technically defensible because corruption of a heap chunk can hand an attacker code execution in the calling process, but in practice nearly every real caller of g_escape_uri_string() (GIO, GVfs, desktop URI handling, GNotification, GApplication) is invoked with attacker-controllable strings that are kilobytes, not the hundreds-of-megabytes required to wrap the length math. The realistic severity sits in MEDIUM territory: it is a latent corruption primitive looking for a caller that will hand it a giant string, and very few do.
4 steps from start to impact.
Reach a glib caller with attacker-influenced URI input
g_escape_uri_string() (or g_uri_escape_string() / g_uri_escape_bytes() which share the helper). Typical surfaces are GVfs URI handlers, GNotification action URIs, desktop file Exec=/URL= parsing, and applications that build URIs from filenames or metadata. The input must be locally delivered — opening a file, mounting a share, clicking a .desktop shim, or passing a CLI argument.- Local code execution as some user already present
- A glib-linked desktop or service process that escapes attacker-supplied content
- Server fleets generally do not run glib URI escaping on tainted, unbounded input
- Headless Linux servers ship glib but rarely link callers that escape user-shaped URIs
Deliver a string large enough to overflow the length calculation
%XX) exceeds the type used for the allocation length. In practice that means hundreds of megabytes to gigabytes of input on 32-bit size_t paths, or astronomically large inputs on 64-bit. The attacker must find a caller that does not pre-bound input length.- A caller that passes unbounded-length strings to the escaper
- Enough memory to materialize the malicious string in the victim process
- Most desktop callers cap URIs at OS PATH limits or a few KB
- GLib's own URI parsers enforce sanity limits before reaching the escaper in most paths
- On 64-bit Linux (the dominant target) wrapping requires ~6 GB of input — implausible in benign flows and noisy in malicious ones
Shape heap layout to land the overflow on something useful
- A heap grooming primitive in the same process
- Knowledge of allocator layout
- glibc 2.32+ tcache hardening, safe-linking, and pointer mangling raise the bar
- ASLR + PIE on every modern distro break naive offsets
- The corrupting bytes are themselves URI-escape characters — limited control of overwrite content
Convert corruption into code execution in caller's context
- Heap grooming succeeded
- Target process not sandboxed by snap/flatpak/AppArmor confinement
- Most glib URI consumers run as the unprivileged user the attacker already controls
- Flatpak/snap sandboxing contains the blast radius
- No known setuid caller of
g_escape_uri_string()with attacker-controlled length
The supporting signals.
| In-the-wild exploitation | None observed. No public campaign, no honeypot hits, not on CISA KEV. |
|---|---|
| Public PoC | No weaponized PoC published. GNOME GitLab issue #3827 describes the math bug; reproducers are conceptual (feed multi-GB string). |
| EPSS | 0.00306 (~0.3%) — well below the 1% triage threshold. |
| CISA KEV | Not listed as of 2026-06-30. |
| CVSS vector | CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H — AV:L is the dominant friction: no network reach, attacker must already be local. |
| Affected versions | glib < 2.86.3 upstream. Distro vulnerable ranges: Debian 11 < 2.66.8-1+deb11u7, Debian 12 < 2.74.6-2+deb12u8, RHEL 8 < glib2-2.56.4-168.el8_10, Amazon Linux per ALAS, IBM Netezza Appliance affected. |
| Fixed versions | Upstream 2.86.3; Debian backports 2.66.8-1+deb11u7 / 2.74.6-2+deb12u8 / 2.84.4-3~deb13u2; RHEL glib2-2.56.4-168.el8_10 (RHSA-2026:0975); Mageia MGASA-2026-0023; MIRACLE LINUX advisory issued. |
| Scanner exposure | Tenable plugin 276961, VulDB 333600, EUVD-2025-199720, OSV/GHSA pickup pending. No internet-facing surface for GreyNoise/Shodan/Censys to instrument. |
| Disclosure | 2025-11-26, via GNOME GitLab issue #3827, tracked Debian bug #1121488. |
| Reporter | Reported upstream to GNOME glib maintainers; no public researcher attribution in advisories. |
noisgate verdict.
Downgraded one bucket because the AV:L attack vector requires the attacker already to be on the host, and the integer-overflow trigger requires multi-gigabyte input that no realistic glib URI caller will hand the function. Blast radius is limited to the caller's user context — there is no setuid amplifier and no high-value-role variant of this bug.
Why this verdict
- AV:L kills network reach. The vendor's own vector confirms no remote path; the attacker must already be on the box, which collapses to a privilege-escalation primitive at best.
- No setuid / high-value-role caller.
g_escape_uri_string()is invoked from desktop URI plumbing (GVfs, GIO, GNotification), GApplication, and userland apps — none of these are canonically high-value roles (no DC, hypervisor, IdP, PAM, backup, kernel agent runs URI escaping on attacker input). Role multiplier does not apply. - Role multiplier — server fleet: glib is present on virtually every Linux server but the vulnerable function is rarely on the data path for server workloads (web servers, databases, K8s nodes don't escape attacker URIs through this helper). Chain does not succeed in the high-value role.
- Trigger size is implausible. Wrapping the length math on 64-bit requires ~6 GB of attacker-shaped input in a single allocation — instantly OOM-killed or detected by EDR on real fleets.
- EPSS 0.3% + no KEV + no PoC — there is no observed adversary interest and no weaponization to chase.
Why not higher?
HIGH would require either a remote vector (this is AV:L), a high-value-role caller (none documented), or a credible exploitation chain. The integer overflow exists but the triggering caller does not exist in the high-blast-radius components defenders care about.
Why not lower?
LOW would imply no realistic impact, but the bug IS a memory-corruption primitive in a near-ubiquitous library. If a future caller emerges that hands it large attacker-controlled input — or a 32-bit embedded build is found — the calculus changes. MEDIUM keeps it on the 365-day remediation clock rather than ignored.
What to do — in priority order.
- Patch glib2 via vendor channel in next monthly window — RHEL:
dnf upgrade glib2to ≥ glib2-2.56.4-168.el8_10 (RHSA-2026:0975). Debian/Ubuntu:apt upgrade libglib2.0-0to the per-release fixed version. Container base images: rebuild from updated upstream. No mitigation SLA at MEDIUM — fold into the noisgate remediation SLA of ≤ 365 days, but realistically ride the next maintenance window. - Inventory which processes actually link vulnerable glib AND escape untrusted URIs — Run
lsof | grep libglib-2.0on representative hosts and cross-reference against your application inventory. Most servers will show glib loaded bysystemd,NetworkManager,polkitd— none of which feed attacker-controlled multi-GB strings to the escaper. Use the result to deprioritize hosts where the caller is not exposed. - Tighten desktop/Flatpak sandboxing where applicable — On user workstations, ensure Flatpak/snap confinement is enforced (
flatpak override --reset) so a successful exploit stays inside the application sandbox rather than reaching the user's home dir. Useful as defense-in-depth, not a mitigation per se. - Add memory-allocation anomaly alerting for desktop processes — Trigger telemetry when GUI processes attempt single allocations > 1 GB or hit OOM-kill — the only realistic trigger path produces conspicuous memory pressure that a tuned EDR or auditd rule will surface.
- WAF / network filtering — vulnerability is AV:L; no network packets traverse the vulnerable code.
- Disabling URI handlers in browsers — the bug is in glib's escaper, not in URL parsing; browsers ship their own URL libraries.
- Running as non-root — the bug already executes in user context; root is not the bar.
Crowdsourced verification payload.
Run on each Linux target host as any user (no privileges required). Example: bash check-cve-2025-13601.sh. Detects vulnerable glib2 package version across RHEL/Debian/Ubuntu/Amazon Linux families and prints VULNERABLE / PATCHED / UNKNOWN.
#!/usr/bin/env bash
# CVE-2025-13601 — glib g_escape_uri_string integer overflow
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
verdict() { echo "$1"; exit "$2"; }
ver_ge() {
# returns 0 if $1 >= $2 using dpkg/rpm semantics where available
if command -v dpkg >/dev/null 2>&1; then
dpkg --compare-versions "$1" ge "$2"
return $?
elif command -v rpmdev-vercmp >/dev/null 2>&1; then
rpmdev-vercmp "$1" "$2" >/dev/null 2>&1
[ $? -eq 11 ] && return 1 || return 0
else
# crude fallback
[ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -1)" = "$2" ] && return 0 || return 1
fi
}
if command -v rpm >/dev/null 2>&1; then
PKG=$(rpm -q --qf '%{VERSION}-%{RELEASE}\n' glib2 2>/dev/null | head -1)
[ -z "$PKG" ] && verdict "UNKNOWN: glib2 not installed via rpm" 2
# RHEL 8 fix: 2.56.4-168.el8_10
if echo "$PKG" | grep -q 'el8'; then
ver_ge "$PKG" "2.56.4-168.el8_10" && verdict "PATCHED: glib2-$PKG" 0
verdict "VULNERABLE: glib2-$PKG (need >= 2.56.4-168.el8_10)" 1
fi
# Generic: anything upstream >= 2.86.3 is fixed
UPSTREAM=$(echo "$PKG" | cut -d- -f1)
ver_ge "$UPSTREAM" "2.86.3" && verdict "PATCHED: glib2-$PKG" 0
verdict "VULNERABLE: glib2-$PKG (need backport or >= 2.86.3 upstream)" 1
fi
if command -v dpkg >/dev/null 2>&1; then
PKG=$(dpkg-query -W -f='${Version}\n' libglib2.0-0 libglib2.0-0t64 2>/dev/null | head -1)
[ -z "$PKG" ] && verdict "UNKNOWN: libglib2.0-0 not installed" 2
CODENAME=$( ( . /etc/os-release; echo "$VERSION_CODENAME" ) 2>/dev/null )
case "$CODENAME" in
bullseye) FIX="2.66.8-1+deb11u7" ;;
bookworm) FIX="2.74.6-2+deb12u8" ;;
trixie) FIX="2.84.4-3~deb13u2" ;;
*) FIX="2.86.3" ;;
esac
ver_ge "$PKG" "$FIX" && verdict "PATCHED: libglib2.0-0 $PKG (>= $FIX)" 0
verdict "VULNERABLE: libglib2.0-0 $PKG (need >= $FIX)" 1
fi
verdict "UNKNOWN: no supported package manager detected" 2
If you remember one thing.
dnf / apt updates). The noisgate mitigation SLA does not apply at MEDIUM; the AV:L vector, 6-GB-input trigger, EPSS 0.3%, and absence of KEV or PoC make this an opportunistic-patch case, not a fire drill. Use this week to confirm your SCA scanners (Trivy / Tenable plugin 276961 / OSV-Scanner) are flagging vulnerable glib2 across your image base so you do not inherit it via stale container layers.Sources
- Debian Security Tracker — CVE-2025-13601
- Debian bug #1121488 — glib#3827 / CVE-2025-13601
- VulDB 333600 — CVE-2025-13601
- Red Hat RHSA-2026:0975 — glib2 security update
- Amazon Linux ALAS — CVE-2025-13601
- IBM Security Bulletin — glib2 affects Netezza Appliance
- Mageia MGASA-2026-0023 — glib2.0 update
- MIRACLE LINUX — glib2-2.56.4-168.el8_10
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.