Stack Canary
Stack Canary: A protection value inserted into a function’s stack frame to detect stack buffer overflows.
Stack Canary Protection Techniques
Stack Canary is a protection technique that places a random value on the stack when a function is executed and checks whether the value has been tampered with before the function ends.
When a buffer overflow occurs, the local variable area is generally overwritten and even SFP and Return Address are overwritten.
At this time, if a canary value is placed in front of the return address, the canary value is modified first before the return address is covered.
char buf[0x30];
read(0, buf, 0x100);
Because the above C language code receives more data than the size of the buf, Stack Buffer Overflow may occur.
If Stack Canary is not applied, the stack structure is as follows.
location content
| high address | Return Address |
| SFP | |
| buf | |
| low address |
When Stack Canary is applied, the stack structure is as follows.
location content
| high address | Return Address |
| SFP | |
| Canary | |
| buf | |
| low address |
In other words, to cover the Return Address beyond buf, the Canary value is covered first.
- If there is no Stack Canary
void vuln() {
char buf[0x30];
read(0, buf, 0x100);
}
push rbp
mov rbp, rsp
sub rsp, 0x30
lea rax, [rbp-0x30]
mov edx, 0x100
mov rsi, rax
mov edi, 0x0
call read
leave
ret
The above code receives as much as 0x100 input into buf, but the size of buf is 0x30.
Therefore, it can cover beyond buf to SFP and Return Address.
- If you have a Stack Canary
void vuln() {
char buf[0x30];
read(0, buf, 0x100);
}
push rbp
mov rbp, rsp
sub rsp, 0x40
mov rax, QWORD PTR fs:0x28
mov QWORD PTR [rbp-0x8], rax
xor eax, eax
lea rax, [rbp-0x40]
mov edx, 0x100
mov rsi, rax
mov edi, 0x0
call read
mov rax, QWORD PTR [rbp-0x8]
sub rax, QWORD PTR fs:0x28
je .L1
call __stack_chk_fail
.L1:
leave
ret
When Stack Canary is applied, the canary value is stored on the stack at the beginning of the function.
mov rax, QWORD PTR fs:0x28
mov QWORD PTR [rbp-0x8], rax
And before the function terminates, the canary value stored on the stack is compared with the original canary value.
mov rax, QWORD PTR [rbp-0x8]
sub rax, QWORD PTR fs:0x28
je .L1
call __stack_chk_fail
If the canary value has been tampered with, the __stack_chk_fail function is called and the program is terminated.
- Check Stack Canary
checksec ./vuln
Canary : ✓
If the canary is activated as above, the Stack Canary protection technique has been applied.
- Stack Canary Compilation Options
Stack Canary can be applied with gcc’s stack protector option.
gcc -fstack-protector -o vuln vuln.c
gcc -fstack-protector-all -o vuln vuln.c
gcc -fno-stack-protector -o vuln vuln.c
Option Description
| -fstack-protector | Apply Stack Canary to some dangerous functions |
| -fstack-protector-all | Apply Stack Canary to all functions |
| -fno-stack-protector | Disable Stack Canary |
- Stack Canary example code
#include <unistd.h>
void vuln() {
char buf[0x30];
read(0, buf, 0x100);
}
int main() {
vuln();
}
Let’s compile the above code with Stack Canary applied.
gcc -fstack-protector-all -o canary canary.c
And if you input for a long time, the canary value is modified and the program ends.
python3 -c 'print("A" * 100)' | ./canary
*** stack smashing detected ***: terminated
Aborted
This is because the value entered beyond buf covered the canary, and verification of the canary value failed when the function ended.
Stack Canary Operation
Stack Canary operates in the following order.
- The function is called.
push rbp
mov rbp, rsp
sub rsp, 0x40
- Retrieves the original canary value.
mov rax, QWORD PTR fs:0x28
- Stores the retrieved canary value on the stack.
mov QWORD PTR [rbp-0x8], rax
- The code inside the function is executed.
lea rax, [rbp-0x40]
mov edx, 0x100
mov rsi, rax
mov edi, 0x0
call read
- Compare canary values before the function ends.
mov rax, QWORD PTR [rbp-0x8]
sub rax, QWORD PTR fs:0x28
je .L1
- If the canary values are different, the program terminates.
call __stack_chk_fail
- If the canary values are the same, the function ends normally.
leave
ret
Stack Canary Bypasses
If Stack Canary is applied, the BOF attack that simply covers the return address will fail.
This is because the canary value is also covered before the return address is covered.
Therefore, to bypass the Stack Canary, you must find out the Canary value and cover the Return Address while keeping the existing Canary value as is.
payload = padding + canary + sfp + ret
For example, if the size of buf is 0x30 and a canary exists, the payload structure is as follows.
area value
| buf | padding |
| Canary | Original Canary Value |
| SFP | dummy |
| Return Address | desired address |
In other words, if the Return Address is covered without knowing the Canary value, the program is terminated.
However, if you know the canary value, you can configure the payload as follows.
payload = b"A" * 0x30
payload += canary
payload += b"B" * 0x8
payload += ret
The above payload can pass the canary test because it inserts the canary value as is without modifying it.
Stack Canary Leak
To bypass a Stack Canary, a Canary Leak is generally required.
Canary Leak is finding out the canary value through the program’s output function or format string vulnerability.
For example, let’s say you have the following code:
#include <stdio.h>
#include <unistd.h>
void vuln() {
char buf[0x30];
read(0, buf, 0x100);
printf("%s", buf);
}
int main() {
vuln();
}
If you input buf so that there are no null bytes, printf can print up to the stack value after buf.
At this time, if a canary value is output, BOF can be performed using that value.
Canary generally has the last 1 byte as NULL.
00 ?? ?? ?? ?? ?? ?? ??
Therefore, when leaking a canary with a string output function, it may not be output immediately because of the NULL bytes.
In this case, a method can be used to fill in the front of the canary and then cover the NULL bytes to output part of the canary behind.
Limitations of Stack Canary
Stack Canary is a protection technique that detects return address tampering.
However, it does not prevent all attacks.
Limitations Description
| Canary Leak | An attacker can bypass the protection after learning the canary value. |
| Non-control-data Attack | Attacks that do not touch the return address are difficult to detect. |
| Heap Overflow | Non-stack heap overflows cannot be directly protected. |
| Format String Bug | Canary values can be leaked |
| Stack Pivot | Can be combined with other attack techniques depending on conditions |
In other words, Stack Canary makes BOF attacks difficult, but cannot completely prevent them.
Compiling and Running with Stack Canaries
Write vulnerable C language code to check the stack canary.
Below is the code with Stack Canary applied and the code without Stack Canary applied.
#include <unistd.h>
void vuln() {
char buf[0x30];
read(0, buf, 0x100);
}
int main() {
vuln();
}
Compile with stack canary protection enabled.
gcc -fstack-protector-all -o canary_on canary.c
Compile with Stack Canary disabled.
gcc -fno-stack-protector -o canary_off canary.c
Check both binaries with checksec.
checksec ./canary_on
Canary : ✓
checksec ./canary_off
Canary : ✘
Let’s input a long input into a binary with Stack Canary applied.
python3 -c 'print("A" * 100)' | ./canary_on
*** stack smashing detected ***: terminated
Aborted
Let’s insert a long input into the binary without Stack Canary applied.
python3 -c 'print("A" * 100)' | ./canary_off
Segmentation fault
When Stack Canary is applied, canary verification fails and __stack_chk_fail is called.
If Stack Canary is not applied, the return address may be altered and a segmentation fault may occur.
Summary
Stack Canary is a protection value inserted before the Return Address to detect stack buffer overflow attacks.
When the function starts, the canary value is stored on the stack, and when the function ends, it is compared with the original value.
If the canary value has been tampered with, __stack_chk_fail is called and the program is terminated.
However, if you can leak the canary value, you can bypass the protection technique by including the original canary value in the payload.