EXPLOIT-DEV · LESSON 03

Memory Protections

20 min

Learning Objectives

  • Explain what DEP/NX prevents and how it changes exploitation
  • Explain how ASLR randomizes memory layout and what it defeats
  • Explain how a stack canary detects (not prevents) an overflow
  • Identify which protection a given crash or binary's behavior suggests is present

Three Mitigations, Three Different Jobs

Modern exploitation is hard not because any single defense is unbreakable, but because DEP, ASLR, and stack canaries each close a different door, and a working exploit has to deal with all three at once. None of them fixes the underlying bug (see Stack Overflows) — they make turning the bug into control much harder.

DEP / NX — Data Isn’t Executable

DEP (Data Execution Prevention, called NX — “No-eXecute” — at the hardware level) marks memory pages as either writable or executable, never both. The stack and heap are writable, so under DEP they’re not executable. Shellcode from e2, sitting in a stack buffer, simply can’t run — the CPU raises a fault the instant RIP tries to fetch from a non-executable page.

This is exactly why Return-Oriented Programming exists: if you can’t inject new executable code, you reuse code that’s already marked executable — the program’s own instructions — and just choose which pieces run.

ASLR — Moving the Target

Address Space Layout Randomization randomizes the base addresses of the stack, heap, shared libraries, and (with PIE binaries) the executable itself, freshly on every run. An offset that pointed exactly at system() or at your shellcode on one run points at nothing predictable on the next.

ASLR defeats hardcoded addresses specifically. A ROP chain or a jump target built from a fixed address baked into the exploit simply won’t line up once ASLR is on — the attacker needs to first leak an actual runtime address (from a crash, an information-disclosure bug, or a partial overwrite) before any address-dependent payload can work. Note what ASLR does not do: it doesn’t stop the overflow itself, and it doesn’t make code non-executable — it’s purely about not knowing where things are.

Stack Canaries — Detection, Not Prevention

A stack canary (or “stack cookie”) is a random value the compiler inserts between the local buffer and the saved RBP/return address, written at function entry and checked just before ret:

[ return address ]
[ saved RBP       ]
[ canary          ]   <- checked before ret; overflow must pass through here
[ local buffer    ]

A linear overflow — the strcpy-style pattern from e1 — has to overwrite the canary before it can reach the return address. If the value at ret time doesn’t match what was stored at entry, the program calls an abort handler instead of returning. Critically, the canary doesn’t stop the write from happening — the buffer still overflows, memory still gets corrupted — it only stops the corrupted return address from being used, and only if the overflow is sequential and doesn’t know or guess the canary’s value.

Reading the Signal

Given a crash or a binary, each protection leaves a distinct fingerprint:

ObservationSuggests
Crash is SIGSEGV at a garbage RIP value with no abort message firstCanary absent, or you already leaked/matched it
Process calls __stack_chk_fail / aborts with a stack-smashing messageCanary present and it caught you
Shellcode placed correctly but execution faults immediately on entry to itDEP/NX active — the page isn’t executable
Same exploit offset works reliably but the target address is wrong every runASLR active — need a leak, not a fixed address

Real targets stack these together — a modern binary with a canary, NX, and ASLR all enabled forces an attacker to chain: leak an address to defeat ASLR, build a ROP chain to defeat NX, and avoid or leak the canary rather than blindly overwrite it.

Takeaway

DEP stops you from running injected code, ASLR stops you from knowing where anything is, and the canary just tells on you — none of them close the hole, they just raise the price of walking through it.