A lock-room door left unlocked for fifteen years now has a master key anyone can copy in five seconds
CVE-2026-43499, branded GhostLock, is a stack use-after-free in the Linux kernel's remove_waiter() function within kernel/locking/rtmutex.c. During a FUTEX_CMP_REQUEUE_PI rollback (-EDEADLK), the kernel clears pi_blocked_on on the *current* task instead of the *waiter's* task, leaving a dangling pointer into a freed kernel stack frame. Any subsequent PI chain walk dereferences that pointer. Affected kernels span 2.6.39 through 7.0.x, shipping with CONFIG_FUTEX_PI=y — the default on every major distribution — making essentially every Linux system deployed in the last fifteen years vulnerable. Fixed upstream in 7.1 with commit 3bfdc63936dd, and backported to stable branches 6.1.175, 6.6.140, 6.12.86, 6.18.27, and 7.0.4.
The vendor CVSS of 7.8 HIGH captures the local-access, low-privilege, high-CIA-impact profile correctly at the base metric level, but it significantly undersells real-world risk. The score uses S:U (Scope: Unchanged), yet Nebula Security's public exploit demonstrates container-to-host escape — a textbook scope change. Moreover, the 97%-reliable, five-second root shell weaponization, the universal default-on attack surface, and the kernel's role as the canonical trust boundary for hypervisors, container runtimes, and CI/CD infrastructure all compound to make this far more dangerous than a garden-variety local privilege escalation. The vendor score should be treated as a floor, not a ceiling.
6 steps from start to impact.
Obtain any local code execution
- Local code execution on a Linux host running kernel 2.6.39–7.0.x
- CONFIG_FUTEX_PI=y (default on all major distros)
- Requires prior initial access — the bug itself is not remotely exploitable
futex(2) with FUTEX_CMP_REQUEUE_PI op can flag the trigger sequence.Trigger FUTEX_CMP_REQUEUE_PI deadlock rollback
-EDEADLK rollback in remove_waiter(). This is a normal unprivileged syscall — no CAP_SYS_PTRACE, no seccomp bypass needed. The NebuSec/VEGA PoC (ghostlock.c) automates the exact thread choreography.- Ability to call
futex(2)syscall (available to all users) - At least two threads in the attacker's process
- seccomp profiles that block
FUTEX_CMP_REQUEUE_PIop would prevent this, but almost no production profile does
FUTEX_CMP_REQUEUE_PI / FUTEX_WAIT_REQUEUE_PI cycling. Nuclei template CVE-2026-43499 available.Dangling pi_blocked_on pointer created
remove_waiter() clears current->pi_blocked_on instead of waiter->task->pi_blocked_on, the waiter thread returns to userspace with its pi_blocked_on still pointing at a struct rt_mutex_waiter on the now-freed kernel stack frame. The attacker controls when this thread resumes, giving a stable race-free dangling pointer — no heap feng shui timing window required.- Successful deadlock rollback from step 2
- Kernel stack randomization (RANDKSTACK) shifts the address but does not prevent reuse; the PoC handles this
Stack spray reclaims freed memory
rt_mutex_waiter struct, giving the attacker a forged waiter with attacker-controlled function pointers and task pointers. The NebuSec exploit uses clone() stacking for deterministic reclamation.- Ability to create threads or trigger stack-heavy syscalls
- PID/thread cgroup limits could slow this, but default limits (tens of thousands) are more than sufficient
Kernel code execution → root
current->cred to gain uid=0 and disables SELinux/AppArmor enforcement in-kernel. The full chain completes in roughly five seconds with ~97% reliability per NebuSec's published metrics.- Successful memory reclamation from step 4
- CFI (Control-Flow Integrity) in newer kernels (≥6.1 with clang CFI) raises the bar but the PoC includes a CFI-compatible variant using data-only attacks
- SMEP/SMAP are bypassed via the data-only code-reuse path
setuid(0) from non-root process.Container escape to host (optional)
current->nsproxy to escape to the host PID/mount/network namespaces, gaining full host root access. This bypasses all container isolation including Docker, containerd, CRI-O, and gVisor's sentry (if using the native ptrace platform).- Target is running inside a container on a vulnerable host kernel
- gVisor with KVM platform isolates the kernel; Kata Containers / Firecracker VMs use a separate kernel
setns patterns) from container processes; container-aware EDR flags PID 1 escape.FUTEX_CMP_REQUEUE_PI futex operation (op code 12). A seccomp-bpf profile that blocks this specific operation prevents the trigger entirely. For Kubernetes, apply via a SeccompProfile in the pod security context. For Docker, extend the default seccomp profile. Warning: Some PI-futex-dependent workloads (real-time audio, certain JVMs, glibc robust mutexes) may break — test first. Deploy within 3 days as an interim control while patching proceeds.lockdown=integrity or lockdown=confidentiality boot param, available since kernel 5.4) restricts what root can do to the running kernel, and the Linux Kernel Runtime Guard (LKRG) detects credential and namespace modifications in real-time. Together they don't prevent the exploit from firing but dramatically limit post-exploitation utility and raise detection probability. Deploy within 3 days on critical-tier hosts.kernel.unprivileged_userns_clone=0 (Debian/Ubuntu) or use user.max_user_namespaces=0 sysctl. While the core exploit does not require user namespaces, some PoC variants use them for heap layout control. This raises the bar modestly. Not a standalone mitigation.- SELinux/AppArmor alone — The exploit operates in kernel context and can disable MAC enforcement in-memory before policy checks apply. MAC is bypassed, not worked around.
- Network segmentation / firewalls — The vulnerability is local, not network-exploitable. Network controls do not affect the attack path at all.
- Disabling hyperthreading / SMT — This is not a speculative-execution or side-channel bug. SMT settings are irrelevant.
- Running as non-root / dropping capabilities — The exploit works from a fully unprivileged user with zero capabilities. Dropping caps provides no protection.
- Docker
--read-onlyor--no-new-privileges— The exploit modifies kernel memory directly; it does not need to write to the filesystem or callexecvewith setuid binaries. These container hardening flags do not help.
The supporting signals.
| In-the-Wild Exploitation | Not confirmed. Not listed in CISA KEV. No GreyNoise or Shadowserver mass-exploitation signals as of 2026-10-03. However, the public full-chain exploit lowers the bar to near-zero skill, and post-exploitation LPE tools are rarely visible at the network layer. |
|---|---|
| Proof-of-Concept Availability | Multiple public PoCs. Primary: NebuSec/CyberMeowfia IonStack — full-chain root exploit, 97% reliable, ~5 seconds. Additional repos: MobiusM/CVE-2026-43499, lkeld/CVE-2026-43499-poc, gagaltotal/CVE-2026-43499-PoC-Scanner. Android-specific variants by chenxqiyu and xianwan1314. Listed on Sploitus with multiple entries. |
| EPSS Score | 0.00278 (0.278%) — low probability of exploitation in the next 30 days per FIRST model. This is likely an artifact of EPSS weighting network-facing vulns more heavily; local kernel LPE chains are historically under-scored by EPSS. |
| KEV Status | Not listed as of 2026-10-03. Given the public exploit and container-escape capability, KEV addition would not be surprising if in-the-wild use is confirmed. |
| CVSS Vector | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H → 7.8 HIGH. Scope is listed as Unchanged, which arguably understates the container-escape impact (should be S:C → ~8.7). Temporal score with public exploit and functional PoC would push toward 7.8 × 1.0 (exploit maturity: High). |
| Affected Versions | Linux kernel 2.6.39 (May 2011) through 7.0.x. Bug introduced in commit 3eb65aeaec87 (rtmutex rework). CONFIG_FUTEX_PI=y required — enabled by default on RHEL, Ubuntu, Debian, SUSE, Arch, Alpine, Android, and all major cloud images. |
| Fixed Versions | Upstream: 7.1 (commit 3bfdc63936dd). Stable backports: 6.1.175, 6.6.140, 6.12.86, 6.18.27, 7.0.4. Ubuntu: 6.8.0-136.136 (Noble), 5.15.0-187.197 (Jammy), 5.4.0-234.254 (Focal). Debian: 5.10.262-1 (bullseye), 6.1.176-1 (bookworm), 6.12.94-1 (trixie). RHEL: kernel-5.14.0-716.el9 (CentOS Stream 9), kernel-6.12.0-239.el10 (Stream 10). AlmaLinux 8/9/10 patched. |
| Disclosure Timeline | Apr 18, 2026: Reported to [email protected]. Apr 20: Fix committed upstream. May 4: Backported to stable branches. May 21: CVE assigned/disclosed. Jun 2: Regression fix (CVE-2026-53166). Jun 30: kernelCTF reward ($92,337). Jul 7: Full public disclosure with PoC by NebuSec/VEGA. |
| Researcher / Discoverer | VEGA (Nebula Security / NebuSec automated scanner). Part of the IonStack research series. Earned $92,337 via Google kernelCTF. Published at nebusec.ai/research/ionstack-part-2/. |
| Scanning / Exposure | Not network-exploitable, so Shodan/Censys/FOFA exposure data is not applicable. However, any multi-tenant Linux environment (cloud VMs, Kubernetes clusters, shared hosting) is exposed if any tenant can run unprivileged code. Scanner coverage: Qualys QID available, Nessus plugin published, Trivy/Grype detect via package version checks. |
Sources.
- NebuSec IonStack Part II: GhostLock Research Writeup
- SecurityOnline: Public Exploit for GhostLock Enables LPE and Container Escape
- The Hacker News: 15-Year-Old GhostLock Flaw Enables Root and Container Escape
- Ubuntu Security Tracker — CVE-2026-43499
- Debian Security Tracker — CVE-2026-43499
- Red Hat Customer Portal — CVE-2026-43499
- NebuSec CyberMeowfia PoC Repository (GitHub)
- TuxCare: GhostLock CVE-2026-43499 Linux Futex Root Exploit
Why this verdict
- Public weaponized exploit removes all exploitation friction. The NebuSec/VEGA full-chain PoC delivers a root shell in ~5 seconds at 97% reliability from any unprivileged local account. This is not theoretical — it is copy-paste weaponized. The temporal/environmental score should reflect exploit maturity: High.
- Container escape elevates scope beyond CVSS baseline. The vendor-assigned
S:U(Scope: Unchanged) does not account for the demonstrated container-to-host escape. In Kubernetes environments, a compromised pod → host root → access to all co-located pods, kubelet credentials, and potentially cluster-admin via node bootstrap tokens. This is a scope change (S:C) that the base score of 7.8 does not reflect. - Universal attack surface with zero configuration requirements.
CONFIG_FUTEX_PI=yis the default on every major distribution. The bug has existed since 2011 (kernel 2.6.39). There is no non-default configuration, optional feature flag, or uncommon deployment pattern that narrows the vulnerable population. Every Linux system that hasn't been patched since May 2026 is vulnerable. - Role multiplier: Kubernetes / container orchestration nodes. The Linux kernel IS the isolation boundary for containers. On Kubernetes worker nodes (a massive fraction of the Linux installed base in enterprise), this chain succeeds and the outcome is *tenant escape → node compromise → lateral movement across all pods on the node → potential cluster compromise via stolen service account tokens and kubelet certs*. This is fleet-scale impact. The kernel is canonically the high-value component here.
- Role multiplier: KVM/QEMU hypervisors. On Linux-based hypervisor hosts, root access means control over all guest VMs — memory inspection, disk access, network interception. This is fleet-scale impact for virtualized infrastructure.
- Role multiplier: CI/CD runners and build infrastructure. Linux-based CI/CD agents (Jenkins, GitLab Runner, GitHub Actions self-hosted) running on vulnerable kernels allow any job with code execution to escalate to host root, pivot to secrets, signing keys, and artifact stores. This is supply-chain-scale impact.
- Role multiplier: Production database servers, SIEM, and backup infrastructure. Root on a PostgreSQL/MongoDB host = full data exfiltration. Root on a Splunk/Elastic host = detection-plane compromise. Root on a Veeam/backup host = ransomware enablement. These are all standard Linux deployments with the default kernel config.
- Friction adjustment: Local access required (+0 friction penalty). While AV:L limits direct remote exploitation, the attack is the *perfect post-initial-access privilege escalation*. In any realistic attack chain (phishing → beacon, web RCE → shell, compromised container workload), the attacker already has local code execution. The local-access prerequisite provides minimal real-world friction because it is the expected state at the point where LPE is needed.
Why not higher?
A score of 9.2 rather than 10.0 reflects that this is not *directly* remotely exploitable — an attacker must first obtain local code execution through a separate vector. It also requires the target to be running an unpatched Linux kernel, and while that population is enormous, patched versions have been available since May 2026 (~5 months). Systems running gVisor (KVM platform) or Kata Containers with dedicated guest kernels are not affected by the container-escape variant.
Why not lower?
Downgrading below CRITICAL would require ignoring the public weaponized exploit, the container-escape capability, and the fact that the Linux kernel is the canonical high-value trust boundary for virtually all enterprise infrastructure. The 97% exploit reliability eliminates the 'theoretical only' discount. The universal default-on attack surface eliminates the 'niche configuration' discount. The kernel's role in hypervisors, container runtimes, CI/CD, databases, and security infrastructure means ≥10% of the installed base occupies high-value roles by definition — triggering the CRITICAL floor per blast-radius policy. The local-access prerequisite is standard for LPE bugs and is already priced into every post-exploitation playbook.
Crowdsourced verification payload.
Run this script on each target Linux host as any user (no root required). Invoke with: chmod +x ghostlock_check.sh && ./ghostlock_check.sh. It checks the running kernel version against known-patched versions and verifies whether CONFIG_FUTEX_PI is enabled.
#!/usr/bin/env bash
# ghostlock_check.sh — Check for CVE-2026-43499 (GhostLock) vulnerability
# Run on target Linux host. No root required.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -euo pipefail
KERNEL=$(uname -r)
echo "[*] Running kernel: $KERNEL"
# Check if FUTEX_PI is compiled in (required for vuln)
if [ -f /boot/config-"$KERNEL" ]; then
if ! grep -q 'CONFIG_FUTEX=y' /boot/config-"$KERNEL" 2>/dev/null; then
echo "[*] CONFIG_FUTEX not enabled in kernel config."
echo "PATCHED"
exit 0
fi
fi
# Extract major.minor.patch from kernel version
# Handles formats like: 6.1.175-1-amd64, 5.15.0-187-generic, 7.0.4-200.fc40
VERSION=$(echo "$KERNEL" | grep -oP '^[0-9]+\.[0-9]+\.[0-9]+')
if [ -z "$VERSION" ]; then
echo "[!] Could not parse kernel version from: $KERNEL"
echo "UNKNOWN"
exit 2
fi
MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f2)
PATCH=$(echo "$VERSION" | cut -d. -f3)
echo "[*] Parsed version: $MAJOR.$MINOR.$PATCH"
# version_gte: returns 0 if $1.$2.$3 >= $4.$5.$6
version_gte() {
local a1=$1 a2=$2 a3=$3 b1=$4 b2=$5 b3=$6
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
}
# Fixed versions per stable branch:
# 7.1+ — fixed
# 7.0.4+ — fixed
# 6.18.27+ — fixed
# 6.12.86+ — fixed
# 6.6.140+ — fixed
# 6.1.175+ — fixed
# < 2.6.39 — not affected (bug introduced in 2.6.39)
# Not affected: kernels before 2.6.39
if (( MAJOR < 2 )) || { (( MAJOR == 2 )) && (( MINOR < 6 )); } || \
{ (( MAJOR == 2 )) && (( MINOR == 6 )) && (( PATCH < 39 )); }; then
echo "[+] Kernel predates the vulnerable code (introduced in 2.6.39)."
echo "PATCHED"
exit 0
fi
# 7.1+ is fixed
if version_gte "$MAJOR" "$MINOR" "$PATCH" 7 1 0; then
echo "[+] Kernel $VERSION >= 7.1.0 — fixed upstream."
echo "PATCHED"
exit 0
fi
# 7.0.x branch
if (( MAJOR == 7 )) && (( MINOR == 0 )); then
if version_gte "$MAJOR" "$MINOR" "$PATCH" 7 0 4; then
echo "[+] Kernel $VERSION >= 7.0.4 — patched in stable."
echo "PATCHED"
exit 0
else
echo "[-] Kernel $VERSION < 7.0.4 — VULNERABLE."
echo "VULNERABLE"
exit 1
fi
fi
# 6.18.x branch
if (( MAJOR == 6 )) && (( MINOR == 18 )); then
if version_gte "$MAJOR" "$MINOR" "$PATCH" 6 18 27; then
echo "[+] Kernel $VERSION >= 6.18.27 — patched in stable."
echo "PATCHED"
exit 0
else
echo "[-] Kernel $VERSION < 6.18.27 — VULNERABLE."
echo "VULNERABLE"
exit 1
fi
fi
# 6.12.x branch
if (( MAJOR == 6 )) && (( MINOR == 12 )); then
if version_gte "$MAJOR" "$MINOR" "$PATCH" 6 12 86; then
echo "[+] Kernel $VERSION >= 6.12.86 — patched in stable."
echo "PATCHED"
exit 0
else
echo "[-] Kernel $VERSION < 6.12.86 — VULNERABLE."
echo "VULNERABLE"
exit 1
fi
fi
# 6.6.x branch
if (( MAJOR == 6 )) && (( MINOR == 6 )); then
if version_gte "$MAJOR" "$MINOR" "$PATCH" 6 6 140; then
echo "[+] Kernel $VERSION >= 6.6.140 — patched in stable."
echo "PATCHED"
exit 0
else
echo "[-] Kernel $VERSION < 6.6.140 — VULNERABLE."
echo "VULNERABLE"
exit 1
fi
fi
# 6.1.x branch
if (( MAJOR == 6 )) && (( MINOR == 1 )); then
if version_gte "$MAJOR" "$MINOR" "$PATCH" 6 1 175; then
echo "[+] Kernel $VERSION >= 6.1.175 — patched in stable."
echo "PATCHED"
exit 0
else
echo "[-] Kernel $VERSION < 6.1.175 — VULNERABLE."
echo "VULNERABLE"
exit 1
fi
fi
# Any other 6.x or 5.x or 4.x or 3.x or 2.6.39+ kernel
# that is NOT on a patched stable branch is vulnerable
echo "[-] Kernel $VERSION is in the affected range (2.6.39–7.0.x) and not on a patched stable branch."
echo " Check your distro's kernel package version for backported fixes."
# Attempt distro-specific package version check
if command -v dpkg &>/dev/null; then
PKG=$(dpkg -l 'linux-image-*' 2>/dev/null | awk '/^ii.*linux-image-[0-9]/{print $2, $3}' | head -5)
echo "[*] Installed kernel packages (dpkg):"
echo "$PKG"
elif command -v rpm &>/dev/null; then
PKG=$(rpm -qa 'kernel*' 2>/dev/null | head -5)
echo "[*] Installed kernel packages (rpm):"
echo "$PKG"
fi
echo "VULNERABLE"
exit 1