Exit Guard Bypass (Magic Value on Stack)
- •
What it is
- •
A stack variable controls whether a function returns or calls
exit(). A return-address overwrite only takes effect if this separate guard allows execution to reach the epilogue.
- •
- •
How it works
- •
The function reserves a local variable (uninitialized, or set to something the attacker's overflow will pass straight through) at a fixed stack offset, closer to the buffer than the canary.
- •
Find the comparison and follow both branches: one terminates the process, while the matching branch permits the normal epilogue.
- •
Place the required value at its verified offset and independently satisfy the canary and return-target requirements.
- •
[ buffer (attacker input) ] [ magic-value slot ] <- must equal the exact constant, or exit() [ canary ] <- must also be correct, separately [ saved rbp ] [ return address ] <- only reachable if BOTH checks above pass
- •
- •
When to use it
- •
If a payload reaches the expected slots but the process exits normally, inspect whether a separate guard leads to
exit()beforeret. - •
A harness warning that the function exits instead of returning points to a separate condition guarding the epilogue.
- •
Disassembly shows a
movabs-loaded 64-bit immediate compared against a local variable shortly before the function's normal exit path — the immediate itself is the magic value to supply.
- •
- •
Examples
- •
If a guard 112 bytes after the buffer must equal
MAGIC, placep64(MAGIC)there and separately preserve the canary before the return target.
- •
- •
pwntools
- •
payload = flat({guard_offset: p64(magic), canary_offset: p64(canary), return_offset: p64(target)}) # Offsets come from this binary.
- •
- •
Debugging
- •
disas challenge # Follow the guard comparison through exit() or ret.
- •
- •
- •
Cards
- •
Why does a correctly-built return-address hijack sometimes still fail to produce control-flow hijacking?
- •
A separate guard elsewhere in the function may call
exit()unconditionally unless an unrelated stack slot holds an exact expected value —exit()terminates the process before any return-address overwrite would ever take effect.
- •
- •
Where does the magic-value slot typically sit relative to the canary in these challenges?
- •
Before it — closer to the buffer — so any overflow reaching the canary already necessarily passes through (and must correctly satisfy) the magic-value slot first.
- •
- •