| title | Kernel Exploitation | |||||||
|---|---|---|---|---|---|---|---|---|
| type | technique | |||||||
| tags |
|
|||||||
| phase | exploitation | |||||||
| date_created | 2026-06-16 | |||||||
| date_updated | 2026-07-14 | |||||||
| sources |
|
Exploiting bugs in the OS kernel or its drivers to execute code / escalate to ring-0 (root / SYSTEM). The highest-impact local privilege-escalation class and a major CVE-research target (syscalls, drivers, ioctls). Builds on userland [[binary-exploitation]] and [[memory-safety-bugs]].
The kernel runs fully privileged and trusts very little from userland - but a memory-safety or logic bug in a syscall, driver, or ioctl handler lets you corrupt kernel memory and redirect execution or overwrite a privileged structure. Modern kernels add mitigations (below) that shape which primitive you need.
Exploitation (local privilege escalation; driver/CVE research).
- Local code execution as a normal user. A debug setup: kernel + symbols (vmlinux / WinDbg), QEMU/VM, and (for research) a sanitizer-built kernel (KASAN) + a fuzzer.
- Linux: syscalls,
ioctlon/dev/*drivers, netlink, eBPF,io_uring, filesystems, and out-of-tree kernel modules. - Windows: IOCTL dispatch in kernel drivers (incl. BYOVD - bring your own vulnerable signed driver),
win32k.sys, and syscalls.
Kernel UAF, out-of-bounds R/W, double-fetch races (TOCTOU between two reads of user memory), type confusion, integer overflow, refcount over/underflow, and uninitialized-memory info leaks. See [[memory-safety-bugs]] for how to spot each.
ret2usr : redirect kernel exec to a userland payload (blocked by SMEP/SMAP)
kernel ROP : when SMEP on, chain gadgets in kernel .text (need a KASLR leak)
cred overwrite : commit_creds(prepare_kernel_cred(0)) -> uid 0
modprobe_path : overwrite /proc/sys ... modprobe_path -> run your script as root
heap spray : msg_msg / pipe_buffer / sk_buff / cross-cache for UAF & OOB control
Leak KASLR first (an info-leak bug or side channel), then place your primitive.
token stealing : overwrite EPROCESS.Token of your process with the SYSTEM token (PID 4)
arbitrary write: PreviousMode / HAL heap / PTE overwrite for R/W -> code exec
data-only : preferred under modern mitigations (kCFI/HVCI)
- Fuzz:
syzkaller(Linux syscall fuzzer, coverage-guided) on a KASAN kernel finds most kernel memory bugs; see [[fuzzing]]. - Practice/dev: HEVD (HackSysExtremeVulnerableDriver) for Windows/Linux exploit primitives; reproduce a public CVE in a VM.
- Patch route: diff a kernel/driver security fix ([[nday-patch-diffing]]) to locate the bug.
A cluster of 2026 Linux kernel LPEs sharing reusable primitives (kernelCTF / rafaeldtinoco / NebuSec research). Post-cutoff and single-source; verify CVE preconditions before relying.
Reusable primitives (portable across the bugs below):
- kernelCTF / libxdk workflow: clone
google/kernel-research, buildlibxdk, pull thekernelctf.kxdbtarget DB, then resolve struct-field offsets and ROP gadgets by name at runtime instead of hardcoding, so one exploit ports across kernel builds. - AF_PACKET
TPACKET_V3PGV page-vector spray plus deliberate hole-frees as a generic cross-cache heap-grooming primitive for UAF/OOB. - KernelSnitch: a timing side-channel over futex hash buckets to defeat KASLR with no info leak.
- Futex requeue-PI (
FUTEX_LOCK_PI+futex_wait_requeue_pi) race as a recurring on-stack-UAF source. - Page-cache-write family (Dirty Pipe lineage): make the kernel do an in-place write over the
page-cache copy of a read-only setuid binary or
/etc/passwd, bypassing DAC without touching the on-disk inode (see [[dirty-frag]] for the CopyFail / Dirty Frag / DirtyClone variants).
Specific bugs:
- CVE-2026-43499 (GhostLock): futex requeue-PI on-stack UAF; Android (Pixel) + Linux to root, KernelSnitch for KASLR, weaponized cross-device via a per-build NDK preload.
- CVE-2026-23274 (netfilter
xt_IDLETIMER): two same-label IDLETIMER rules of different type (v0 workqueue vs v1 alarmtimer) confuse the timer object; overwritetimer.function-> ROP root. - CVE-2026-43501 (IPv6 RPL SRH): CmprI/CmprE length miscalc on source-routing-header decompression -> heap OOB write; AF_PACKET PGV spray places the victim object.
- pidfd_getfd FD-theft (fix 31e62c2ebbfd, 2026-05-14):
do_exitrunsexit_mm(setsmm=NULL) beforeexit_files; in that window__ptrace_may_access()skips the dumpable check, so an unprivileged user callspidfd_getfd(2)in a tight loop to steal a setuid process's still-open FD (ssh-keysign host keys,chage->/etc/shadow). Detection:pidfd_getfd(NR 438) fromeuid != 0, especially at high call rate.
A second cluster (Project Zero / kernelCTF 2025) distinct from the 2026 bugs above. These contribute reusable primitives (a KASLR-free data path, an SKB cross-cache read/write, a timer race) more than one-off CVEs. Verify the config gate before relying on any of them.
The single highest-leverage primitive here: it removes the KASLR-leak stage entirely for data-only exploits on arm64 Android. Since commit 1db780bafa4c, arm64 anchors the linear map at a fixed VA and no longer randomizes it, so every physical page has a deterministic kernel VA independent of the KASLR slide:
// PAGE_OFFSET is compiled-in; PHYS_OFFSET = memstart_addr (0x80000000 on stock Android)
#define phys_to_virt(p) (((unsigned long)(p) - 0x80000000UL) | 0xffffff8000000000UL)Read memstart_addr (exported in /proc/kallsyms, or via any kernel-read primitive / Jann Horn's bpf_arb_read) to confirm PHYS_OFFSET. On Pixels the kernel physbase is also fixed (0x80010000 in /proc/iomem), so a .data symbol's linear-map VA is stable across reboots: virt = phys_to_virt(0x80010000 + (sym_virt - _stext_virt)). Where the physbase is randomized (Samsung), spray ~5 GiB of user pages, harvest PFNs from /proc/pagemap, profile which PFNs are reliably attacker-controlled across reboots, and convert those PFNs to linear-map VAs. The linear map is RW (but NX, so gadget hunting still needs a .text leak); any arbitrary-write primitive can then patch modprobe_path/init_cred/LSM ops arrays, or forge fake cred/file_operations in known-supervised pages and repoint victim pointers there.
A flawed manage_oob() refactor in Linux >= 6.9 lets two stacked zero-length SKBs bypass the u->oob_skb cleanup, so a normal recv() frees the out-of-band SKB while the pointer stays live. Config/version gate: CONFIG_AF_UNIX_OOB reachable and the fix 32ca245464e1 absent (check the commit, not the release string; vendors backport). Trigger is a short socketpair loop of send(MSG_OOB)/recv(MSG_OOB) then a plain recv() that frees the live OOB SKB. Two primitives fall out:
- 1-byte repeatable arbitrary read via
recv(MSG_OOB|MSG_PEEK)->unix_stream_recv_urg()->copy_to_user()(keepMSG_PEEKto preserve the dangling pointer). - Constrained write: without
MSG_PEEK,UNIXCB(oob_skb).consumed += 1at offset0x44becomes a +4 GiB increment of the 64-bit word at offset0x40.
The full chain shows the reusable SKB cross-cache technique: drain order-0/1 unmovable freelists with a page-table spray, isolate a skbuff_head_cache slab page, free it back to the buddy allocator, and realloc it as a pipe-buffer page so a forged sk_buff (fake data/head/skb_shared_info) turns the 1-byte read into a broad kernel read (breaks KASLR off the fixed CPU_ENTRY_AREA_RO_IDT IDT, walks pgd_list). Recycling the same page as a cloned thread's top kernel-stack page, a self-looping frag_list and an mprotect-vs-MADV_DONTNEED stall time the +4 GiB increment onto a spilled copy_page_from_iter() length, overflowing into an adjacent PTE page for arbitrary PTE writes (RWX kernel mappings, SMEP/SMAP disable). Mitigation: apply 32ca245464e1, gate with CONFIG_AF_UNIX_OOB, filter MSG_OOB/MSG_PEEK in seccomp (Chrome did in CL 6711812).
An expiry-vs-deletion race in kernel/time/posix-cpu-timers.c, gated on CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n (IRQ-context expiry path). handle_posix_cpu_timers() drops sighand via unlock_task_sighand() before firing collected timers; if the target task is exiting/reaping in that window, a sibling thread's posix_cpu_timer_del() fails its task lookup and skips the it.cpu.firing in-flight guard, corrupting timer state. Trigger: attach a CLOCK_THREAD_CPUTIME_ID timer with a 1 ns value/interval to a thread about to exit, hammer timer_delete() from a sibling, amplify with high tick rate and exit/re-create cycles. It is a reliable crash primitive; the Chronomaly PoC turns it into LPE on x86_64 5.10 via a cross-cache struct sigqueue UAF reallocated onto a pipe-buffer page (needs a quiet allocator to land the replacement). Fix: early if (tsk->exit_state) return; gate, or prefer CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y.
Heap out-of-bounds write in the in-kernel SMB server (ksmbd) alternate-data-stream xattr handling; reachable by an authenticated SMB client writing a named stream, giving a remote kernel memory-corruption primitive on hosts exposing ksmbd. Pairs with the AF_PACKET/SKB grooming primitives above for object placement. Treat any ksmbd-exposing host as remotely kernel-attackable and patch to the fixed release.
- SMEP/SMAP (no exec/access userland from kernel) -> kernel ROP / data-only.
- KASLR/kASLR -> need an info leak.
- KPTI (page-table isolation) -> use a signal-return / trampoline approach.
- HVCI / kCFI / SMAP -> prefer data-only primitives (cred/token overwrite) over control-flow hijack.
Keep the kernel patched; enable SMEP/SMAP, KASLR, KPTI, stack protector, lockdown, kptr_restrict; Windows HVCI + driver blocklist (kills BYOVD); minimise loaded drivers; KASAN + syzkaller in development.
gdb+vmlinux / WinDbg, QEMU, syzkaller ([[fuzzing]]), [[pwntools]], HEVD, [[ghidra]] (driver RE). Builds on [[binary-exploitation]], [[memory-safety-bugs]]; LPE context in [[linux-privesc]] / [[windows-privilege-escalation]].