← Back to Feed CACHED · 2026-10-15 16:58:01 · CACHE_KEY CVE-2026-74580
CVE-2026-74580 · CWE-825 · Disclosed 2026-08-21

Linux kernel vhost stale IOTLB metadata cache leading to LPE and VM escape (Vghost)

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

A forgotten cache key lets you rewrite kernel memory the way a lost hotel master-key lets you open every door on the floor

CVE-2026-74580 — branded *Vghost* by Tencent Zhuque Lab — is a stale metadata-cache flaw in the Linux kernel's vhost virtualization-acceleration subsystem, present since kernel 4.11 (2017) through 7.2-rc4. When a user reconfigures a virtual ring via VHOST_SET_VRING_ADDR or VHOST_SET_VRING_NUM, the per-queue IOTLB cache (vq->meta_iotlb[]) is not invalidated. Subsequent operations hit the stale cache, skip validation, and translate new addresses through old mappings — yielding deterministic, race-free arbitrary kernel read/write. From there the attacker reads the IDT to bypass KASLR and overwrites modprobe_path for instant root. A second exploitation vector targets KVM/QEMU hosts with virtual IOMMU: a guest triggers VIRTIO_F_RING_RESET, forcing the host's QEMU process to re-issue VHOST_SET_VRING_ADDR without cache reset, corrupting the QEMU heap and hijacking QOM method tables for guest-to-host escape. Affected versions: Linux 4.11 – 7.2-rc4. Fixed in stable branches 5.10.265, 5.15.216, 6.1.183, 6.6.152, 6.12.104, 6.18.45, 7.1.9, 7.2.

The vendor (kernel.org CNA) rates this HIGH 8.8 with a Scope-Changed vector (S:C). That score is defensible for the general Linux population, but it under-weights the hypervisor blast radius. The vhost subsystem exists solely to accelerate virtio I/O for virtual machines — its canonical deployment IS the hypervisor host. On RHEL 9.8, Amazon Linux 2023, Azure Linux 3.0, and Oracle Linux 10.2, the device nodes /dev/vhost-vsock and /dev/vhost-net ship with 0666 world-accessible permissions, meaning *any* unprivileged shell — a compromised web app, a low-priv container escape, an SSH brute-force — immediately chains to root with a single deterministic exploit. On a hypervisor node, that root equals control of every guest on the box. The PoC is public, race-free, and portable across a decade of kernels. noisgate upgrades this to CRITICAL 9.0.

"Decade-old vhost cache bug gives any local user deterministic root; on hypervisors it is a VM escape."
02 · The Attack Path

6 steps from start to impact.

STEP 01

Obtain local shell on target host

Attacker gains any low-privileged shell on the target Linux system — via SSH credential stuffing, web-app compromise, container escape, or insider access. No special group membership or capability is required on distributions shipping /dev/vhost-* with 0666 permissions (RHEL, Amazon Linux, Azure Linux, Oracle Linux). On Ubuntu, membership in the kvm group is needed.
Conditions required:
  • Low-privilege local shell on a Linux host with vhost module loaded or loadable
Where this breaks in practice:
  • Requires initial access — this is not remotely exploitable
  • On Ubuntu the device is 0660 root:kvm, limiting to kvm group members
Detection/coverage: Standard login/access monitoring; SSH audit logs; container runtime alerts
STEP 02

Open vhost device and prime the IOTLB cache

The attacker opens /dev/vhost-vsock (or /dev/vhost-net), sets up a vring, and issues a normal VHOST_SET_VRING_ADDR ioctl to populate vq->meta_iotlb[] with valid IOTLB translations for the descriptor, available, and used ring regions. This is a normal kernel API call; no exploit code is visible yet.
Conditions required:
  • Read/write access to /dev/vhost-vsock or /dev/vhost-net
Where this breaks in practice:
  • None on 0666 distros — the ioctl succeeds for any user
Detection/coverage: Auditd rules on vhost device opens (-w /dev/vhost-vsock -p rw); Falco/Tetragon eBPF rules for unexpected vhost ioctl sequences
STEP 03

Repoint vring address without cache invalidation

The attacker issues a second VHOST_SET_VRING_ADDR ioctl pointing the used-ring address to a target kernel address (e.g., modprobe_path). Because the kernel does not call __vhost_vq_meta_reset(), the old IOTLB cache entry persists. The next iotlb_access_ok() check hits the stale cache and returns success without range validation — the kernel now believes the attacker-chosen address is a legitimate guest mapping.
Conditions required:
  • Stale cache from step 2 still valid
Where this breaks in practice:
  • None — deterministic, race-free; no timing window or heap grooming required
Detection/coverage: No mainstream detection today; kernel tracepoints on vhost_vq_meta_fetch could be instrumented but are not in standard rulesets
STEP 04

Achieve arbitrary kernel read/write

With the stale cache translating arbitrary addresses, the attacker triggers a vring kick. For writes, the kernel writes an 8-byte vring_used_elem at the attacker-chosen address. For reads, repointing desc_user_addr to kernel memory causes vhost to fetch from that address; the first 8 bytes leak via IOTLB miss messages. Both primitives are fully deterministic and repeatable. The PoC (zhangutah/security-research) demonstrates a benign modprobe_path overwrite and restore.
Conditions required:
  • Steps 2-3 completed
Where this breaks in practice:
  • Attacker needs kernel symbol addresses — resolved from /boot/System.map or /proc/kallsyms (readable by default on many distros)
Detection/coverage: Kernel integrity monitoring (e.g., LKRG) could detect modprobe_path tampering; runtime KASLR entropy monitoring
STEP 05

KASLR bypass and root escalation

The attacker reads IDT gate 0 at the fixed virtual address 0xfffffe0000000000 to recover the asm_exc_divide_error handler and calculate the kernel slide, defeating KASLR. Then overwrites modprobe_path to point to an attacker-controlled script, triggers an unknown binary format to invoke modprobe, and gains root execution. The entire chain — from unprivileged user to root — runs in a single process without races or retries.
Conditions required:
  • Arbitrary read/write from step 4
  • System.map or /proc/kallsyms readable (default on tested distros)
Where this breaks in practice:
  • Kernel lockdown mode (if enforced) restricts /proc/kallsyms and System.map access
  • SELinux in enforcing mode may block modprobe_path overwrite payload execution
Detection/coverage: EDR/auditd alerting on modprobe_path changes; process execution from unusual paths
STEP 06

VM escape variant (conditional)

On KVM/QEMU hosts with virtual IOMMU (iommu_platform=on), an untrusted guest negotiates VIRTIO_F_RING_RESET and triggers a ring reset. The host QEMU process calls VHOST_SET_VRING_ADDR in the host kernel without clearing the vhost metadata cache. The stale mapping extrapolates beyond guest RAM into the host QEMU process heap. Guest-controlled writes corrupt QOM method tables, hijacking QEMU control flow for guest-to-host code execution.
Conditions required:
  • Guest VM on a KVM/QEMU host
  • Virtual IOMMU enabled with iommu_platform=on
  • VIRTIO_F_RING_RESET support negotiated
Where this breaks in practice:
  • Virtual IOMMU is not the default QEMU configuration — it must be explicitly enabled
  • Cloud providers typically use custom hypervisors, not stock QEMU+vhost
  • Requires guest kernel cooperation to negotiate the feature flags
Detection/coverage: QEMU process crash monitoring; host-side integrity checks on QEMU heap; libvirt audit logs for vIOMMU-enabled domains
03 · Compensating Control

1
CRITICAL 9.0→HIGH 7.5
SEVERITY REDUCED
Restrict /dev/vhost-* permissions to 0660 root:kvm immediately — The single highest-impact mitigation. On RHEL, Amazon Linux, Azure Linux, and Oracle Linux, vhost device nodes ship 0666 (world-accessible). Run chmod 0660 /dev/vhost-net /dev/vhost-vsock and persist via udev rules (KERNEL=="vhost-*", MODE="0660", GROUP="kvm"). This eliminates the unprivileged LPE path entirely — only kvm group members retain access. Deploy within 3 days per noisgate mitigation SLA for CRITICAL findings.
2
CRITICAL 9.0→IGNORE 0.0
SEVERITY REDUCED
Blacklist vhost modules on non-virtualization hosts — On servers that do not run KVM/QEMU, prevent module autoloading: echo 'install vhost /bin/true' > /etc/modprobe.d/disable-vhost.conf and unload with modprobe -r vhost_net vhost_vsock vhost_scsi vhost. This removes the attack surface entirely on non-virtualization systems. Deploy within 3 days.
3
CRITICAL 9.0→HIGH 7.0
SEVERITY REDUCED
Disable virtual IOMMU on VMs that do not require it — The VM escape vector requires iommu_platform=on in the guest's virtio device configuration. Audit libvirt domain XMLs for <driver iommu='on'/> and remove it from guests that don't need device assignment or VFIO passthrough. This eliminates the guest-to-host escape path. Deploy within 3 days.
4
CRITICAL 9.0→IGNORE 0.0
SEVERITY REDUCED
Apply kernel patch and reboot — The definitive fix. Update to a kernel containing commit de845981da67: 5.10.265+, 5.15.216+, 6.1.183+, 6.6.152+, 6.12.104+, 6.18.45+, 7.1.9+, or 7.2+. Schedule within 90 days per noisgate remediation SLA for CRITICAL findings. Prioritize hypervisor hosts and multi-tenant infrastructure for the earliest patch wave.
5
CRITICAL 9.0→CRITICAL 8.5
Deploy auditd rules for vhost device access — Add detection coverage: -w /dev/vhost-vsock -p rw -k vhost_access and -w /dev/vhost-net -p rw -k vhost_access. Alert on access by UIDs outside the expected virtualization service accounts (libvirtd, QEMU). This does not prevent exploitation but provides forensic visibility and early warning. Deploy within 3 days.
What doesn't work
  • SELinux / AppArmor alone — while MAC policies may restrict post-exploitation payload execution (e.g., blocking the modprobe_path trick), they do not prevent the kernel memory corruption itself. An attacker with arbitrary kernel read/write can disable or bypass MAC enforcement by modifying in-kernel security structures.
  • Network segmentation / firewalls — this is a local privilege escalation; there is no network component to block. Firewalls and IDS/IPS provide zero mitigation.
  • Kernel lockdown mode — restricts access to /proc/kallsyms and /boot/System.map, which complicates KASLR bypass. However, the PoC includes a fallback to hardcoded IDT addresses at 0xfffffe0000000000, and kernel lockdown is not enabled by default on any tested distribution.
  • Container namespaces without device-cgroup — standard container isolation (PID/network/mount namespaces) does not prevent access to /dev/vhost-* if the device is bind-mounted or the container runs with elevated device access. Only explicit device-cgroup deny rules or --device restrictions prevent container-based exploitation.
04 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo confirmed exploitation as of 2026-10-16. Not listed in CISA KEV. Tencent Zhuque Lab disclosed full technical details and working demonstrations on 2026-10-12, with oss-security post on 2026-10-14. The deterministic, race-free nature of the exploit and public PoC make weaponization trivial.
Proof-of-conceptPublic and functional. Tencent Zhuque Lab published a full write-up at matrix.tencent.com. PoC code at zhangutah/security-research — a benign modprobe_path overwrite/restore against /dev/vhost-vsock. Also indexed on Sploitus. The exploit requires no heap grooming, no race conditions, and no kernel-structure-layout dependencies — it is portable across a decade of kernels.
EPSS0.00126 (~60th percentile). Low model confidence likely reflects the local-only attack vector and recent PoC disclosure (2026-10-12). Expect this to climb as exploit tooling propagates.
KEV statusNot listed as of 2026-10-16. Given no confirmed in-the-wild exploitation yet, KEV addition is not imminent but should be monitored.
CVSS vectorCVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H = 8.8 HIGH. S:C (Scope Changed) reflects the cross-boundary impact: unprivileged user → kernel, or guest → host. AC:L is accurate — the exploit is deterministic with no race condition. PR:L is accurate for most distros with 0666 device permissions.
Affected versionsLinux kernel 4.11 through 7.2-rc4. Introduced by commit f88949138058 (v4.11, O(1) metadata cache) compounded by 0210a8db2aec (v5.9, deferred GIOVA validation). Fixed in stable branches: 5.10.265, 5.15.216, 6.1.183, 6.6.152, 6.12.104, 6.18.45, 7.1.9, 7.2. Upstream fix: commit de845981da67.
Distro patch statusUbuntu 22.04: fixed (5.15.0-198.208). Ubuntu 24.04/26.04: vulnerable, patches in progress. Debian Bookworm: fixed (6.1.187-1). Debian Trixie: fixed (6.12.107-1). RHEL 9.8: fix pending (Red Hat Bugzilla 2521055). Amazon Linux 2023: fix pending. Oracle Linux: fix pending.
Device permissionsCritical detail: /dev/vhost-vsock and /dev/vhost-net ship with 0666 (world read/write) on RHEL 9.8, Amazon Linux 2023, Azure Linux 3.0, and Oracle Linux 10.2. On Ubuntu, permissions are 0660 root:kvm. The 0666 default means any unprivileged local user can exploit this without group membership.
Scanning/exposureNot remotely exploitable — no Shodan/Censys/GreyNoise exposure surface. The attack surface is local: any Linux system with vhost module loaded and /dev/vhost-* accessible. Nessus plugin ID 342043 available per VulDB.
Researcher / discoveryFourie Zhang (TencentOS Security Team / Tencent Zhuque Lab), discovered with assistance from Corvus AI (Tencent's multi-agent vulnerability discovery system). Timeline: bug identified 2026-07-31, fix merged 2026-08-03, CVE announced 2026-08-21, full LPE verified across five distros 2026-08-24, public disclosure 2026-10-12.

Sources.

  1. Tencent Zhuque Lab — Vghost full write-up
  2. GitHub PoC — zhangutah/security-research
  3. oss-security disclosure
  4. Red Hat CVE page
  5. Ubuntu Security Tracker
  6. Debian Security Tracker
  7. SecurityOnline — Vghost PoC disclosure analysis
  8. The Hacker News coverage
05 · The Call

Final Verdict
↑ UPGRADED to CRITICAL (9.0/10)

Why this verdict

  • Canonical hypervisor component — floor is CRITICAL. The vhost subsystem exists solely to accelerate virtio I/O for KVM/QEMU virtual machines. It is, by definition, a hypervisor component. The VM escape path (guest → host via stale IOTLB + QEMU heap corruption) succeeds under documented conditions (vIOMMU + iommu_platform). On multi-tenant KVM hosts, host compromise = fleet-scale impact across all co-resident guests. Per the deployment-role blast radius rule, the verdict floor for a bug IN a hypervisor subsystem with a demonstrated cross-boundary escape is CRITICAL.
  • Role multiplier: Hypervisor hosts (KVM/QEMU). The documented VM escape chain succeeds when virtual IOMMU is enabled. On dedicated KVM hypervisor nodes — which represent ≥10% of the installed base for vhost by design — the blast radius is tenant → host → fleet. A compromised guest gains arbitrary code execution in the host QEMU process, then leverages root on the hypervisor to access all co-resident VMs. This is the canonical worst-case for any virtualization bug.
  • Role multiplier: Kubernetes worker nodes. Many Kubernetes deployments load vhost-net for accelerated pod networking (e.g., with DPDK/SR-IOV passthrough). Root on a Kubernetes node means access to kubelet credentials, all pod secrets on that node, and lateral movement to the control plane. Blast radius: host → cluster.
  • Role multiplier: General Linux servers. On RHEL 9.8, Amazon Linux 2023, Azure Linux 3.0, and Oracle Linux 10.2, device nodes are 0666 — any unprivileged shell chains to root. On database servers, CI/CD runners, or backup hosts, this turns a low-value initial foothold into a high-value compromise.
  • Trivial exploitation eliminates practical AC barrier. The CVSS vector scores AC:L, and the reality is even more favorable to the attacker: the exploit is deterministic, race-free, requires no heap grooming, and works unchanged across a decade of kernels. The public PoC compiles with a single cc invocation and runs in under a second. This is not a theoretical primitive — it is a weaponizable tool.
  • Default permissions eliminate the PR:L barrier on most enterprise distros. CVSS scores PR:L, implying some privilege is needed. In practice, on four of the five tested enterprise distributions, /dev/vhost-* is world-accessible (0666). Any process that can execute code — a compromised web application, a container escape to the host namespace, a low-privilege SSH session — can immediately trigger the full chain.
  • Public PoC with detailed write-up collapses attacker effort to near-zero. Tencent Zhuque Lab's Vghost write-up provides the complete exploitation methodology including KASLR bypass technique, arbitrary read/write primitives, and the VM escape chain. The GitHub PoC is a self-contained C file. The gap between 'public PoC' and 'in-the-wild exploitation' is measured in days, not months.

Why not higher?

A 9.0 rather than 10.0 because the vulnerability requires local access — there is no remote exploitation path. An attacker must first obtain a shell on the target host (or control a guest VM for the escape variant). This initial-access requirement is a genuine constraint that prevents mass autonomous exploitation. The VM escape path additionally requires explicit vIOMMU configuration, which is not the QEMU default. These factors cap the score below the maximum.

Why not lower?

The local-access requirement cannot pull the verdict below CRITICAL because the affected component is canonically a hypervisor subsystem and the demonstrated impact includes guest-to-host VM escape on KVM infrastructure. On non-virtualization hosts, the 0666 default permissions on most enterprise distros mean the 'low privilege' prerequisite is effectively zero — any code execution foothold chains to root instantly. The exploit is deterministic and public. Downgrading to HIGH would require evidence that fewer than 10% of systems with vhost loaded serve virtualization or other high-value roles, but vhost's entire purpose is VM I/O acceleration, and the LPE path threatens every Linux server regardless of role.

06 · Verification

Crowdsourced verification payload.

Run this script on each target Linux host as any user (no root required). Invoke with: bash check_vghost.sh. The script checks the running kernel version against known-fixed stable branches and inspects vhost device permissions. Exit code 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_vghost.sh — CVE-2026-74580 (Vghost) local detection
# Checks running kernel version and vhost device permissions.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -euo pipefail

KERNEL=$(uname -r)
echo "[*] Running kernel: $KERNEL"

# Extract major.minor.patch from kernel version string
parse_version() {
  local ver="$1"
  # Strip everything after first hyphen for comparison
  local base="${ver%%-*}"
  local major minor patch
  IFS='.' read -r major minor patch <<< "$base"
  patch=${patch%%[^0-9]*}  # strip trailing non-numeric
  echo "$major $minor ${patch:-0}"
}

version_gte() {
  local a1 a2 a3 b1 b2 b3
  read -r a1 a2 a3 <<< "$(parse_version "$1")"
  read -r b1 b2 b3 <<< "$(parse_version "$2")"
  if (( a1 > b1 )); then return 0; fi
  if (( a1 < b1 )); then return 1; fi
  if (( a2 > b2 )); then return 0; fi
  if (( a2 < b2 )); then return 1; fi
  if (( a3 >= b3 )); then return 0; fi
  return 1
}

VULNERABLE=0
PATCHED=0

read -r MAJOR MINOR PATCH <<< "$(parse_version "$KERNEL")"

# Check against fixed stable branches
if (( MAJOR < 4 )) || { (( MAJOR == 4 )) && (( MINOR < 11 )); }; then
  echo "[+] Kernel predates vulnerable code (introduced in 4.11). PATCHED"
  PATCHED=1
elif (( MAJOR == 5 && MINOR == 10 )); then
  if version_gte "$KERNEL" "5.10.265"; then
    echo "[+] Fixed in 5.10.265; running $KERNEL. PATCHED"; PATCHED=1
  else
    echo "[-] Vulnerable: 5.10.x < 5.10.265"; VULNERABLE=1
  fi
elif (( MAJOR == 5 && MINOR == 15 )); then
  if version_gte "$KERNEL" "5.15.216"; then
    echo "[+] Fixed in 5.15.216; running $KERNEL. PATCHED"; PATCHED=1
  else
    echo "[-] Vulnerable: 5.15.x < 5.15.216"; VULNERABLE=1
  fi
elif (( MAJOR == 6 && MINOR == 1 )); then
  if version_gte "$KERNEL" "6.1.183"; then
    echo "[+] Fixed in 6.1.183; running $KERNEL. PATCHED"; PATCHED=1
  else
    echo "[-] Vulnerable: 6.1.x < 6.1.183"; VULNERABLE=1
  fi
elif (( MAJOR == 6 && MINOR == 6 )); then
  if version_gte "$KERNEL" "6.6.152"; then
    echo "[+] Fixed in 6.6.152; running $KERNEL. PATCHED"; PATCHED=1
  else
    echo "[-] Vulnerable: 6.6.x < 6.6.152"; VULNERABLE=1
  fi
elif (( MAJOR == 6 && MINOR == 12 )); then
  if version_gte "$KERNEL" "6.12.104"; then
    echo "[+] Fixed in 6.12.104; running $KERNEL. PATCHED"; PATCHED=1
  else
    echo "[-] Vulnerable: 6.12.x < 6.12.104"; VULNERABLE=1
  fi
elif (( MAJOR == 6 && MINOR == 18 )); then
  if version_gte "$KERNEL" "6.18.45"; then
    echo "[+] Fixed in 6.18.45; running $KERNEL. PATCHED"; PATCHED=1
  else
    echo "[-] Vulnerable: 6.18.x < 6.18.45"; VULNERABLE=1
  fi
elif (( MAJOR == 7 && MINOR == 1 )); then
  if version_gte "$KERNEL" "7.1.9"; then
    echo "[+] Fixed in 7.1.9; running $KERNEL. PATCHED"; PATCHED=1
  else
    echo "[-] Vulnerable: 7.1.x < 7.1.9"; VULNERABLE=1
  fi
elif (( MAJOR == 7 && MINOR >= 2 )); then
  echo "[+] Kernel 7.2+ includes the fix. PATCHED"; PATCHED=1
elif (( MAJOR >= 4 && MAJOR <= 7 )); then
  # Non-stable branches between 4.11 and 7.1 without explicit fix tracking
  echo "[?] Kernel $KERNEL is in the vulnerable range (4.11-7.2) but not a tracked stable branch."
  echo "    Check if your distro has backported commit de845981da67."
fi

# Check vhost device permissions
echo ""
echo "[*] Checking vhost device exposure:"
EXPOSED=0
for dev in /dev/vhost-net /dev/vhost-vsock /dev/vhost-scsi; do
  if [ -e "$dev" ]; then
    PERMS=$(stat -c '%a' "$dev" 2>/dev/null || stat -f '%Lp' "$dev" 2>/dev/null)
    OWNER=$(stat -c '%U:%G' "$dev" 2>/dev/null || stat -f '%Su:%Sg' "$dev" 2>/dev/null)
    echo "    $dev  mode=$PERMS  owner=$OWNER"
    if [[ "$PERMS" == *"666"* ]] || [[ "$PERMS" == *"662"* ]]; then
      echo "    WARNING: $dev is world-writable — any local user can exploit CVE-2026-74580"
      EXPOSED=1
    fi
  else
    echo "    $dev  not present"
  fi
done

# Check if vhost modules are loaded
echo ""
echo "[*] Checking vhost module status:"
if lsmod 2>/dev/null | grep -q vhost; then
  lsmod | grep vhost | while read -r mod rest; do
    echo "    Module loaded: $mod"
  done
else
  echo "    No vhost modules currently loaded"
fi

echo ""
if (( PATCHED == 1 )); then
  echo "RESULT: PATCHED"
  exit 0
elif (( VULNERABLE == 1 )); then
  if (( EXPOSED == 1 )); then
    echo "RESULT: VULNERABLE (world-accessible vhost devices — CRITICAL exposure)"
  else
    echo "RESULT: VULNERABLE"
  fi
  exit 1
else
  echo "RESULT: UNKNOWN — verify commit de845981da67 is present in your kernel source"
  exit 2
fi