TL;DR
This guide shows how to bypass a stack canary protection by overflowing the return address on the stack, allowing you to redirect program execution. We’ll cover identifying vulnerable code, crafting an exploit payload, and executing arbitrary commands.
Prerequisites
- Basic understanding of assembly language (x86-64 preferred).
- Familiarity with the stack and function calls.
- A vulnerable program (we’ll assume a simple buffer overflow example for demonstration).
- Debugging tools like GDB or similar.
1. Identify Vulnerable Code
The first step is to find code susceptible to a stack buffer overflow. Look for functions that copy user-supplied input into fixed-size buffers without proper bounds checking.
// Example vulnerable C code
#include <stdio.h>
#include <string.h>
void vulnerable_function(char *user_input) {
char buffer[64];
strcpy(buffer, user_input); // No bounds checking!
}
int main() {
char input[256];
printf("Enter your input: ");
fgets(input, sizeof(input), stdin);
vulnerable_function(input);
return 0;
}
In this example, strcpy is dangerous because it doesn’t limit the number of bytes copied. If user_input is larger than 63 characters (plus a null terminator), it will overflow buffer.
2. Understand Stack Layout
Before crafting an exploit, you need to understand how data is arranged on the stack during function calls. Key elements include:
- Local Variables: Space for variables declared within a function (like
bufferin our example). - Saved Frame Pointer (SFP): Used to restore the previous stack frame.
- Return Address: The address of the instruction to execute after the function returns. This is what we’ll overwrite.
Use a debugger to inspect the stack layout when the vulnerable function is called.
3. Determine Offset to Return Address
Find the distance (in bytes) from the beginning of buffer to the return address on the stack. This offset will be crucial for crafting your payload.
- Debugging: Set a breakpoint inside
vulnerable_functionafter thestrcpycall. - Inspect Stack: Use GDB (or similar) to examine the stack pointer (e.g.,
rspin x86-64). Look for the return address – it will be located above the local variables on the stack. Calculate the difference between the buffer’s starting address and the return address.
For example, if the offset is 72 bytes, you need to fill 72 bytes with garbage data before overwriting the return address.
4. Craft the Exploit Payload
The payload will consist of:
- Padding: Enough bytes to reach the return address on the stack (determined in step 3).
- New Return Address: The address you want execution to jump to. This could be:
- The address of a shellcode injected into the program’s memory.
- The address of an existing function within the program (Return-Oriented Programming – ROP).
- A system call address (less common, requires careful setup).
// Example Python payload generation (assuming offset is 72 and shellcode address is 0x401122)
padding = b"A" * 72
return_address = b"x22x11x40x00x00x00x00x00"
payload = padding + return_address
# Send the payload to the program (e.g., using input() or a socket)
5. Execute the Exploit
Send the crafted payload as input to the vulnerable program.
- Run Program: Start the program and provide the payload when prompted for input.
- Monitor Execution: Use a debugger to observe execution flow. If successful, the program should jump to your specified return address (e.g., shellcode).
6. Stack Canary Mitigation
Stack canaries are random values placed on the stack before the return address. If the canary is overwritten during a buffer overflow, it detects the corruption and terminates the program.
To bypass this:
- Leak Canary: Find a way to read the canary value from memory (e.g., format string vulnerability).
- Include Canary in Payload: Prepend the leaked canary value to your payload before overwriting the return address. This will make the stack look as if no overflow occurred, allowing you to redirect execution.
Example:
// Assuming the leaked canary is 0x12345678
cunary = b"x78x56x34x12"
padding = b"A" * (offset - len(canary))
return_address = b"x22x11x40x00x00x00x00x00"
payload = canary + padding + return_address
Important Considerations
- Address Space Layout Randomization (ASLR): ASLR randomizes the base addresses of libraries and other program components, making it harder to predict addresses. Techniques like ROP are often used to bypass ASLR.
- Non-Executable Stack: Prevents execution of code directly from the stack. Shellcode injection may not work without bypassing this protection (e.g., using Return-Oriented Programming).

