
A Go implementation of dirtyfrag (CVE-2026-43284 / CVE-2026-43500)
Two bugs. One shell. No disk writes.
Go port of dirtyfrag (CVE-2026-43284 / CVE-2026-43500).
The bug class is in the Linux kernel XFRM subsystem. When a file page is splice()d into a pipe and then into an ESP-encrypted socket, the kernel decrypts the ESP payload in-place - directly into the page-cache page of the source file. The file is read-only. The page cache is not.
dirtyfrag chains two independent bugs exploiting this primitive:
CVE-2026-43284 - ESP/XFRM path
Installs 48 XFRM transport-mode SAs via NETLINK_XFRM. Each SA encodes 4 bytes of shellcode payload in the seq_hi field of its ESN replay state. A vmsplice + splice chain triggers ESP in-place decryption for each SA, writing those 4 bytes into the /usr/bin/su page-cache entry. 48 triggers, 192 bytes written, no race condition.
CVE-2026-43500 - RxRPC/rxkad path
Brute-forces 3 PCBC(fcrypt) session keys offline that produce the target byte sequences when decrypted. Fires an AF_RXRPC socket trigger for each key to patch the root entry in /etc/passwd to root::0:0 (nullok). su - then accepts an empty password.
Both paths run in a user + network namespace (CLONE_NEWUSER | CLONE_NEWNET) to avoid requiring CAP_NET_ADMIN on the host.
go build -o dirtyfrag-go .
./dirtyfrag-go
# Force a specific path
./dirtyfrag-go --force-esp
./dirtyfrag-go --force-rxrpc
# Verbose output
./dirtyfrag-go -v
The binary tries the ESP path first. If /usr/bin/su is not patched after, it falls back to the RxRPC path (up to 3 retries). On success it drops into a root shell via a fresh PTY.
The exploit only touches the page cache. The on-disk binaries and /etc/passwd are untouched. A reboot restores everything. To evict immediately without rebooting:
echo 1 | tee /proc/sys/vm/drop_caches
esp4, esp6 (ESP path) and/or rxrpc (RxRPC path)All kernels before the patches:
rmmod esp4 esp6 rxrpc
printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n' \
> /etc/modprobe.d/dirtyfrag.conf
Go's runtime is multi-threaded from the start, so unshare(CLONE_NEWUSER) is not allowed. The Go port re-execs itself as a child process with SysProcAttr.Cloneflags set, which calls clone() before the Go runtime initializes.
| C (original) | Go |
|---|
| External dependencies | none | none |
| Single file | no | no |
| Static binary | yes | yes |
| RxRPC path | yes | yes |
| ESP path | yes | yes |
| Namespace isolation | unshare() | re-exec with Cloneflags |