← Back to Feed CACHED · 2026-10-02 20:29:47 · CACHE_KEY CVE-2026-43499
CVE-2026-43499 · CWE-416 · Disclosed 2026-05-21

Linux Kernel rtmutex/futex-PI Stack Use-After-Free (GhostLock)

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

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.

"Public 97%-reliable root exploit + container escape on every Linux distro since 2011. Patch now."
02 · The Attack Path

6 steps from start to impact.

STEP 01

Obtain any local code execution

The attacker needs an unprivileged shell or code-execution primitive on the target Linux host. This could come from a web application RCE, SSH with stolen credentials, a compromised CI/CD job, a container workload, or post-phishing implant. No elevated privileges or special capabilities are required — any UID works.
Conditions required:
  • Local code execution on a Linux host running kernel 2.6.39–7.0.x
  • CONFIG_FUTEX_PI=y (default on all major distros)
Where this breaks in practice:
  • Requires prior initial access — the bug itself is not remotely exploitable
Detection/coverage: EDR agents (CrowdStrike Falcon, SentinelOne) monitor for anomalous futex syscall patterns; auditd rules on futex(2) with FUTEX_CMP_REQUEUE_PI op can flag the trigger sequence.
STEP 02

Trigger FUTEX_CMP_REQUEUE_PI deadlock rollback

The attacker crafts a pair of futexes and threads so that the kernel's requeue path detects a priority-inheritance deadlock cycle and enters the -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.
Conditions required:
  • Ability to call futex(2) syscall (available to all users)
  • At least two threads in the attacker's process
Where this breaks in practice:
  • seccomp profiles that block FUTEX_CMP_REQUEUE_PI op would prevent this, but almost no production profile does
Detection/coverage: Falco rules or eBPF-based monitors can detect rapid FUTEX_CMP_REQUEUE_PI / FUTEX_WAIT_REQUEUE_PI cycling. Nuclei template CVE-2026-43499 available.
STEP 03

Dangling pi_blocked_on pointer created

Because 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.
Conditions required:
  • Successful deadlock rollback from step 2
Where this breaks in practice:
  • Kernel stack randomization (RANDKSTACK) shifts the address but does not prevent reuse; the PoC handles this
Detection/coverage: KASAN (if compiled in) will flag the stack-use-after-free at this point; production kernels rarely ship with KASAN.
STEP 04

Stack spray reclaims freed memory

The attacker triggers new kernel stack allocations (via thread creation or deep syscall chains) to reclaim the freed stack page. Controlled data is written into the region formerly occupied by the 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.
Conditions required:
  • Ability to create threads or trigger stack-heavy syscalls
Where this breaks in practice:
  • PID/thread cgroup limits could slow this, but default limits (tens of thousands) are more than sufficient
Detection/coverage: Anomalous thread-creation spikes detectable by process-behavior analytics.
STEP 05

Kernel code execution → root

A subsequent PI chain walk (triggered by another futex operation or scheduling event) follows the dangling pointer into the attacker-controlled data, dereferencing forged function pointers. This gives arbitrary kernel code execution. The exploit overwrites 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.
Conditions required:
  • Successful memory reclamation from step 4
Where this breaks in practice:
  • 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
Detection/coverage: Kernel integrity monitors (LKRG) detect credential modification; EDR behavioral detection for setuid(0) from non-root process.
STEP 06

Container escape to host (optional)

When exploited from within a container, the kernel code execution primitive operates in host kernel context — containers share the host kernel. The exploit can directly modify namespace pointers and cgroup membership in 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).
Conditions required:
  • Target is running inside a container on a vulnerable host kernel
Where this breaks in practice:
  • gVisor with KVM platform isolates the kernel; Kata Containers / Firecracker VMs use a separate kernel
Detection/coverage: Falco/Tetragon detect namespace transitions (setns patterns) from container processes; container-aware EDR flags PID 1 escape.
03 · Compensating Control

1
CRITICAL 9.2→IGNORE 0.0
SEVERITY REDUCED
Apply vendor kernel patches immediately — This is the definitive fix. Upgrade to the patched kernel version for your distribution (e.g., Ubuntu 6.8.0-136.136 for Noble, RHEL kernel-5.14.0-716.el9 for CentOS Stream 9) and reboot. KernelCare/TuxCare livepatch is available for rebootless deployment. Per the noisgate mitigation SLA for CRITICAL, deploy within 3 days. The noisgate remediation SLA allows 90 days for full fleet coverage, but given the public weaponized exploit, prioritize internet-facing hosts, Kubernetes nodes, and hypervisors in the first 72 hours.
2
CRITICAL 9.2→LOW 2.5
SEVERITY REDUCED
Deploy seccomp profiles blocking FUTEX_CMP_REQUEUE_PI — The exploit requires the 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.
3
CRITICAL 9.2→HIGH 7.0
SEVERITY REDUCED
Enable kernel lockdown mode and LKRG — Kernel lockdown mode (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.
4
CRITICAL 9.2→MEDIUM 5.5
SEVERITY REDUCED
Isolate container workloads with VM-based runtimes — Replace runc/containerd with Kata Containers or gVisor (KVM platform) on high-value Kubernetes nodes. These use a dedicated guest kernel, so a host-kernel UAF in the container's futex path is not reachable from the workload. This eliminates the container-escape vector entirely. Deploy within 3 days for multi-tenant and internet-facing clusters.
5
CRITICAL 9.2→HIGH 7.5
SEVERITY REDUCED
Restrict unprivileged user namespaces — Set 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.
What doesn't work
  • 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-only or --no-new-privileges — The exploit modifies kernel memory directly; it does not need to write to the filesystem or call execve with setuid binaries. These container hardening flags do not help.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNot 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 AvailabilityMultiple 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 Score0.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 StatusNot 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 VectorCVSS: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 VersionsLinux 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 VersionsUpstream: 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 TimelineApr 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 / DiscovererVEGA (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 / ExposureNot 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.

  1. NebuSec IonStack Part II: GhostLock Research Writeup
  2. SecurityOnline: Public Exploit for GhostLock Enables LPE and Container Escape
  3. The Hacker News: 15-Year-Old GhostLock Flaw Enables Root and Container Escape
  4. Ubuntu Security Tracker — CVE-2026-43499
  5. Debian Security Tracker — CVE-2026-43499
  6. Red Hat Customer Portal — CVE-2026-43499
  7. NebuSec CyberMeowfia PoC Repository (GitHub)
  8. TuxCare: GhostLock CVE-2026-43499 Linux Futex Root Exploit
05 · The Call

Final Verdict
↑ UPGRADED to CRITICAL (9.2/10)

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=y is 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.

06 · Verification

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.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/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