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.
Security engineer writing about memory safety, exploit mitigations, sanitizers and fuzzing, from the secure-coder's side of the fence.
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.
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.
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.
Every exploit walks the same stages: bug, corruption, hijack, code execution, escalation. A defender's checklist of which mitigation stops each — across Linux, ARM, Windows and the kernel.
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.
How SafeSEH, SEHOP, ASLR, Control Flow Guard and hardware CET each close a Windows exploitation technique — what they check, how to enable them, and their limits.
Windows keeps a linked list of exception handlers on the stack. Overflow into one, point it at a pop-pop-ret, trigger a fault, and control is yours — the technique Windows made famous.
On AArch64 the return address lives in a register, not on the stack — until a non-leaf function saves it. Build the ARM ret2win in a lab and see where the saved link register sits.
Pointer authentication signs return addresses so a forged one faults; BTI forces indirect branches onto landing pads. How both work, how to enable and verify them, and their limits.
AArch64 gadgets end in ret, which branches to x30 — so the chain is threaded through the link register with ldp gadgets. Build a system("/bin/sh") chain, then watch PAC and BTI break it.
Sometimes you control just one pointer. A one-gadget is a single libc address that calls execve("/bin/sh") — if its register and stack constraints hold. Find one, check the constraints, and use it.
No libc leak, no problem: forge the relocation the dynamic linker reads and make it resolve and call system for you. A pwntools walkthrough — and why Full RELRO ends it.
A seccomp policy that bans execve takes the shell off the table, so attackers switch goals: open the flag, read it, write it back. Build the ORW chain, and see what a tighter policy stops.
When hooks are gone, the FILE object is the target. A stdio call dispatches through a vtable pointer in writable memory — corrupt it and the next fwrite runs your code.
Modern exploits are leak-then-act. Build an out-of-bounds read, then use it to recover a stack canary, the PIE base and the libc base — the three secrets every mitigation relies on.
No pop rdx gadget? The C runtime's startup code has a universal sequence that loads several registers and makes a controlled call. Build it with pwntools, and see where it's gone.
When gadgets are scarce, forge a signal frame. A sigreturn syscall restores every register from the stack at once — build one with pwntools to call execve, then see how seccomp and CET respond.
When the overflow gives you only a few bytes past the return address, pivot the stack pointer into a buffer you fully control and run the real chain from there. A pwntools walkthrough.
One printf(user_input) is a full read/write primitive. Leak with %p, overwrite with %n via pwntools, then watch -Wformat=2 and FORTIFY_SOURCE shut it down.
Lazy binding leaves the Global Offset Table writable. Aim an arbitrary write at a GOT entry, turn puts() into system(), then enable Full RELRO and watch the same write fault instantly.
The heap's own metadata is the write primitive. Build a use-after-free, poison the glibc tcache to allocate a chunk over a chosen address, and see why safe-linking and ASan raise the bar.
A complete lab walkthrough: build a vulnerable C program, find the offset to the saved return address, redirect execution with pwntools, then watch each mitigation break the exploit.
NX is on and the binary is tiny, but libc is mapped and full of useful code. Leak its base past ASLR, then return straight into system("/bin/sh") — a full two-stage pwntools walkthrough.
With NX on, injected shellcode is dead — so we reuse the program's own code. A full lab walkthrough: find gadgets, hand-build an execve syscall chain with pwntools, then watch CET and CFI break it.
A staged, legal path from C and assembly to CTF pwn challenges: what to learn in which order, practice platforms built for it, lab rules, and how it leads to defensive careers.
Why memory-safe languages remove whole bug classes, what Rust's ownership model guarantees, where unsafe code and FFI remain risky, and how to harden the C++ you keep.
Write a fuzz harness, build it with sanitizers, run libFuzzer and AFL++, manage corpora and dictionaries, and run continuous fuzzing in CI with OSS-Fuzz or ClusterFuzzLite.
Triage native crashes like a defender: what SIGSEGV and SIGABRT mean, glibc abort messages, gdb and core dumps, ASan reports, WinDbg, and how to prioritise memory-safety crashes.
How ASan, UBSan, MSan, TSan and HWASan work, which flags and runtime options to use, how to read an ASan report line by line, and how to run sanitizers in CI.
How forward-edge and backward-edge control-flow integrity work, from Clang CFI and Microsoft CFG to Intel CET shadow stacks and Arm pointer authentication, and their limits.
What each classic exploit mitigation protects, the GCC and Clang flags that enable it, what it costs, and how to verify a binary with checksec and readelf.
How integer overflow, truncation and signedness errors turn into memory corruption, why user-controlled format strings are dangerous, and the checks and warnings that prevent both.
How heap memory bugs corrupt allocator metadata and object state, why use-after-free is so dangerous, and the allocator hardening, sanitizers and ownership rules that stop them.
Why writing past a stack buffer is dangerous, the C patterns that cause it, how compilers and sanitizers catch it, and the fixes and mitigations that contain it.
A defender's tour of a Linux process's address space: ELF segments, the stack and calling conventions, the heap, mmap, and why permissions and randomization matter.