Koshmare-Blossom/PinTheft-go

GitHub: Koshmare-Blossom/PinTheft-go

Stars: 0 | Forks: 1

# pintheft-go Go port of [pintheft](https://github.com/v12-security/pocs/tree/main/pintheft) - RDS zcopy double-free LPE. Member of the [Dirty Frag](https://github.com/V4bel/dirtyfrag) vulnerability class. ## How it works (tl;dr) The bug is in `rds_message_zcopy_from_user()`. The function GUP-pins user pages one at a time via `FOLL_GET`. If a later page faults (e.g. a `PROT_NONE` guard page), the error path calls `put_page()` on already-pinned pages, then `rds_message_purge()` calls `__free_page()` on them again because `op_mmp_znotifier` was NULLed but `op_nents`/sg entries were left intact. When the page still has other references, `__free_page` silently decrements the refcount. Each failing `sendmsg` steals exactly one ref from the first page. ### Bypassing `CONFIG_INIT_ON_ALLOC_DEFAULT_ON` Pin the target page via `io_uring REGISTER_BUFFERS`, which adds `GUP_PIN_COUNTING_BIAS` (1024) to the refcount through `FOLL_PIN`. Steal all 1024 pin refs with failing zcopy sends. The page refcount is now ~1 (just the PTE mapping). `munmap` takes the normal `__folio_put` path, which calls `mem_cgroup_uncharge` (clearing `memcg_data`) before freeing. No `bad_page` check fires. ### Dangling bvec write `io_uring` keeps the raw `struct page*` in its bvec array with no liveness checks. After the page is reclaimed as page cache for a suid binary, `IORING_OP_READ_FIXED` writes our payload into it through that dangling pointer. ### Preventing unpin on ring close `IORING_REGISTER_CLONE_BUFFERS` increments `imu->refs`. A forked daemon child holds the clone ring fd open - `io_buffer_unmap` sees `refs > 1` and skips `unpin_user_folio`, preventing refcount corruption on the freed page. ### PCP LIFO Pin to CPU 0, drain stale PCP entries before freeing - our page lands at the top when the page cache allocator grabs it. ## Exploit chain REGISTER_BUFFERS(+1024) -> CLONE_BUFFERS(imu->refs=2) -> daemon holds clone -> steal 1024 refs -> evict page cache -> drain PCP -> munmap(free) -> pread(reclaim) -> READ_FIXED(overwrite) -> verify -> exec -> root ## Usage go build -o pintheft-go . ./pintheft-go On success drops into a root shell via PTY. The on-disk binary is untouched. ### Cleanup sudo cp /tmp/.backup_pintheft_ /usr/bin/su && sudo chmod u+s /usr/bin/su ## Requirements - Linux kernel with unpatched RDS + io_uring - `CONFIG_RDS=m`, `CONFIG_RDS_TCP=m` (autoloaded via `SO_RDS_TRANSPORT=2`) - `CONFIG_IO_URING=y` with `io_uring_disabled=0` - Readable suid-root binary - No capabilities needed ## References - [v12-security/pocs - pintheft](https://github.com/v12-security/pocs/tree/main/pintheft) - original C PoC
标签:EVTX分析