setcontext magic gadget pivot¶
glibc'nin
setcontext()'i, pointer'ını attacker'ın zaten kontrol ettiği birucontext_t'den tüm general-purpose register file'ını —rspveripdahil — yeniden yükler; böylece tek bir hijack edilmiş call'u tam bir register-control ve stack-pivot "magic gadget"ına çevirir.
Mechanism¶
Suistimal edilen invariant: setcontext, güvenilmeyen bir pointer'la beslenen, güvenilir ve eksiksiz bir register-restore rutinidir
setcontext(), userspace context switching'i implemente etmek için vardır: bir
ucontext_t'ye pointer alır ve saved machine state'i ondan geri yükler, saved
instruction pointer'a kontrolü aktararak biter. Kavramsal olarak, her callee/argüman
register'ını yapının sabit bir offset'inden yükleyen, saved stack pointer'dan yeni
bir rsp yükleyen, saved rip'i push'layan ve ona ret'leyen elle yazılmış bir
assembly rutinidir.
Bug class'ı setcontext'in kendisinde değildir — bir function pointer'ı (bir FILE
vtable slot'u, __free_hook, __malloc_hook, bir exit handler, bir GOT entry'si)
zaten corrupt etmiş bir attacker'ın, okuduğu pointer argümanını da kontrol
ederken control flow'u setcontext'in içine yöneltebilmesidir. O pointer'ı tutan
register glibc sürümüne göre değişir:
- eski glibc: context pointer
rdi'den alınır (entry point fonksiyonun başında). - glibc ≥ 2.29: yaygın kullanılan entry
setcontext+61'dir; context pointer'ırdx'te bekler (alias'lar:setcontext+61,setcontext+53, "magic gadget").
Rutin kontrollü yapıdan önce rsp'yi sonra rip'i yüklediğinden, tek bir
yönlendirilmiş call attacker'a hem pivot edilmiş bir stack hem de arbitrary bir
ilk rip verir — yani "bir indirect call ve bir register kontrol ediyorum"u "tüm
register file'ı ve stack'i kontrol ediyorum"a çevirir, bir ROP zinciri için fırlatma
rampası.
Walkthrough¶
Herkese açık House-of-Apple / angelboy tarzı FILE writeup'larını takip eden yüksek seviye yeniden üretim. Attacker'ın bir arbitrary write'ı ve bir indirect call ile biten kontrol edilebilir bir trigger'ı var.
-
Sahte bir
ucontext_thazırla, attacker'ın kontrol ettiği bellekte (genellikle forge edilmiş bir FILE yapısıyla aynı heap bölgesi).setcontext'inrspiçin (onu sahte bir stack / ROP zincirine yönelt) veripiçin (onu ilk gadget'a yönelt, örneğin birmov rsppivot'u veyasyscalltaşıyan gadget) okuduğu offset'leri doldur. Diğer register offset'leri syscall argümanlarını (rdi,rsi,rdx) hazırlamak için set edilir. -
Argüman register'ını ayarla. glibc ≥ 2.29'da
setcontext+61entry'si context pointer'ınırdx'ten okur — yani hijack'ten hemen öncerdxsahte context'e işaret etmelidir. Standart(*fp->vtable->slot)(fp)dispatch'indefprdi'ye geçer,rdxdeğil; dolayısıylardxiki yoldan biriyle ayarlanır: (a) hijack edilen call'dan önce çalışan,rdx'e kontrollü bir pointer yükleyen bir register-setting (mov rdx, .../pop rdx) gadget'ı, ya da (b) belirli House-of-Apple tarzı IO variant'ları — burada zincir, controlled argümanırdx'te taşıyan spesifik bir dolaylı call site'ına (örn. wide-data allocation path'lerindekicall [reg+off]formları) ulaşır. Hangi yolun geçerli olduğu glibc sürümüne ve seçilen IO path'ine bağlıdır ve hedef binary'de doğrulanmalıdır. -
Kontrolü setcontext'e yönlendir. Seçilen function pointer'ı (vtable slot / hook / handler)
&setcontext+61ile overwrite et. Program bir sonraki sefer o path'i tetiklediğinde,setcontextregister'ları yeniden yükler ve şimdi pivot edilmiş stack üzerinde duran attacker'ınrip'ineret'ler.
hijacked call --> setcontext+61 --> load rsp/rip/rdi.. from *rdx --> ret
(attacker-chosen registers + pivoted stack)
Neden bir one-gadget'a tercih edilir
Bir one-gadget, call site'ta sağlanması gereken belirli register/bellek
constraint'lerine ihtiyaç duyar ve bunlar sıklıkla başarısız olur. setcontext
constraint problemini tamamen atlatır: attacker, call-sonrası register state'ini
kontrollü yapıdan tanımlar, dolayısıyla tek bir hijack edilmiş call'u güvenilir
şekilde tam bir stack pivot'a ve ROP'a köprüler.
Detection¶
- Control-flow anomalisi: bir indirect call site'ın (FILE vtable dispatch'i, bir
libc hook'u, bir exit handler) normal caller'ları yerine
setcontext'in ortasına (setcontext+61/+53) transfer etmesi son derece anormaldir. CFI/CET shadow-stack ihlalleri veya__libc_messageabort'ları görünebilir. - Register/stack telemetrisi:
setcontext-sonrası execution'darsp'nin thread stack yerine heap'e veya başka bir yazılabilir data bölgesine (pivot edilmiş bir stack) işaret etmesi güçlü bir sinyaldir; core'larda/EDR backtrace'lerinde yakala. - Hook/vtable integrity:
__free_hook/__malloc_hookveya FILE vtable pointer'larının non-null/libc-aralığı-dışı hale gelmesini izlemek, pivot'u mümkün kılan öncül overwrite'ı yakalar. - Upstream memory-corruption primitive'ini
setcontext'e ulaşmadan yakalamak için hedefleri CI'da ASan/CFI altında çalıştır.
Mitigation¶
- Intel CET shadow stack, kontrol edilen
rip'eretadımını kırar: modernsetcontextshadow stack'i valide eder/kullanır, dolayısıyla forge edilmiş bir return hedefi bir control-protection fault'u tetikler. - glibc,
__free_hook/__malloc_hook'u kaldırdı (2.34) ve FILE vtable validation'ı (_IO_vtable_check) ekledi; bu dasetcontext'e inen bir hijack edilmiş call'a ulaşmanın yaygın yollarını daralttı. - glibc'yi güncel tut; CET (
-fcf-protection) ile derle ve mümkün olan yerde donanım shadow stack'i etkinleştir. - Asıl çözüm upstream'dedir: control flow'un yönlendirilmesine olanak tanıyan arbitrary write / pointer overwrite'ı ortadan kaldır.