Skip to content

link_map / _dl_fini corruption

Bir link_map node'unu forge ya da corrupt et ki dynamic linker'ın exit-time finalizer walk'u attacker-controlled bir pointer'ı çağırsın.

Mechanism

Kırılan invariant

Normal process exit'inde, glibc'nin dynamic linker'ı her loaded object'in destructor'larını çalıştırır. _dl_fini() (elf/dl-fini.c) l_next'i takip ederek _rtld_global._dl_ns[ns]._ns_loaded'dan link_map list'ini gezer, objeleri toplar, sort'lar ve l_init_called set olan her map için finalizer'ları invoke eder. Modern glibc'de call _dl_call_fini()'de (elf/dl-call_fini.c) gerçekleşir; bu map->l_info[DT_FINI_ARRAY]'i okur, array'i map->l_addr + d_un.d_ptr olarak hesaplar, count'u DT_FINI_ARRAYSZ'den okur ve her entry'yi ters sırada bir function pointer olarak çağırır, sonra legacy DT_FINI entry'sini onurlandırır. Bu pointer'lar attacker'a ilgili link_map field'larından türetildiği için, onları corrupt etmek exit-time walk'u bir arbitrary call'a çevirir — House of Banana ailesi.

Walkthrough

Üst düzey, public House of Banana write-up'ından ve glibc source'undan:

  1. _rtld_global (loaded-objects list head'i) içindeki bir pointer'ı overwrite edebilen ve fake bir yapı sahneleyebilen bir write primitive elde et (write-up'lar genelde bir large-bin attack kullanır).
  2. Field'ları traversal'ı karşılayan ve l_info[DT_FINI_ARRAY] / DT_FINI_ARRAYSZ'i attacker-controlled bir function-pointer array'ine referans veren bir fake link_map hazırla.
  3. Fake node'u list'e splice et ki _dl_fini() onu ziyaret etsin.
  4. Process exit'ini tetikle (main'den return / exit); _dl_call_fini() forge edilmiş finalizer'ları çağırır, control'ü yönlendirir (genelde bir one-gadget'a ya da ROP'a bir stack-pivot'a).
Tutarlı kalması gereken field'lar

l_real fake node'un kendisine işaret eder (l == l->l_real check'ini geçer), l_init_called set, l_info[DT_FINI_ARRAY] geçerli bir Elf64_Dyn şeklindeki entry'ye işaret eder ve küçük, eşleşen bir DT_FINI_ARRAYSZ. Tam offset'ler çıkarılmıştır.

Uygulanabilirlik

_rtld_global ld.so içinde yaşar, dolayısıyla teknik bir libc/loader leak'i ister ama hiçbir IO/FSOP veya __free_hook gerektirmez. Kaynaklar glibc 2.23–2.36 boyunca uygulanabilirlik bildirir (setcontext-tabanlı ORW chaining'i glibc ≥ 2.29 ister).

Detection

  • Exit'te _rtld_global / link_map chain consistency'si (l_next / l_real) üzerinde integrity check'leri.
  • Large-bin pointer manipulation'ını flag'leyen heap-corruption detector'ları / hardened allocator'lar.
  • _dl_fini / _dl_call_fini'den non-text ya da heap adreslerine yönelen anormal control transfer'leri (CFI / telemetry).

Mitigation

  • CFI / CET shadow stack'ler + IBT ile forge edilmiş finalizer pointer'ların indirect-call validation'ında başarısız olması.
  • Full RELRO (GOT/relocation data'sını read-only yapar; linker-data tamper yüzeyini azaltır).
  • Write primitive'ini reddetmek için allocator hardening (glibc large-bin pointer check'leri, safe-linking).
  • Loader-internal sanity check'leri ve loader/glibc yapıları üzerinde ASLR.

References