LPE via GRO managed-frag UAF This article describes a Linux privilege escalation exploit (CVE-2024-0582) that targets a use-after-free vulnerability in the Generic Receive Offload (GRO) subsystem. The bug occurs when `skb_gro_receive()` copies zero-copy (ZC) fragment descriptors into a non-ZC GRO accumulator without properly incrementing page reference counts, leading to a refcount underflow when the accumulator is freed. The exploit chains this vulnerability with io_uring SEND_ZC operations and AF_PACKET sockets to achieve arbitrary physical memory read/write, ultimately allowing an unprivileged user to gain root access on Linux kernels 6.0 and later. Created May 22, 2026 15:18 - - Save lcfr-eth/2566a5cef312c94a5ff8d62fa417955f to your computer and use it in GitHub Desktop. LPE via GRO managed-frag UAF This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters https://github.co/hiddenchars | / | | | gro frag.c — LPE via GRO managed-frag UAF io uring SEND ZC + veth | | | | | | The bug: skb gro receive copies frag descriptors from a ZC skb | | | SKBFL MANAGED FRAG REFS → no per-frag page refs into a non-ZC | | | GRO accumulator. When the accumulator is freed, skb release data | | | calls put page on each frag — including the stolen ones that never | | | had get page called. This gives us one extra put page per merged | | | ZC frag: a refcount underflow. | | | | | | Race window: between ZC notification page refs from GUP released | | | and GRO accumulator destruction put page fires , we clean up the | | | page's PTE and page-cache references. The vmsplice pipe reference | | | is the one "stolen" by the underflow — leaving a stale read handle | | | to a freed physical page. | | | | | | Exploitation: | | | 1. AF PACKET PACKET TX RING UNMOVABLE pages + vmsplice + io uring pin | | | 2. Fixed-buf SEND ZC → GRO merges managed frags into non-ZC seed | | | 3. munmap + unpin + close AF PACKET → pages freed to UNMOVABLE PCP | | | 4. pipe B writes 8 bytes → grabs freed page from UNMOVABLE PCP CAN MERGE | | | 5. pipe A read → put page frees pipe B's page back to UNMOVABLE PCP | | | 6. mmap /etc/passwd at 2MB-aligned slot → touch → PTE page allocated | | | from UNMOVABLE PCP → IS pipe B's freed page dirty pagetable | | | 7. tee pipe B → read PTE 0 → extract /etc/passwd PFN | | | 8. CAN MERGE write to pipe B → crafted PTE 1 RW, same PFN | | | 9. Write through crafted PTE → hardware walk bypasses VMA check | | | 10. su hax → root | | | | | | Affected: Linux 6.0+ unprivileged, requires io uring | | | Fixed by: 4db79a322db8 "net: gro: don't merge zcopy skbs" | | | | | | Tested: Ubuntu 24.04 | | | Compile: gcc -Wall -O2 -o gro lpe gro lpe.c -static -lutil | | | / | | | define GNU SOURCE | | | include