Ubuntu Linux Kernel PPPoL2TP Use-After-Free Local Privilege Escalation (CVE-2026-68398)
Published: 2026-08-16 • Researcher: A. Ramos (@aramosf); vulnerability discovery and upstream fix by Norbert Szetei (Doyensec)
- Severity
- High
- CVE
- CVE-2026-68398
- Category
- binary
- Affected product
- Linux Kernel (PPPoL2TP subsystem)
- Affected versions
- Linux 4.15+ through mainline pre-7.2-rc4; validated on Ubuntu 22.04.5 LTS kernel 5.15.0-187-generic (5.15.0-187.197)
- Disclosed
- 2026-08-16
- Patch status
- Patched
Tags
References
- https://www.cve.org/CVERecord?id=CVE-2026-68398
- https://git.kernel.org/stable/c/ec4215683e47424c9c4762fd3c60f552a3119142
- https://patch.msgid.link/E793FCF2-58DE-4387-A983-C7B4BC3158BD@doyensec.com
- https://git.launchpad.net/ubuntu-cve-tracker/plain/active/CVE-2026-68398
- https://github.com/aramosf/cve-2026-68398
Archive entry
intelseclab/poc-archiveOn this page
Metadata
| Field | Value |
|---|---|
| Date Added | 2026-08-16 |
| Last Updated | 2026-08-16 |
| Author / Researcher | A. Ramos (@aramosf); vulnerability discovery and upstream fix by Norbert Szetei (Doyensec) |
| CVE / Advisory | CVE-2026-68398 |
| Category | binary |
| Severity | High |
| CVSS Score | 7.8 (local privilege escalation) |
| Status | Patched |
| Tags | linux, kernel, ubuntu, pppol2tp, l2tp, ppp, uaf, use-after-free, race-condition, lpe, privilege-escalation, kaslr-bypass, apparmor-bypass, suid, heap-spray, kmalloc-256, CVE-2026-68398 |
| Related |
Affected Target
| Field | Value |
|---|---|
| Software / System | Linux Kernel (PPPoL2TP subsystem) |
| Versions Affected | Linux 4.15+ through mainline pre-7.2-rc4; validated on Ubuntu 22.04.5 LTS kernel 5.15.0-187-generic (5.15.0-187.197) |
| Language / Platform | C (x86-64 Linux, static binary) |
| Authentication Required | None – unprivileged local user, no capabilities or sudo |
| Network Access Required | Local |
Summary
CVE-2026-68398 is a use-after-free race condition between PPPoL2TP receive processing and destruction of a bound-but-unattached PPP channel in the Linux kernel. The PPPoX socket and its embedded ppp_channel are RCU-safe, but the internal struct channel used by ppp_input() is a separate allocation that was freed immediately on close, while the L2TP UDP receive path could still be accessing it under an RCU read-side critical section.
This build-specific exploit turns the race into initial-namespace UID 0 on an unmodified Ubuntu 22.04.5 kernel with KASLR, SMEP, SMAP, and AppArmor all enabled. It uses user-key payloads to reclaim the freed kmalloc-256 channel object, performs multi-stage heap disclosures via skb queue list operations and core_pattern scratch, constructs a forged credential with a valid unconfined AppArmor label, and invokes override_creds() through a controlled waitqueue callback. A temporary SUID copy converts the kernel-task credential primitive into real process credentials.
Vulnerability Details
Root Cause
pppol2tp_recv() executes in the L2TP UDP receive path under an RCU read-side critical section and calls ppp_input(&po->chan). For a channel bound with PPPIOCGCHAN but neither attached to a PPP unit nor bridged, the close path freed the internal struct channel immediately:
RX: l2tp_udp_encap_recv -> pppol2tp_recv -> ppp_input(channel)
CLOSE: pppol2tp_release -> pppox_unbind_sock
-> ppp_unregister_channel -> ppp_release_channel -> kfree(channel)The receive path can operate on the freed channel skb queue and waitqueue. The fix adds an RCU callback to struct channel and defers the queue purge and free until an RCU grace period has elapsed.
Attack Flow
- Race setup: Four race workers repeatedly create bound-but-unattached PPPoL2TP channels via loopback UDP, send valid L2TPv2/PPP frames from multiple CPUs, and close the data socket to trigger the free.
- Heap reclaim: A user-key payload (
KEYCTL_UPDATE) reclaims the freedstruct channelinkmalloc-256. The still-running RX path treats the controlled key bytes as its skb queue and waitqueue. - Stage 1-2 disclosure: Queue list operations disclose a live skb pointer through the readable key payload. A controlled unlink uses
core_patternas disclosure scratch and exposes a key-allocation heap address. - Stage 3-4 credential carrier: A 224-byte key payload becomes a persistent credential carrier with embedded fake cred fields (UID/GID 0, full capabilities,
override_creds()as callback). A second disclosure reveals the carrier key object heap address. - Stage 5-8 AppArmor bypass: Two additional pointer-source and unlink stages inject a valid AppArmor credential blob pointer and an unconfined fake label into the forged credential, satisfying Ubuntu AppArmor MAC checks.
- Stage 9 privilege escalation: The reclaimed waitqueue entry calls
override_creds()with the forged cred. The temporarily privileged execution context copies/proc/self/exeto a SUID root helper in/tmp, which the original unprivileged task then executes to obtain real UID/GID 0.
Impact
- Local privilege escalation to initial-namespace root
- Bypasses KASLR (via prefetch timing side channel), SMEP, SMAP, and AppArmor
- Requires only an unprivileged user account – no capabilities, no sudo, no namespace setup
- PPPoL2TP module autoloads when the exploit creates its socket
- Probabilistic race – can panic or corrupt the kernel on failure
Environment / Lab Setup
| |
Proof of Concept
See
exploit.c(906 lines, C) andkaslr_prefetch.c(333 lines, C) in this folder – mirrored byte-for-byte from aramosf/cve-2026-68398. The upstream README is preserved asupstream-README.md.
Step-by-Step Reproduction
- Build:
makeproduces a statically linked binary atbuild/CVE-2026-68398. - Prepare VM: Boot Ubuntu 22.04.5 with kernel
5.15.0-187-genericin QEMU/KVM with 4 vCPUs and 5 GiB RAM. Installlinux-modules-extra-5.15.0-187-genericif not present. - Run as unprivileged user:
./CVE-2026-68398 auto 60(auto KASLR detection, 60-second race timeout per stage). - Verify: Successful run ends with
uid=0(root) gid=0(root).
Exploit Code
Heap reclaim via user-key update after PPPoL2TP channel free:
| |
Forged credential construction with AppArmor unconfined label:
| |
Expected Output
[KASLR] image_base=0xffffffffb9200000 slide=0x38200000
CVE-2026-68398 Ubuntu local privilege-escalation exploit
uid=1001 euid=1001 slide=0x38200000
session keyring=123456789
[1/9] racing for a reclaimed channel and persistent skb...
stage 1 hit: key=12345 last_skb=0xffff... qlen=1
...
[9/9] invoking override_creds and installing the SUID transition...
override callback: uid=0 euid=0 gid=0 egid=0
installed root-owned mode 4755 helper: /tmp/.cve-2026-68398-root-948
SUID helper ready; executing it from the original unprivileged task...
SUID transition: uid=0 euid=0 gid=0 egid=0
uid=0(root) gid=0(root) groups=0(root),1001(poc)Detection and Indicators of Compromise
Remediation
| Action | Detail |
|---|---|
| Patch | Update to a fixed kernel: 6.6.148+, 6.12.101+, 6.18.42+, 7.1.6+, or mainline 7.2-rc4+. Ubuntu Jammy: check the Ubuntu CVE tracker for the patched linux-image package version. |
| Workaround | Block unprivileged loading of l2tp_ppp and pppol2tp modules: echo "install l2tp_ppp /bin/false" >> /etc/modprobe.d/cve-2026-68398.conf. Disable unprivileged user namespaces if not needed. |
| Verification | Check kernel version with uname -r. Verify the fix commit is present in your kernel source or package changelog. |
References
Notes
Verified by reading the full exploit source: exploit.c (906 lines, C), kaslr_prefetch.c (333 lines, C/MIT), kaslr_prefetch.h (9 lines), Makefile (20 lines), README.md (224 lines), and SUBMISSION.md (100 lines). Also reviewed scripts/ directory (expect/shell recording helpers) and assets/ (asciinema cast + gif demo).
Malware screen – clean. Pure C using only Linux syscalls and POSIX interfaces (socket, ioctl, keyctl, pthread, open/read/write/close/fchown/fchmod/execl). No network callbacks, no remote downloaders, no credential exfiltration, no miners, no obfuscation. The KASLR prefetch component is MIT-licensed and derived from the well-known KASLD project (bcoles/kasld). The Makefile produces a statically linked binary with no install-time side effects. No committed binaries – source only.
The exploit is build-specific: hardcoded to Ubuntu 22.04.5 kernel 5.15.0-187-generic with exact vmlinux symbol addresses. The source validates uname() and refuses any other kernel release. Nine-stage exploitation chain with KASLR bypass, multi-stage heap disclosure, forged credential construction with AppArmor unconfined label, and SUID transition. Exceptionally high quality with negative test against the upstream fix included.
Author: A. Ramos (@aramosf) – credible security researcher already in our archive for CVE-2026-68138. Vulnerability discovery credited to Norbert Szetei of Doyensec (reputable offensive security firm).