Skip to content

exit handler hijack via PTR_MANGLE bypass

glibc'nin __exit_funcs listesinde encrypted bir entry forge ederek __run_exit_handlers'ın attacker kodunu çağırmasını sağla; PTR_MANGLE pointer guard'ını bir leak ya da sıfır/bilinen bir guard ile yen.

Mechanism

Note

atexit/__cxa_atexit ile register edilen fonksiyonlar exit_function_list node'larında saklanır — head __exit_funcs'tır ve initial bir blok libc'nin writable data/.bss'inde yaşar, her node 32 entry'ye kadar tutar. Saklanan her function pointer PTR_MANGLE ile korunur: register sırasında pointer, fs:0x30'da saklanan per-thread pointer guard ile rotate edilir ve XOR'lanır (rol reg,0x11 ; xor reg,fs:0x30 tarzı); exit() zamanında __run_exit_handlers listeyi ters yürür ve her entry'yi call etmeden önce demangle eder (ror reg,0x11 ; xor reg,fs:0x30). Güvenlik sınırı o guard'ın gizliliğidir. Eğer attacker guard'ı leak'leyebilirse (ya da guard sıfırsa — static-link bug'ı CVE-2013-4788), herhangi bir target adresi için geçerli bir ciphertext hesaplayabilir; böylece exit-function bloğuna yapılan tek bir arbitrary write, process teardown sırasında controlled code execution'a dönüşür. İlgili "House of Banana" varyantı, mevcut bir entry'yi düzenlemek yerine bütün bir fake node forge eder ve __exit_funcs'ı ona yönlendirir.

Walkthrough

Kavramsal mangle/demangle ilişkisi (x86-64):

encoded = rol(target ^ pointer_guard, 0x11)      /* registration */
target  = ror(encoded, 0x11) ^ pointer_guard      /* __run_exit_handlers */

Public writeup'lardan derlenen yüksek seviyeli adımlar:

  1. __exit_funcs'ı (export edilmemiş) bul — örn. bir libc leak'i üzerinden ya da __cxa_atexit içindeki RIP-relative lea'yı okuyarak.
  2. Bilinen bir mangled/plaintext çiftinden (örn. initial _dl_fini handler'ı) guard'ı kurtar: guard = ror(encoded, 0x11) ^ known_addr.
  3. İstenen target'ı yeniden mangle et: encoded = rol(target ^ guard, 0x11); entry'nin flavour'unu (ef_cxa/ef_on) ve argument field'larını uygun şekilde ayarla.
  4. Mevcut bir handler entry'sini forge edilmiş değerinle overwrite et (ya da House-of-Banana tarzı, __exit_funcs'ı tamamen controlled bir fake node'a işaret ettir).
  5. __run_exit_handlers'ı tetiklemek için exit()'e ulaş / main'den return et.

Warning

__exit_funcs'ın, initial bloğun ve fs:0x30'un offset'leri glibc ve architecture'a göre değişir; target libc'ye karşı doğrula. Modern glibc (2.34+), exit'e ulaşmak için sık kullanılan malloc/free hook'larını kaldırdı, ama exit-handler path'inin kendisi kalır.

Detection

  • __run_exit_handlers'dan .text/PLT dışına düşen indirect call.
  • Demangle edilmiş target'ı loaded bir modülün koduna işaret etmeyen exit-function entry'leri ya da heap'e işaret eden __exit_funcs.
  • Runtime'da .bss/data initial exit bloğuna veya TCB fs:0x30'a yapılan write'lar.

Mitigation

  • Pointer guard'ın randomize edildiğinden emin ol (zero-guard static path'i CVE-2013-4788'de düzeltildi); predictable bir guard'la bir build asla ship'leme.
  • ASLR artı agresif info-leak önleme, guard'ı kurtarmanın maliyetini yükseltir.
  • Exit-handler indirect call üzerinde CFI / -fcf-protection; sonraki glibc çalışmaları bu struct'ları korumaya doğru ilerliyor.

References