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.
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.
hijacked kernel RIPcontrol seized in ring 0user-mapped payloadattacker's function in ring 3 memorycommit_creds(prepare_kernel_cred(0))current task becomes rootNo 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.
| Mitigation | Blocks | Attacker response |
|---|---|---|
| SMEP | Ring 0 executing user pages | Kernel ROP (reuse kernel code) |
| SMAP | Ring 0 reading/writing user pages | Keep all data in kernel memory |
| CR4 pinning | The mov cr4 disable trick | Pure 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 cr4shortcut 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.