PinTheft: RDS zcopy Refcount-Steal Double-Free LPE — Pure NASM Rewrite (CVE-2026-43494)
by Koshmare-Blossom (rewrite); original PoC by Aaron Esau (stong) / V12 Security Team · 2026-07-05
- Severity
- High
- CVE
- CVE-2026-43494
- Category
- binary
- Affected product
- Linux kernel (RDS zerocopy send path + io_uring fixed buffers)
- Affected versions
- Kernels with CONFIG_RDS, CONFIG_RDS_TCP, and CONFIG_IO_URING enabled — see advisory
- Disclosed
- 2026-07-05
- Patch status
- patched
Tags
References
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-05 |
| Last Updated | 2026-06 |
| Author / Researcher | Koshmare-Blossom (rewrite); original PoC by Aaron Esau (stong) / V12 Security Team |
| CVE / Advisory | CVE-2026-43494 |
| Category | binary |
| Severity | High |
| CVSS Score | N/A (not published by upstream sources reviewed) |
| Status | PoC |
| Tags | linux-kernel, lpe, double-free, use-after-free, rds, io_uring, page-cache-overwrite, x86_64, nasm, asm, local, root-shell |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | Linux kernel (RDS zerocopy send path + io_uring fixed buffers) |
| Versions Affected | Kernels with CONFIG_RDS, CONFIG_RDS_TCP, and CONFIG_IO_URING enabled — see advisory |
| Language / Platform | x86-64 NASM assembly, Linux |
| Authentication Required | No (local unprivileged user) |
| Network Access Required | No — local only |
Summary
This is a hand-written, dependency-free x86-64 NASM rewrite of the “PinTheft” Linux local privilege escalation exploit (originally published as PinTheft-go). It targets a refcount double-drop in the RDS zerocopy send path (rds_message_zcopy_from_user()), which is abused together with io_uring fixed/cloned buffers to steal page-pin references, free a page still mapped by a dangling io_uring bvec, force page-cache reallocation of that frame onto a SUID-root binary, and then use IORING_OP_READ_FIXED through the stale fixed buffer to overwrite that page cache with an embedded shell ELF payload — yielding a root shell. The asm source is heavily commented with a step-by-step chain summary at the top of the file.
Vulnerability Details
Root Cause
rds_message_zcopy_from_user() pins user pages one at a time via GUP. If a later page in a multi-page zerocopy send faults, the error unwind path drops the already-pinned earlier pages via put_page(). However, rds_message_purge() runs afterward and drops the same scatterlist entries a second time because the zerocopy notifier state (op_mmp_znotifier) was cleared while op_nents and the scatterlist itself were left populated — a double-drop / refcount underflow on the targeted page.
Attack Vector
Per the in-source chain summary (pintheft.asm header comment):
pin_cpu0— pin the exploit to CPU 0 for deterministic timing (sched_setaffinity).find_suid— locate a candidate SUID-root binary on disk.backup_target— back up the target binary before corrupting its page cache.mmapan anonymous page plus guard page; register it as anio_uringfixed buffer (IORING_REGISTER_BUFFERS), adding 1024FOLL_PINreferences (GUP_PIN_COUNTING_BIAS).IORING_REGISTER_CLONE_BUFFERSclones the fixed buffer into a second ring (imu->refs = 2); a forked daemon process holds the second ring open to prevent premature unpin.steal_one_ref— perform up to 1024 failing two-page RDS zerocopy sends (second page mappedPROT_NONE) to trigger the double-drop once per send, stealing one pin reference from the target page each time.- Once all synthetic references are drained,
munmapfrees the page cleanly through the normal path. preadon the SUID-root binary forces page-cache reallocation onto the just-freed physical frame; the staleio_uringfixed-bufferstruct page *now aliases live page cache.IORING_OP_READ_FIXEDthrough the dangling fixed buffer writes an embedded ELF payload (setuid(0)+execve("/bin/sh")) into the SUID binary’s page cache.- Executing the (now-poisoned) SUID binary drops a root shell; the tool prints a restore command on exit to repair the backed-up binary.
Impact
A local, unprivileged Linux user can obtain a root shell on any system with CONFIG_RDS/CONFIG_RDS_TCP and CONFIG_IO_URING enabled — no special capabilities or existing privileges required beyond the ability to run unprivileged code and access a SUID-root binary’s page cache.
Environment / Lab Setup
Target: Linux kernel with CONFIG_RDS, CONFIG_RDS_TCP, and CONFIG_IO_URING enabled
(confirmed default exposure noted on distributions shipping the RDS module,
e.g. Arch Linux, per the original PinTheft research).
Build:
nasm -f elf64 -g -F dwarf pintheft.asm -o pintheft.o
ld pintheft.o -o pintheft
(or simply: make)
Proof of Concept
PoC Script
See
pintheft.asmandMakefilein this folder.
| |
Running the binary drives the full pin-steal / free / page-cache-overwrite chain described above and, on success, drops a root shell; it prints the command needed to restore the backed-up SUID binary on exit.
Detection & Indicators of Compromise
Generic indicators (no vendor telemetry shipped with this repo):
- Unprivileged processes registering/cloning io_uring fixed buffers
(IORING_REGISTER_BUFFERS / IORING_REGISTER_CLONE_BUFFERS) shortly before or
after repeated RDS socket send() failures.
- High-frequency failing sendmsg()/sendto() calls on AF_RDS sockets from a
non-privileged process.
- A short-lived child process holding an io_uring ring fd open while the
parent munmaps and re-reads a SUID binary's file contents.
- Unexpected modification timestamps/content on SUID-root binaries not
accompanied by a package manager transaction.
Signs of compromise:
- Root shells spawned from processes with no corresponding privileged parent or sudo/pkexec audit trail.
- A
.backup_pintheft_<pid>-style temp file in/tmp(naming convention used by the underlying PinTheft family). - SUID-root binaries whose on-disk hash suddenly diverges from the package-manager-recorded hash.
Remediation
| Action | Detail |
|---|---|
| Primary fix | Apply the upstream kernel fix for the RDS zerocopy double-free / refcount-steal issue tracked as CVE-2026-43494 (patch the double-drop in rds_message_zcopy_from_user()/rds_message_purge() interaction); update to a kernel build that includes the fix. |
| Interim mitigation | Disable/blocklist the rds and rds_tcp kernel modules on systems that do not require RDS; restrict io_uring availability via seccomp or io_uring_disabled sysctl where operationally acceptable. |
References
Notes
Mirrored from https://github.com/Koshmare-Blossom/PinTheft-asm on 2026-07-05. This is a pure-assembly rewrite of the previously archived PinTheft RDS double-free PoC (see pocs/binary/2026-05-20_pintheft-rds-double-free/); this entry is catalogued separately as it is tracked against CVE-2026-43494 and ships as a distinct, dependency-free NASM implementation.