Format-String Canary Leak¶
Bir
printf(user_input)format-string bug'ı,%p/%N$p'nin stack canary'sini okumasına izin verir; bu canary sonra bir overflow içinde replay edilir, böylece__stack_chk_failasla tetiklenmez.
Mechanism¶
Note
printf, argümanları format string'e göre çeker; string attacker-controlled
olduğunda ama eşleşen arg geçilmediğinde, her %p/%x stack'te bir sonraki
slot'u tüketir (x86-64'te önce register'lar, sonra stack). Positional %N$p,
N'inci variadic slot'u doğrudan okur ve canary'nin sabit offset'ini tam olarak
belirler. Canary tanınabilir, çünkü least-significant byte'ı 0x00'dır (string
overrun'larını durdurmak için bir null terminator), bu yüzden leak olan değer
"00 ile biter" ve libc/stack adreslerinden (genellikle 0x7f…) ayırt edilebilir.
Leak olduktan sonra, overflow orijinal canary'yi canary slot'una geri yazar;
epilogue onu reference TLS canary'sine karşı karşılaştırır, eşleşirler ve
__stack_chk_fail abort'u atlanır — stack-smashing protector'ı etkisizleştirir.
Walkthrough¶
%p spray ederek canary offset'ini bul; işlenmiş bir örnekte %23$p'de duruyordu
ve 0xcc987300 olarak leak oldu (sondaki 00'a dikkat). Onu overflow'da yeniden kullan:
canary = int(io.recvline().strip(), 16) # leaked via %23$p, ends in 00
payload = b'A' * 64 # fill buffer up to the canary
payload += p32(canary) # restore the exact canary -> passes the check
payload += b'A' * 12 # saved registers / EBP padding
payload += p32(target_function) # overwrite saved return address
Beklenen: program 0xcc987300 yazdırır; ikinci geçişte canary check'i sağlanır ve
execution target_function'a döner.
Detection¶
Leak'in kendisi sessizdir; overflow'dan sonra *** stack smashing detected ***
abort'unun yokluğu başarılı bir canary replay'ine işaret eder.
Mitigation¶
Bu, stack canaries'i etkisizleştirir — yalnızca
secret kaldıkları sürece çalışırlar. Bir format-string read primitive'i ve bir overflow
gerektirir ve canary process başına olduğu için, onu re-randomize eden herhangi bir
fork+exec'ten sonra yeniden leak edilmelidir (re-exec yapmayan bir forking server aynı
canary'yi korur, bu da bunu pratik kılar). Savunmalar: kullanıcı girdisini asla format string
olarak geçme (-Werror=format-security) ve FORTIFY_SOURCE=2 tehlikeli
%n'i kısıtlar. byte-by-byte canary brute force ve
stack canary leak ile karşılaştır.