
CVE-2026-68398 Ubuntu PPPoL2TP use-after-free local privilege escalation

This repository contains a build-specific local privilege-escalation exploit
for CVE-2026-68398, a use-after-free race between PPPoL2TP receive
processing and destruction of an unattached PPP channel. It was validated in
QEMU against the official Ubuntu 22.04 package
linux-image-5.15.0-187-generic (5.15.0-187.197).
The final test starts as a dedicated UID 1001 user with no supplementary
groups, no capabilities and no sudo access, then obtains initial-namespace
UID 0. KASLR, SMEP, SMAP and AppArmor remain enabled. The guest is not booted
with nokaslr, nopti, nosmep or nosmap.
Warning
This code intentionally corrupts kernel heap state and is tied to one exact Ubuntu kernel build. A failed race can panic or corrupt the system. Run it only in an isolated, disposable VM that you own.
pppol2tp_recv() executes in the L2TP UDP receive path under an RCU read-side
critical section and eventually calls ppp_input(&po->chan). The PPPoX socket
and its embedded ppp_channel are RCU-safe, but the internal struct channel
used by ppp_input() is a separate allocation.
For a channel bound with PPPIOCGCHAN but neither attached to a PPP unit nor
bridged, the close path freed that internal object 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 consequently operate on the freed channel's 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.
The vulnerability was discovered and fixed upstream by
Norbert Szetei of Doyensec.
The mainline fix is
ec4215683e47424c9c4762fd3c60f552a3119142.
The Linux CNA record identifies the vulnerable lineage as beginning with Linux 4.15. Vendor kernels may carry the fix under a different version number, so the exact source or package changelog takes precedence over this table.
| Kernel line | First fixed release | Fix commit |
|---|---|---|
| 6.6.y | 6.6.148 | 4bb84e964ff0 |
| 6.12.y | 6.12.101 | 3ab32218d718 |
| 6.18.y | 6.18.42 | c9574b8a8ede |
| 7.1.y | 7.1.6 | 06213c85d8c0 |
| Mainline | 7.2-rc4 | ec4215683e47 |
Older supported vendor lines require a backport. As of 2026-08-12, the
Ubuntu CVE tracker
marks Jammy's linux package as needs-triage; no corrected Ubuntu Jammy
package was identified. The tested 5.15.0-187.197 source still calls
kfree(pch) directly and is vulnerable.
| Property | Value |
|---|---|
| Distribution | Ubuntu 22.04.5 LTS (Jammy) |
| Kernel package | linux-image-5.15.0-187-generic / 5.15.0-187.197 |
| Kernel release | 5.15.0-187-generic #197-Ubuntu |
| Architecture | x86-64 |
| VM | QEMU/KVM, -cpu host, four vCPUs, 5 GiB RAM |
| Initial identity | uid=1001(poc) gid=1001(poc) groups=1001(poc) |
| Initial capabilities | inheritable, permitted, effective and ambient all zero |
| Final identity | uid=0(root) gid=0(root) in the initial namespaces |
| Boot changes | None; stock command line, KASLR enabled |
The kernel was not recompiled. The minimal cloud image lacked
linux-modules-extra-5.15.0-187-generic, so that official Ubuntu package was
installed to provide l2tp_ppp. A normal Jammy installation using the
linux-generic metapackage already depends on the matching modules-extra
package. The protocol module autoloads when the exploit creates its PPPoL2TP
socket.
| Requirement | Ubuntu 5.15.0-187 state | Exploit use |
|---|---|---|
| PPP | CONFIG_PPP=y | Built in |
| L2TP / PPPoL2TP | CONFIG_L2TP=m, CONFIG_PPPOL2TP=m | Official linux-modules-extra module |
| User keys | CONFIG_KEYS=y | Reclaims the freed kmalloc-256 channel |
| AppArmor | CONFIG_SECURITY_APPARMOR=y | Remains active; the fake cred includes a valid unconfined label |
| KASLR | CONFIG_RANDOMIZE_BASE=y | Remains active; slide is recovered at runtime |
| SMAP | CONFIG_X86_SMAP=y | Remains active |
| User/network namespaces | Enabled by Ubuntu, but not used | No namespace setup or capability gain is required |
/dev/ppp | Not required | The channel is bound but never connected to a PPP unit |
The automatic KASLR detector uses an x86 prefetch timing side channel and
requires RDTSCP with KPTI inactive. The tested host CPU exposes rdtscp,
smep and smap and is not affected by Meltdown, so Ubuntu leaves KPTI
inactive without any boot override. On a CPU where /proc/cpuinfo reports the
pti flag, the detector refuses to run. That is an exploit-specific hardware
condition: the underlying CVE remains reachable, but this release does not
claim a KASLR bypass on a KPTI-active system. A manually supplied slide exists
only for controlled diagnostics.
struct channel. The still-running RX
path treats controlled key bytes as its skb queue and waitqueue.core_pattern as disclosure
scratch and exposes a key-allocation pointer.override_creds() with the forged cred.
UID/GID fields, capabilities and the required user, namespace, ucount and
group pointers match the exact Ubuntu build./proc/self/exe to a
unique file in /tmp, changes it to root-owned mode 4755 and verifies those
attributes./usr/bin/id.The last bridge is necessary because override_creds() changes subjective
credentials for the active kernel task; installing and executing the verified
SUID copy turns that short-lived primitive into normal process credentials.
The Makefile statically links the exploit. The timing-sensitive prefetch object
is deliberately compiled at -O0; the exploit itself uses -O2.
make
Equivalent commands:
gcc -O0 -Wall -Wextra -Werror -c -o build/kaslr_prefetch.o kaslr_prefetch.c
gcc -O2 -static -pthread -Wall -Wextra -Werror \
-o build/CVE-2026-68398 exploit.c build/kaslr_prefetch.o
From the ordinary user account on the exact validated kernel:
id
grep '^Cap' /proc/self/status
./CVE-2026-68398 auto 60