Vtable hijacking¶
Bir C++ object'inin vtable pointer'ını (ya da vtable'ındaki bir entry'yi) overwrite ederek bir virtual call'un attacker'ın seçtiği koda dispatch etmesini sağlamak.
Mechanism¶
Virtual method'ları olan bir class'ın her C++ object'i, offset 0'da gizli bir vptr taşır — class'ın vtable'ına bir pointer, yani her virtual method için bir tane olmak üzere function pointer'larından oluşan bir array. Bir virtual call, hedefi runtime'da çözer: object'ten vptr'ı okur, vtable'ı index'ler ve o pointer'ı çağırır:
"Every class with virtual methods has a hidden member variable: a pointer to a vtable, which is basically just an array of function pointers, one element for each of the class's virtual methods." — defuse.ca
Dispatch kabaca şuna derlenir:
vptr writable object belleğinde yaşadığı ve call ona körü körüne güvendiği için, vptr'ı bir fake vtable'ı gösterecek şekilde corrupt etmek, sonraki her virtual call'u attacker-controlled function pointer'ları üzerinden yönlendirir.
Note
vptr'a iki yol ulaşır. (1) Direct overwrite — bir buffer overflow veya
arbitrary write, object'in ilk 8 byte'ını ezer. (2) Use-after-free —
object'i free et, slot'unu attacker-controlled veriyle geri al, ve dangling
virtual call artık bir attacker vptr'ı okur (use-after-free,
heap-grooming-feng-shui). Fikir rix'in "Smashing C++ VPTRs" (Phrack 56)
yazısına dayanır.
Walkthrough¶
vptr'ı bir kardeş class'ın vtable'ına corrupt edilen minimal bir tip; böylece
zararsız bir call tehlikeli bir method'u çağırır (defuse.ca'nın
Greeter/CommandExecutor biçimi):
struct Greeter { virtual void sayHello(); char buf[0x40]; };
struct CommandExecutor{ virtual void execute(); /* runs system() */ };
Greeter *g = new Greeter();
/* vulnerability: overflow into g->buf overwrites g's vptr (first 8 bytes) */
overflow(g, /*new vptr=*/ &CommandExecutor_vtable);
g->sayHello(); /* dispatches through the swapped vtable -> execute() runs */
Tamamen attacker-controlled fake vtable (genel primitive):
void *fake_vtable[1] = { (void*)attacker_gadget }; /* in writable, leaked mem */
*(void**)victim = fake_vtable; /* set victim->vptr */
victim->any_virtual_method(); /* calls attacker_gadget */
Fake vtable bilinen bir adreste bulunmalıdır (dolayısıyla önceden bir
address-leak) ve attacker_gadget tipik olarak bir stack-pivot ya da bir
return-oriented-programming chain'ine devreden bir one-shot'tır.
Detection / Mitigation¶
Compiler/runtime CFI birincil savunmadır: Clang/GCC -fsanitize=cfi-vcall her
virtual call'da vtable'ın yasal bir tipe ait olduğunu doğrular; MSVC
Control Flow Guard ve VTable-protection (vtguard) da indirect hedefleri benzer
şekilde kısıtlar. Araştırma savunmaları (VTV "vtable verification", VTrust, VTable
interleaving, VTPin), bir vptr'ın static tip için geçerli bir vtable'ı gösterdiğini
zorunlu kılar. Hardware CET IBT her indirect hedefte bir endbr landing pad ister,
mid-gadget adreslerine jump'ları kırar. Heap hardening (segregated cache'ler, MTE,
quarantine), UAF reclaim yolunu zorlaştırır.