sysmalloc int_free¶
top chunk'ın size'ını corrupt ederek, heap'i büyütmenin
sysmalloc'a eski wilderness'ı_int_free()ettirmesini sağlamak ve hiçfree()çağırmadan neredeyse keyfi bir bin yerleştirmek.
Mechanism¶
Note
malloc bir isteği top chunk'tan karşılayamadığında, heap'i genişletmek
için (sbrk/mmap üzerinden) sysmalloc'u çağırır. Yeni bellek eski top
ile bitişik değilse — ya da eski top basitçe büyütülemiyorsa —
sysmalloc eski wilderness'ı yerinde yeniden kullanamaz. Bunun yerine onu
bir double fencepost ile mühürler ve kalanı _int_free (av, old_top, 1)
ile allocator'a geri bırakır.
glibc 2.35 sysmalloc'ta (malloc/malloc.c) ilgili kuyruk şöyledir:
if (old_size != 0)
{
old_size = (old_size - 2 * CHUNK_HDR_SZ) & ~MALLOC_ALIGN_MASK;
set_head (old_top, old_size | PREV_INUSE);
set_head (chunk_at_offset (old_top, old_size),
CHUNK_HDR_SZ | PREV_INUSE);
set_head (chunk_at_offset (old_top, old_size + CHUNK_HDR_SZ),
CHUNK_HDR_SZ | PREV_INUSE);
/* If possible, release the rest. */
if (old_size >= MINSIZE)
_int_free (av, old_top, 1);
}
Attacker'ın altüst ettiği anahtar invariant: top chunk'ın size'ı, bir
overflow'un ulaşabileceği inline heap metadata'sından okunur. sysmalloc
yalnızca corrupt edilmiş size'ın page-aligned kalmasını ister (yeni
brk'i türetmek için kullanılır) ve eski top'un _int_free'si
have_lock/is_top flag'i set edilmiş halde çalışır, dolayısıyla normal bir
free()'nin uygulayacağı size-sanity'yi atlar. Free edilen "old top",
senin seçtiğin bir size'a sahip bir unsorted/large bin'e düşer.
Bu, House of Orange ve House of Tangerine'in ardındaki motordur:
top-chunk size alanının tek bir lineer overflow'unu, sonradan reclaim edilebilen
(overlapping allocation'lar veren) ya da unsorted-bin attack üzerinden pompalanan
bir free edilmiş chunk'a dönüştürür — hepsi açık bir free() çağrısı olmadan.
Walkthrough¶
shellphish how2heap sysmalloc_int_free.c'den (glibc 2.27–2.39'da test edildi).
Plan: top-chunk size'ını küçült ki kalan wilderness bir sonraki istekten daha
küçük olsun, heap büyümesini zorla ve eski top'u free et.
#define CHUNK_HDR_SZ (sizeof(size_t)*2)
#define MALLOC_ALIGN 0x10
#define FENCEPOST (2*CHUNK_HDR_SZ)
#define CHUNK_FREED_SIZE 0x150 // size we want the freed bin to be
// 1. allocate a chunk that sits just below the top chunk
new = malloc(allocated_size);
size_t *top_size_ptr = &new[(allocated_size/sizeof(size_t)) - 1
+ (MALLOC_ALIGN/sizeof(size_t))];
// 2. linear overflow corrupts the top-chunk size: keep it PAGE-aligned
size_t top_size = *top_size_ptr;
size_t new_top_size = top_size & PAGE_MASK; // e.g. round down to 0x...000
*top_size_ptr = new_top_size;
// 3. request more than the (now smaller) top can give -> heap growth
old = new;
new = malloc(CHUNK_FREED_SIZE + 0x10); // triggers sysmalloc -> _int_free(old_top)
// the old top (minus fenceposts) is now a freed 0x150 chunk:
// freed_top_size == (new_top_size - FENCEPOST) & ~0xf == 0x150
Free edilmiş wilderness'ı reclaim etmek onu new ile overlap eder:
old = new;
new = malloc(CHUNK_FREED_SIZE - CHUNK_HDR_SZ); // FREED_SIZE
assert((size_t)old > (size_t)new); // allocated *below* old: into freed top
Beklenen çıktı: demo, sondalanmış top size'ını, corrupt edilmiş page-aligned size'ı yazdırır, sonra realloc adresinin eskiden top chunk olan region'ın içine düştüğünü assert eder — eski top'un free edilip yeniden kullanıldığını kanıtlar.
Warning
Corrupt edilmiş top size page-aligned kalmalıdır — sysmalloc onu brk
aritmetiğine besler ve page-aligned olmayan bir değer heap'i
desenkronize eder ve çöker. Düşük PAGE_MASK bit'lerini orijinaliyle aynı
tut; yalnızca yüksek bit'ler değişir. Free edilen size ayrıca iki fencepost
oyulduktan sonra old_size >= MINSIZE'ı da sağlamalıdır.
Detection¶
Corrupt edilmiş top-chunk size'ı tespit edilebilir: size'ı av->system_mem ile
bitişik olmayan ya da istekler arasında küçülmüş bir top chunk anormaldir.
glibc'nin kendi checktop/assert yolları ve malloc_check/AddressSanitizer
gibi araçlar tutarsız wilderness'ı işaretler.
Mitigation¶
- glibc zamanla top-chunk işlemeyi sıkılaştırdı (normal extend yolunda
malloc(): corrupted top size), amasysmalloc_int_freerotası modern glibc'de varlığını sürdürür çünkü free edilen old-topis_topolarak güvenilirdir. - Gerçek savunma, top size alanına ulaşan lineer overflow'u önlemektir — guard page'ler ve heap-içi canary'ler çıtayı yükseltir.