## https://sploitus.com/exploit?id=628B205B-26F6-549D-8E7E-A25EAC5E1A6F
# CVE-2026-68398: Ubuntu PPPoL2TP use-after-free to local root

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.
## Vulnerability
`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:
```text
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](https://patch.msgid.link/E793FCF2-58DE-4387-A983-C7B4BC3158BD@doyensec.com).
The mainline fix is
[`ec4215683e47424c9c4762fd3c60f552a3119142`](https://git.kernel.org/stable/c/ec4215683e47424c9c4762fd3c60f552a3119142).
## Affected and fixed versions
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`](https://git.kernel.org/stable/c/4bb84e964ff0fe0a171c965362de72f9820dbce9) |
| 6.12.y | 6.12.101 | [`3ab32218d718`](https://git.kernel.org/stable/c/3ab32218d7182705dae5c86f13925f458072da2c) |
| 6.18.y | 6.18.42 | [`c9574b8a8ede`](https://git.kernel.org/stable/c/c9574b8a8edeb4edd3ac6472c27ef7184bdb2baa) |
| 7.1.y | 7.1.6 | [`06213c85d8c0`](https://git.kernel.org/stable/c/06213c85d8c0994f786c093b8b2a517987943ca6) |
| Mainline | 7.2-rc4 | [`ec4215683e47`](https://git.kernel.org/stable/c/ec4215683e47424c9c4762fd3c60f552a3119142) |
Older supported vendor lines require a backport. As of **2026-08-12**, the
[Ubuntu CVE tracker](https://git.launchpad.net/ubuntu-cve-tracker/plain/active/CVE-2026-68398)
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.
## Validated target
| 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.
## Requirements and default Ubuntu state
| 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.
## Exploitation process
1. Four race workers repeatedly create a bound-but-unattached PPPoL2TP channel,
send valid L2TPv2/PPP frames from other CPUs and close the data socket.
2. A user-key payload reclaims the freed `struct channel`. The still-running RX
path treats controlled key bytes as its skb queue and waitqueue.
3. Queue list operations disclose a live skb pointer through the readable key
payload. A second controlled unlink uses `core_pattern` as disclosure
scratch and exposes a key-allocation pointer.
4. A 224-byte key payload becomes a persistent credential carrier. Repeating
the queue disclosure reveals the carrier's address.
5. Two additional pointer-source and unlink stages place a valid AppArmor cred
blob pointer and an unconfined fake label into the forged credential.
6. The reclaimed waitqueue entry calls `override_creds()` with the forged cred.
UID/GID fields, capabilities and the required user, namespace, ucount and
group pointers match the exact Ubuntu build.
7. The temporarily privileged execution context copies `/proc/self/exe` to a
unique file in `/tmp`, changes it to root-owned mode 4755 and verifies those
attributes.
8. The original unprivileged task executes that SUID copy, obtains real UID/GID
0, removes the temporary helper and executes `/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.
## Build
The Makefile statically links the exploit. The timing-sensitive prefetch object
is deliberately compiled at `-O0`; the exploit itself uses `-O2`.
```sh
make
```
Equivalent commands:
```sh
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
```
## Run
From the ordinary user account on the exact validated kernel:
```sh
id
grep '^Cap' /proc/self/status
./CVE-2026-68398 auto 60
```
The source checks `uname()` and refuses any release other than
`5.15.0-187-generic`. The race is probabilistic; reboot a disposable snapshot
before retrying after a miss or crash.
A successful run ends with:
```text
[KASLR] image_base=0xffffffffb9200000 slide=0x38200000
CVE-2026-68398 Ubuntu local privilege-escalation exploit
uid=1001 euid=1001 slide=0x38200000
...
[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)
```
The complete concise transcript from that real QEMU run is in
[`docs/example-output.txt`](docs/example-output.txt).
## Reproduced results
The chain completed on three fresh, normally randomized Ubuntu boots. The
automatically recovered image bases were `0xffffffff90800000`,
`0xffffffffa5a00000` and `0xffffffffb9200000`. The final run used the dedicated
non-administrative UID 1001 account described above, and verified that the
temporary SUID file was removed afterward.
The same final race was also run for 60 seconds on the exact upstream fix
commit with KASAN enabled and the same four-vCPU topology. It completed 5,969
channel/key cycles, reported `stage 1: no controlled reclaim`, and produced no
KASAN report or exploit stage transition.
## Public-exploit check
Checked on **2026-08-12** before this private repository was created.
SearchSploit/Exploit-DB returned no exploit; Packet Storm returned no matching
file; GitHub exact-code search returned 22 CVE/advisory mirrors and no PoC; and
exact web searches found only the upstream fix and metadata. This is a
point-in-time result, not a guarantee that another PoC will not appear later.
## References
- [CVE-2026-68398 record](https://vulners.com/cve/CVE-2026-68398)
- [Mainline fix](https://git.kernel.org/stable/c/ec4215683e47424c9c4762fd3c60f552a3119142)
- [Original patch submission](https://patch.msgid.link/E793FCF2-58DE-4387-A983-C7B4BC3158BD@doyensec.com)
- [Ubuntu CVE tracker entry](https://git.launchpad.net/ubuntu-cve-tracker/plain/active/CVE-2026-68398)
- [KASLD](https://github.com/bcoles/kasld), whose prefetch component is the
basis for `kaslr_prefetch.c`
## Attribution and licensing
The exploit was written by **A. Ramos** `` (Twitter:
[@aramosf](https://twitter.com/aramosf)). Vulnerability discovery and the
upstream fix are credited to **Norbert Szetei (Doyensec)**.
`exploit.c` is GPL-2.0-only. `kaslr_prefetch.c` and `kaslr_prefetch.h` are MIT
licensed and retain the KASLD attribution and full permission notice in the
source files.