Someone left a loaded gun in a room nobody visits — the PPPoE send path holds a stale pointer across a buffer reallocation
CVE-2026-68121 is a use-after-free in the Linux kernel's pppoe_sendmsg() function. The code saves a pointer to the PPPoE header *before* calling dev_hard_header(), but that callback is allowed to reallocate the skb head — specifically when a GRE-delegated team device expands it via pskb_expand_head(). PPPoE then writes six bytes through the now-dangling pointer into freed heap memory. The bug was introduced in kernel 2.6.12 and affects every mainline kernel through 7.1.x. Fixed versions include 5.10.265, 5.15.216, 6.1.183, 6.6.148, 6.12.101, 6.18.42, and 7.1.6 (with distro backports still rolling out).
The vendor score of HIGH 7.8 is technically defensible — it's a kernel-mode UAF that can yield root, and a public PoC (PPPoEject) exists. But the CVSS vector doesn't capture the narrow reachability of this code path. Exploitation requires: (1) a local unprivileged user, (2) an active PPPoE session, (3) a team device with a GRE-delegated header callback, and (4) winning a race between copy_from_user() blocking and a port-add event. In the vast majority of enterprise Linux fleets — cloud VMs, container hosts, application servers — PPPoE is simply not configured. The 7.8 overweights the impact while ignoring the near-zero exposure population.
4 steps from start to impact.
Local user opens AF_PPPOX socket
socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE). This requires CAP_NET_RAW or an unprivileged user namespace with network namespace access, depending on kernel config.- Local shell on target host
- PPPoE session active on the host
- AF_PPPOX socket family compiled into kernel (default on most distros)
- PPPoE is rarely used in enterprise data centers, cloud VMs, or container hosts
- Most hardened environments restrict
CAP_NET_RAWfor unprivileged users
Trigger pppoe_sendmsg with large payload
sendmsg() on the PPPoE socket with a payload large enough that copy_from_user() blocks (e.g., due to page fault / userfaultfd). While blocked, the kernel holds a stale pointer to the PPPoE header in the skb. The PPPoEject PoC uses userfaultfd or FUSE to reliably stall the copy.- Ability to use
userfaultfdorFUSEfor fault stalling - Kernel must not have
userfaultfdrestricted (sysctlvm.unprivileged_userfaultfd=1)
- Many modern distros set
vm.unprivileged_userfaultfd=0by default (RHEL 9, Ubuntu 22.04+) - FUSE-based stalling requires
fusegroup membership or user namespace tricks
userfaultfd syscall; seccomp profiles blocking it in container runtimesRace: add non-Ethernet port to team device
pskb_expand_head(), reallocating the skb head and freeing the old buffer. The PPPoE header pointer is now dangling.- A team device must exist or be creatable
- Attacker needs
CAP_NET_ADMINin the relevant network namespace to manipulate team device membership
- Creating/modifying team devices requires elevated network capabilities
- Team + GRE configuration is uncommon outside specialized network appliances
- Race window is tight — PoC uses userfaultfd to widen it
UAF write and heap corruption
copy_from_user() returns, pppoe_sendmsg() writes six bytes of attacker-controlled PPPoE header data through the stale pointer into the freed slab object. With heap feng shui (pre-arranged slab layout), the attacker overwrites a sensitive kernel structure — typically a struct cred or function pointer — to escalate to root.- Successful race condition from step 3
- Heap layout must be groomed for reliable exploitation
- Heap grooming reliability varies across kernel versions and slab allocators (SLUB vs SLAB)
- KASLR, SMEP, SMAP, and CFI all raise the exploitation bar significantly
team device creation in audit logsThe supporting signals.
| In-the-Wild Exploitation | None confirmed as of 2026-09-18. No CISA KEV listing. No GreyNoise or Shadowserver tags observed. |
|---|---|
| Proof-of-Concept | Public. PPPoEject by Asim Viladi Oglu Manizada. Uses userfaultfd/FUSE for race stabilization. Demonstrated root shell on Ubuntu 22.04 with default kernel. |
| EPSS | 0.00138 (≈ 14th percentile) — very low predicted exploitation probability |
| KEV Status | Not listed as of 2026-09-18 |
| CVSS Vector | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H — Local access, low complexity per CVSS (but real-world complexity is higher due to race + PPPoE prerequisite) |
| Affected Versions | Introduced in 2.6.12 (2005). Affects all mainline kernels through 7.1.x. Essentially every unpatched Linux kernel for the last 21 years carries the vulnerable code path. |
| Fixed Versions | 5.10.265, 5.15.216, 6.1.183, 6.6.148, 6.12.101, 6.18.42, 7.1.6. Distro backports: RHEL 9 via 5.14.0-687.46.1.el9_8; Ubuntu LTS patches still in progress across all supported releases. |
| Exposure / Scanning | PPPoE is a Layer 2 protocol — not directly scannable via Shodan/Censys. Exposure is determined by host configuration, not internet-facing surface. Enterprise PPPoE usage is estimated at <3% of managed Linux hosts (primarily ISP edge, some legacy WAN). |
| Disclosure Timeline | Reported mid-July 2026 → kernel patch merged late July → CVE published 2026-08-10 → PoC published 2026-09-18 |
| Researcher | Asim Viladi Oglu Manizada — coordinated disclosure with kernel security team and major distros |
noisgate verdict.
The single most decisive factor is the vanishingly small exposure population: PPPoE is active on fewer than 3% of enterprise Linux hosts, and the exploit additionally requires a team device with GRE delegation plus a race condition stabilized by userfaultfd (disabled by default on modern distros). The attack surface simply does not exist on the overwhelming majority of managed hosts.
Why this verdict
- Exposure population is near-zero in enterprise. PPPoE is a residential/ISP protocol. Cloud VMs, container hosts, database servers, and application tiers do not run PPPoE sessions. Fewer than 3% of enterprise-managed Linux hosts have the attack surface exposed.
- Multi-stage race condition with restricted primitives. The exploit requires
userfaultfd(disabled on RHEL 9, Ubuntu 22.04+ by default) or FUSE membership to stabilize the race. Without these, exploitation is probabilistic and crash-prone. CAP_NET_ADMINneeded for team device manipulation. Creating or modifying team interfaces requires network admin capabilities, which unprivileged users lack outside user namespaces.- Role multiplier: The high-value roles for this CVE would be Linux-based network edge appliances or ISP gateways where PPPoE is actually in use. On those devices, a local-to-root escalation is serious (blast radius: host-level, potentially network-segment-level). However, these devices represent a tiny fraction of the installed base. On canonical high-value roles — hypervisors (ESXi/KVM), domain controllers (not Linux PPPoE), CI/CD, backup servers — PPPoE is not configured, so the chain does not complete. The floor does not engage because no high-value role represents ≥1% of affected-component installs with PPPoE active.
- Public PoC keeps this from dropping to LOW. PPPoEject is weaponized and demonstrated. Any host that *does* run PPPoE with
userfaultfdenabled is exploitable today.
Why not higher?
The attack requires local access to a host running PPPoE — a protocol virtually absent from enterprise server fleets. Even on the rare host with PPPoE, the attacker needs userfaultfd or FUSE access to stabilize the race, and CAP_NET_ADMIN (or user-namespace tricks) for team device manipulation. This chain of prerequisites compounds to make real-world exploitation exceptional rather than routine.
Why not lower?
A public, weaponized PoC (PPPoEject) exists and has been demonstrated to yield root. The vulnerable code has been present since kernel 2.6.12, meaning the theoretical attack surface spans 21 years of kernels. On the small population of hosts where PPPoE *is* active — ISP edge boxes, some WAN gateways — this is a genuine local privilege escalation to root. Dropping to LOW would undercount that residual risk.
What to do — in priority order.
- Disable unprivileged userfaultfd — Set
sysctl vm.unprivileged_userfaultfd=0(already default on RHEL 9, Ubuntu 22.04+). This breaks the PoC's race-stabilization primitive. No mitigation SLA for MEDIUM — apply as part of standard hardening within the 365-day remediation window. - Restrict CAP_NET_RAW and CAP_NET_ADMIN — Ensure unprivileged users cannot open AF_PPPOX sockets or manipulate team devices. Use seccomp profiles in container runtimes to block
userfaultfdand raw socket creation. - Audit for PPPoE usage across your fleet — Run
grep -r pppoe /etc/ /etc/ppp/ 2>/dev/nullandip link show type pppoefleet-wide. Identify the (likely tiny) population of hosts with active PPPoE. Prioritize patching only those hosts. - Unload pppoe kernel module where unused — Run
modprobe -r pppoeand blacklist via/etc/modprobe.d/blacklist-pppoe.confon hosts that don't need PPPoE. Eliminates the attack surface entirely.
- Network-level firewalling — this is a local privilege escalation, not a remote exploit. Firewall rules are irrelevant.
- SELinux/AppArmor in default policy — confined users can still open AF_PPPOX sockets in many default profiles; custom policy would be needed to block it.
- KASLR alone — KASLR raises the bar but does not prevent heap-based UAF exploitation; the PoC accounts for it with information leaks.
Crowdsourced verification payload.
Run on each target Linux host as root (or via sudo). Example: sudo bash check_cve_2026_68121.sh. Checks kernel version against known-fixed releases and PPPoE module/configuration status.
#!/bin/bash
# CVE-2026-68121 Checker — PPPoE stale-pointer UAF
# Run as root on target Linux host
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -euo pipefail
KERNEL=$(uname -r)
echo "[*] Kernel: $KERNEL"
# Extract major.minor.patch
MAJOR=$(echo "$KERNEL" | cut -d. -f1)
MINOR=$(echo "$KERNEL" | cut -d. -f2)
PATCH=$(echo "$KERNEL" | cut -d. -f3 | cut -d- -f1)
version_gte() {
printf '%s\n%s' "$1" "$2" | sort -V -C
}
PPPOE_LOADED=0
if lsmod 2>/dev/null | grep -q pppoe; then
PPPOE_LOADED=1
echo "[!] pppoe kernel module is LOADED"
else
echo "[*] pppoe kernel module is not loaded"
fi
PPPOE_CONFIGURED=0
if [ -d /etc/ppp ] && grep -rq pppoe /etc/ppp/ 2>/dev/null; then
PPPOE_CONFIGURED=1
echo "[!] PPPoE configuration found in /etc/ppp/"
fi
if ip link show type pppoe &>/dev/null 2>&1; then
PPPOE_CONFIGURED=1
echo "[!] Active PPPoE interface detected"
fi
# Check fixed versions per stable branch
PATCHED=0
case "$MAJOR.$MINOR" in
5.10) version_gte "$MAJOR.$MINOR.$PATCH" "5.10.265" && PATCHED=1 ;;
5.15) version_gte "$MAJOR.$MINOR.$PATCH" "5.15.216" && PATCHED=1 ;;
6.1) version_gte "$MAJOR.$MINOR.$PATCH" "6.1.183" && PATCHED=1 ;;
6.6) version_gte "$MAJOR.$MINOR.$PATCH" "6.6.148" && PATCHED=1 ;;
6.12) version_gte "$MAJOR.$MINOR.$PATCH" "6.12.101" && PATCHED=1 ;;
6.18) version_gte "$MAJOR.$MINOR.$PATCH" "6.18.42" && PATCHED=1 ;;
7.1) version_gte "$MAJOR.$MINOR.$PATCH" "7.1.6" && PATCHED=1 ;;
7.2) version_gte "$MAJOR.$MINOR.$PATCH" "7.2.4" && PATCHED=1 ;;
*) # For distro kernels (e.g., RHEL 5.14.0-687.46+), check changelog
if rpm -q kernel 2>/dev/null | grep -q '687\.46' || \
dpkg -l linux-image-"$KERNEL" 2>/dev/null | grep -q '^ii'; then
echo "[*] Distro kernel detected — verify vendor advisory for patch status"
if [ $PPPOE_LOADED -eq 0 ] && [ $PPPOE_CONFIGURED -eq 0 ]; then
echo "PATCHED (not exposed — PPPoE not in use)"
exit 0
fi
echo "UNKNOWN (distro kernel — check vendor advisory)"
exit 2
fi
;;
esac
if [ $PATCHED -eq 1 ]; then
echo "PATCHED"
exit 0
fi
if [ $PPPOE_LOADED -eq 0 ] && [ $PPPOE_CONFIGURED -eq 0 ]; then
echo "VULNERABLE (code present but PPPoE not active — low practical risk)"
exit 1
fi
echo "VULNERABLE"
exit 1If you remember one thing.
pppoe module if it's not needed and ensure vm.unprivileged_userfaultfd=0. There is no mitigation SLA under noisgate for MEDIUM-severity findings — go straight to the 365-day noisgate remediation SLA and fold the kernel update into your next scheduled patch cycle. If you do find PPPoE on high-value infrastructure (ISP edge, WAN gateways), treat those hosts as an exception and patch within 30 days given the public PoC. Do not let the vendor's HIGH label drive an emergency patching cycle across thousands of hosts that don't even have the attack surface.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.