PoC Archive PoC Archive
High CVE-2026-43494 patched

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

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-06
Author / ResearcherKoshmare-Blossom (rewrite); original PoC by Aaron Esau (stong) / V12 Security Team
CVE / AdvisoryCVE-2026-43494
Categorybinary
SeverityHigh
CVSS ScoreN/A (not published by upstream sources reviewed)
StatusPoC
Tagslinux-kernel, lpe, double-free, use-after-free, rds, io_uring, page-cache-overwrite, x86_64, nasm, asm, local, root-shell
RelatedN/A

Affected Target

FieldValue
Software / SystemLinux kernel (RDS zerocopy send path + io_uring fixed buffers)
Versions AffectedKernels with CONFIG_RDS, CONFIG_RDS_TCP, and CONFIG_IO_URING enabled — see advisory
Language / Platformx86-64 NASM assembly, Linux
Authentication RequiredNo (local unprivileged user)
Network Access RequiredNo — 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):

  1. pin_cpu0 — pin the exploit to CPU 0 for deterministic timing (sched_setaffinity).
  2. find_suid — locate a candidate SUID-root binary on disk.
  3. backup_target — back up the target binary before corrupting its page cache.
  4. mmap an anonymous page plus guard page; register it as an io_uring fixed buffer (IORING_REGISTER_BUFFERS), adding 1024 FOLL_PIN references (GUP_PIN_COUNTING_BIAS).
  5. IORING_REGISTER_CLONE_BUFFERS clones the fixed buffer into a second ring (imu->refs = 2); a forked daemon process holds the second ring open to prevent premature unpin.
  6. steal_one_ref — perform up to 1024 failing two-page RDS zerocopy sends (second page mapped PROT_NONE) to trigger the double-drop once per send, stealing one pin reference from the target page each time.
  7. Once all synthetic references are drained, munmap frees the page cleanly through the normal path.
  8. pread on the SUID-root binary forces page-cache reallocation onto the just-freed physical frame; the stale io_uring fixed-buffer struct page * now aliases live page cache.
  9. IORING_OP_READ_FIXED through the dangling fixed buffer writes an embedded ELF payload (setuid(0) + execve("/bin/sh")) into the SUID binary’s page cache.
  10. 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.asm and Makefile in this folder.

1
2
make
./pintheft

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

ActionDetail
Primary fixApply 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 mitigationDisable/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.