Skip to content

ret2usr, SMEP and SMAP

The kernel once trusted userspace memory, so exploits just pointed kernel execution at a user payload. SMEP and SMAP ended that — and how kernel ROP works around them.

Published on 4 min read

This is the second walkthrough in the Kernel Exploitation area. The first guide hijacked kernel control flow and ran a privilege-escalation payload. It quietly relied on the kernel being willing to execute code sitting in userspace. Two CPU features — SMEP and SMAP — ended that, and this guide explains the technique they broke and how kernel ROP works around them.

Same rules: revertible VM, your own module. See the lab rules.

ret2usr: the kernel trusting your memory

Historically the kernel was mapped into the same address space as the running process, and nothing stopped ring 0 from jumping into a user page. So the easiest kernel exploit was return-to-user (ret2usr): place your commit_creds(prepare_kernel_cred(0)) payload in your own userspace memory, then hijack the kernel's instruction pointer to jump straight to it.

ret2usr — the kernel executes a payload mapped in userspace
hijacked kernel RIPcontrol seized in ring 0
jumps to
user-mapped payloadattacker's function in ring 3 memory
runs with ring 0 privilege
commit_creds(prepare_kernel_cred(0))current task becomes root
codewritable memory

No kernel gadgets, no kernel-code leak — the payload is your own C function. This was the standard kernel-exploitation shape for years.

SMEP: no executing userspace from ring 0

SMEP (Supervisor Mode Execution Prevention), a CPU feature enabled via a bit in CR4, makes ring 0 fault the instant it tries to execute a user page. ret2usr dies immediately: the kernel can no longer run your user-mapped payload.

The answer is the same as when NX killed shellcode in userland: stop executing your own pages and reuse the kernel's. Build the whole payload as a kernel ROP chain (the chain in the first guide is already this shape). Gadgets come from the kernel image; the chain calls prepare_kernel_cred and commit_creds directly.

The CR4 shortcut

A famous shortcut: find a gadget like mov cr4, rdi ; ret, set rdi to a CR4 value with the SMEP bit cleared, and execute it. SMEP is now off and ret2usr works again:

pop rdi ; ret        -> 0x6f0    ; a CR4 value with SMEP (bit 20) cleared
mov cr4, rdi ; ret               ; disable SMEP
<address of user payload>        ; ret2usr now succeeds

Modern kernels defend this with CR4 pinning (CONFIG_X86_PIN_CR4 era hardening): security-critical CR4 bits are cached and any attempt to clear them is detected, so the shortcut faults or is reverted.

SMAP: no reading userspace either

SMAP (Supervisor Mode Access Prevention) extends the idea to data: ring 0 cannot read or write user pages either, unless it explicitly brackets the access with stac/clac. This blocks exploits that left fake structures, argument arrays or a stack-pivot target in userspace for the kernel to consume.

With SMAP on, everything the kernel must read during the exploit has to live in kernel memory. Techniques adapt: spray the needed data into kernel heap objects, or build a pure kernel ROP chain whose operands are all on the kernel stack you overflowed.

MitigationBlocksAttacker response
SMEPRing 0 executing user pagesKernel ROP (reuse kernel code)
SMAPRing 0 reading/writing user pagesKeep all data in kernel memory
CR4 pinningThe mov cr4 disable trickPure kernel ROP without disabling SMEP

Turn the mitigations back on

SMEP and SMAP are enabled by default on any modern kernel and CPU (nosmep/nosmap boot flags disable them — never do that in production). Confirm they are active:

$ grep -o 'smep\|smap' /proc/cpuinfo | sort -u
smap
smep

Combined with a kernel stack canary and KASLR, they turn "jump to my payload" into "find a leak, then build a kernel ROP chain that never touches userspace" — dramatically more work.

What this teaches a defender

  • SMEP/SMAP are the kernel's NX/DEP moment. They ended the trivial ret2usr exploit and forced attackers into kernel ROP, exactly as NX forced userland ROP. Ensure they are on (they are, by default) and never boot with nosmep/nosmap.
  • CR4 pinning matters. It closes the cheapest SMEP bypass. It is standard in modern kernels; do not run ancient ones where it is absent.
  • Attack surface again. Kernel ROP still needs a bug and usually a leak. Reducing reachable drivers and syscalls (seccomp, lockdown) keeps both out of reach.

Key takeaways

  • ret2usr pointed hijacked kernel execution at a userspace payload — trivial while the kernel trusted user memory.
  • SMEP faults on ring-0 execution of user pages; SMAP faults on ring-0 access to user data.
  • The response is kernel ROP: reuse kernel code and keep all data in kernel memory, just like userland ROP after NX.
  • CR4 pinning blocks the mov cr4 shortcut that used to disable SMEP.

Next: KASLR, KPTI and modern kernel defenses, the address-hiding and isolation layers that sit on top of SMEP/SMAP.

Related guides

0x9000 · Kernel Exploitation

Kernel Exploitation: From a Bug to root

A kernel memory-corruption bug is about privilege, not a shell. The credential model, the commit_creds(prepare_kernel_cred(0)) payload, and returning cleanly to userspace — in a lab VM.

0x9000 · Kernel Exploitation

KASLR, KPTI and Modern Kernel Defenses

Kernel address randomization, page-table isolation, and the config options that harden a Linux kernel against memory-corruption exploits — what each one stops and how to enable it.

0xa000 · Windows Exploitation

Bypassing DEP on Windows with ROP

DEP makes stack shellcode unrunnable, so a Windows ROP chain calls VirtualProtect to mark the shellcode region executable, then jumps to it. Build it with mona, then see ASLR and CFG respond.