Skip to content

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_fail asla 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.

References