A graphics server forgets to put down its pen before someone else grabs it
CVE-2025-26599 is an uninitialized pointer dereference in the X.Org server's device handling code path (the Xkb / device event plumbing shared with Xwayland). When a local client sends a crafted sequence of input device requests, the server reads a stack/heap slot that was never initialized, giving a controlled value that can be steered into a write primitive. Affected: xorg-server before 21.1.16 and xwayland before 24.1.6, plus all distro packages built from those trees (RHEL 7/8/9, Ubuntu 20.04/22.04/24.04, Debian bullseye/bookworm, SUSE 15.x). The fix landed in the X.Org 21.1.16 release alongside a batch of six sibling memory-safety CVEs (CVE-2025-26594 through 26600) reported by Trend Micro Zero Day Initiative.
Vendor severity of HIGH (7.8) is *defensible but inflated for the typical enterprise host*. The CVSS vector AV:L/AC:L/PR:L/UI:N/C:H/I:H/A:H is honest — it correctly captures that this is local-only with low-privileged auth required. The score assumes the affected machine is a multi-user desktop where unprivileged users routinely have an X session. That describes a *narrow slice* of an enterprise fleet: lab boxes, jump hosts, RHV/RHEL Workstation, Linux engineer laptops. On the 90%+ of Linux installs that are headless servers with no Xorg running, this CVE is *inert*.
4 steps from start to impact.
Land a local session on a host running Xorg or Xwayland
/tmp/.X11-unix/X0) or to Xwayland via the user's session. This is the precondition that defines the entire CVE class — no X server, no bug.- Local code execution as any UID
- Xorg or Xwayland process active
- Access to the X socket (same user, or
xhost +misconfig)
- Headless servers do not run Xorg at all
- Server-class RHEL/Ubuntu minimal images do not install xorg-server
- MAC (SELinux, AppArmor) confines unconfined_t shells away from the X socket in some hardened builds
xorg-x11-server-cve-2025-26599 all detect by package version. Nessus credentialed scan catches it cleanly.Open an X connection and issue the trigger request
DISPLAY=:0, then issues the malformed device / Xkb extension request sequence that walks the uninitialized pointer. ZDI's disclosure references a PoC pattern shared across the Feb 2025 X.Org batch (xkb_swap_* family).- Xlib/XCB available or static binary
- Display reachable from attacker's UID
- With per-user Wayland sessions and no Xwayland fallback, no socket exists
- GNOME/KDE on modern Wayland-by-default distros do not run rootful Xorg
connect() to X sockets from unexpected binaries.Convert uninitialized read into a controlled write
XIChangeDeviceProperty, OOB write in XISendDeviceHierarchyEvent), a reliable write-what-where becomes practical. ZDI rated this exploitable.- Heap groom feasibility (typical glibc layout)
- No CFI on Xorg binary (default on stock distro builds)
- Distro builds with
-fstack-protector-strong,-D_FORTIFY_SOURCE=2, full RELRO reduce reliability - Xorg runs as root on classic deployments but as the user under
Xorg.wrap+ systemd-logind on modern distros — root drop changes the prize
Escalate to root (classic Xorg) or to session compromise (Xwayland / rootless)
CAP_SYS_RAWIO, the write primitive yields root. On modern Wayland-by-default systems where Xwayland runs as the logged-in user, the primitive yields session-level code execution — equivalent privilege to the user but with access to all their sessions, keyrings, and SSH agent.- Setuid Xorg OR session containing valuable credentials
- Fedora 36+, Ubuntu 21.04+, RHEL 9 default to rootless Xorg / Wayland — root is no longer the prize
- Locked-down kiosk hosts have no creds worth stealing
execve from xorg uid, EDR (CrowdStrike, SentinelOne, Defender for Endpoint Linux) flags post-exploit behaviour, not the primitive.systemctl get-default is multi-user.target and no admin uses ssh -X, uninstall xorg-x11-server-Xorg / xserver-xorg-core. Eliminates the attack surface entirely. Do this within the 365-day noisgate remediation SLA; no mitigation SLA applies at MEDIUM.DISABLE_WAYLAND=0 and remove xwayland package on hosts that have no legacy X11 client requirement. Reduces the runtime attack surface to zero on modern desktops.xhost + in /etc/X11/xinit/* and user dotfiles. Ensure MIT-MAGIC-COOKIE-1 is the auth method and /tmp/.X11-unix is mode 1777 with sticky bit intact. Blocks cross-user socket access on shared hosts.- Network ACLs / firewall rules — vector is
AV:L. Nothing on the wire to filter. - WAF / reverse proxy controls — X11 is not HTTP and is not exposed externally on any sane deployment.
- Disabling X11 forwarding in sshd (
X11Forwarding no) — protects against remote-X-via-SSH abuse but does not stop a local attacker who already has a shell from talking to a locally-running Xorg. - EDR behavioral rules alone — there is no characteristic syscall pattern for the uninitialized-pointer trigger; EDR catches post-exploit, not the primitive.
The supporting signals.
| In-the-wild status | No known exploitation. Not on CISA KEV. No incident reports from Mandiant, CrowdStrike Falcon Intel, or Red Canary as of disclosure batch review. |
|---|---|
| Public PoC | No fully weaponized public PoC. ZDI advisory ZDI-25-117 describes the bug class; trigger snippets exist in the X.Org commit history (xserver mainline patch xkb: Fix buffer overflow in _XkbSetCompatMap-adjacent commits). |
| EPSS | 0.0036 (≈ 0.4 percentile) — FIRST.org models it as essentially never exploited in the wild. |
| KEV | Not listed. No CISA action. |
| CVSS v3.1 | AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H → 7.8 HIGH. Local attack vector is the key constraint. |
| CWE | CWE-824 — Access of Uninitialized Pointer. |
| Affected | xorg-server < 21.1.16, xwayland < 24.1.6, plus distro builds (RHEL xorg-x11-server pre-backport, Ubuntu xorg-server pre-USN-7244-1, Debian DSA-5860-1, SUSE SUSE-SU-2025:0683). |
| Fixed | Upstream 21.1.16 / xwayland 24.1.6. Distro backports: RHEL xorg-x11-server-1.20.11-25.el8_10, Ubuntu 2:21.1.4-2ubuntu1.7~22.04.13, Debian 2:1.20.11-1+deb11u14. |
| Exposure | Shodan/Censys do not index local X sockets. GreyNoise N/A — not network-reachable. Internal estimate: 5-15% of a typical Linux fleet runs Xorg/Xwayland (engineering workstations, jump hosts, lab); ~85% headless servers are unaffected at runtime even when the package is installed. |
| Reporter | Trend Micro Zero Day Initiative, part of the Feb 2025 X.Org coordinated batch (CVE-2025-26594 through CVE-2025-26600). |
Sources.
Why this verdict
- Local-only vector (
AV:L) — attacker must already have a shell. This is an LPE, not an RCE. Subtract one bucket from the vendor's vector-blind 7.8. - Exposure narrowing — on a 10,000-host fleet, the realistic affected population is the desktop / jump host slice (~10-20%). Servers patch the package via routine OS updates but never load the vulnerable code.
- Role multiplier: low-value role (workstation, dev sandbox) — chain succeeds, blast radius is *single user's session* on modern rootless Xorg. No lateral movement primitive from this CVE alone.
- Role multiplier: typical role (multi-user jump host, bastion, shared engineering box) — chain succeeds, blast radius is *root on that host* if legacy setuid Xorg, otherwise session-level. Still host-scoped, no domain pivot.
- Role multiplier: high-value role — Xorg is not canonically deployed on identity, hypervisor, PKI, backup, or CI/CD control planes. Where it is (admin workstation with vSphere/Vault TUI cached creds), the blast radius is the *operator's session*, not the platform — but this argues for hardening admin workstations, not floor-raising this CVE.
- No KEV, EPSS 0.36%, no public weaponized PoC — no active campaign pressure to override the friction discount.
Why not higher?
Not HIGH because the vendor's 7.8 already prices in C:H/I:H/A:H against a hypothetical multi-user desktop. In an enterprise fleet that ratio is inverted — most hosts have no X server at runtime — and the chain requires prior local access, so the effective risk is materially below the 7.0 HIGH threshold. No KEV, no observed exploitation, no fleet-scale blast radius keep it below HIGH.
Why not lower?
Not LOW because on engineering workstations and shared multi-user Linux hosts this is a reliable LPE primitive, and the package is installed on a meaningful slice of the fleet. Six sibling CVEs in the same batch raise the chance one of them gets weaponized publicly, which would re-rate this class quickly. MEDIUM with a 365-day patch window — but not backlog-only.
Crowdsourced verification payload.
Run on each Linux target as root or via sudo (needs to read package DB and check whether Xorg is actually running). Invoke as sudo ./check-cve-2025-26599.sh. Exits 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN. Output also includes whether Xorg/Xwayland is *running* — if not, the host is effectively not exploitable even when the package is present.
#!/usr/bin/env bash
# noisgate check: CVE-2025-26599 (X.Org / Xwayland uninitialized pointer LPE)
# Exit: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
VULN=2
RUNNING="no"
ver_ge() {
# returns 0 if $1 >= $2 (dpkg-style compare, fallback to sort -V)
if command -v dpkg >/dev/null 2>&1; then
dpkg --compare-versions "$1" ge "$2"
else
[ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -n1)" = "$2" ]
fi
}
check_pkg() {
local name="$1" fixed="$2" cur=""
if command -v rpm >/dev/null 2>&1; then
cur=$(rpm -q --qf '%{VERSION}-%{RELEASE}\n' "$name" 2>/dev/null | head -n1)
elif command -v dpkg-query >/dev/null 2>&1; then
cur=$(dpkg-query -W -f='${Version}\n' "$name" 2>/dev/null | head -n1)
fi
[ -z "$cur" ] && return 2
echo " $name installed: $cur (fixed >= $fixed)"
if ver_ge "$cur" "$fixed"; then return 0; else return 1; fi
}
echo "[*] Checking xorg-server and xwayland packages..."
for entry in \
"xorg-x11-server-Xorg:1.20.11-25" \
"xserver-xorg-core:2:21.1.4-2ubuntu1.7" \
"xwayland:24.1.6" \
"xorg-x11-server-Xwayland:24.1.6"; do
pkg="${entry%%:*}"
fix="${entry#*:}"
check_pkg "$pkg" "$fix"
rc=$?
case $rc in
0) [ "$VULN" -ne 1 ] && VULN=0 ;;
1) VULN=1 ;;
esac
done
if pgrep -x Xorg >/dev/null 2>&1 || pgrep -x Xwayland >/dev/null 2>&1; then
RUNNING="yes"
fi
echo "[*] Xorg/Xwayland currently running: $RUNNING"
case $VULN in
0) echo "PATCHED"; exit 0 ;;
1) if [ "$RUNNING" = "yes" ]; then
echo "VULNERABLE (runtime exposure: YES)"
else
echo "VULNERABLE (runtime exposure: NO — package present but X server not running)"
fi
exit 1 ;;
*) echo "UNKNOWN (xorg/xwayland not installed or package manager not detected)"; exit 2 ;;
esac