Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

VsockDrop

VsockDrop-LPE-PoC.mp4

Unprivileged local privilege escalation in the Linux kernel io_uring zerocopy send path, reachable over AF_VSOCK. A managed-frag reference underflow frees a still-pinned page; it is reclaimed as /usr/bin/su page-cache and its .interp is rewritten. Data-only, no leak, no kernel execution.

Reported to security@kernel.org; fixed upstream as CVE-2026-53365. NVD rates it 5.5 as an availability issue. It should be 7.8 since it's an LPE.

No user namespaces needed, one static binary. Bug affects Linux 6.7 -> 7.0.10 (introduced in 6.7, fixed in 7.0.11). Tested on unpatched Ubuntu 22.04 HWE/24.04/26.04, Debian 13, Arch, and openSUSE Leap/Tumbleweed.

Root cause

io_uring attaches SEND_ZC fixed-buffer pages to an skb without taking a reference (__skb_fill_page_desc_noacc) and sets SKBFL_MANAGED_FRAG_REFS. io_uring owns the pages (they are FOLL_LONGTERM-pinned), so the network stack must never get/put them. skb_release_data() honours it:

bool skip_unref = shinfo->flags & SKBFL_MANAGED_FRAG_REFS;
if (skip_unref)
        goto free_head;                 /* skip __skb_frag_unref() */

On the vsock multi-skb path (payload > 64k) the flag is not honoured for the frags we own, so __skb_frag_unref() -> put_page() runs on a managed page. Since that frag never held a reference, the put_page does not balance anything, so it decrements the pin directly. One stray put_page per send.

Primitive

GUP_PIN_COUNTING_BIAS is 1024. After io_uring_register_buffers the page is at refcount = 1 + 1024; munmap() drops the mapped ref (leaving the pin) and sets _mapcount = -1, which also lets the page pass free_page_is_bad() later (the init_on_alloc bypass). The pin is now the only reference:

step _refcount note
register 1025 FOLL_LONGTERM pin added
munmap 1024 mapped ref dropped, _mapcount = -1
send × 1024 0 managed-frag underflow, 1/send -> page freed
su page-0 read reused freed PFN reclaimed as su page-cache (LIFO)

1024 is not tuned. It is the ABI constant, which is why a fixed count works across every kernel once the decrement is deterministic. The receiver drains exactly one send and acks before the next, so each submit produces exactly one skb (one underflow).

Exploitation

  1. Drain the pin to 0 -> the still-pinned page lands on the PCP freelist.
  2. A single cold pread of /usr/bin/su page 0 pops that PFN back (LIFO) as su's page-cache. The page is now aliased by both su and our fixed buffer.
  3. write_fixed/read_fixed (not a PTE, the buffer is unmapped) rewrite the PT_INTERP string in that page to our loader path.
  4. exec("/usr/bin/su") (setuid-root) -> kernel maps our loader as the interpreter and enters it with root creds -> root shell.

Build & run

make
./exploit 

Disclosure

Defensive research, disclosed upstream to security@kernel.org. Do not run on systems you do not own: it corrupts /usr/bin/su in page-cache, and on CoW filesystems the change can persist to disk.

About

Linux kernel LPE via an io_uring zerocopy page-refcount underflow over AF_VSOCK

Resources

Stars

43 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages