overlapping chunks¶
Free edilmiş bir chunk'ın size field'ını bozmak, böylece sonraki bir
mallochâlâ canlı bir allocation ile overlap eden bir chunk döndürür ve attacker'a başka bir nesne üzerinde ikinci, aliasing yapan bir görünüm verir.
Mechanism¶
glibc'in ptmalloc'u her heap allocation'ını bir chunk olarak takip eder: size
field'ını tutan (low bit'leri PREV_INUSE, IS_MMAPPED, NON_MAIN_ARENA ile) bir
header ardından user data. Allocator bir sonraki chunk'ı tamamen aritmetikle bulur —
next = (char*)chunk + (chunk->size & ~0x7). Bir allocation'ın nerede bitip bir
sonrakinin nerede başladığına dair ayrı bir kayıt yoktur; size field'ı sınırın
ta kendisidir.
Note
Invariant, bir chunk'ın size field'ının onun kapsamını doğru biçimde
tanımlamasıdır, böylece ardışık chunk'lar memory'yi boşluk ya da overlap olmadan
döşer. Free edilmiş bir chunk unsorted bin'de otururken size'ı büyütüldüğünde,
allocator o bin chunk'ının, gerçekte ayrı, hâlâ allocate edilmiş bir chunk'ı da
içeren bir bölgeyi kapsadığına inanır. Şişirilmiş size'lı bir isteği karşılamak,
caller'a aralığı canlı chunk ile overlap eden bir pointer verir. İki pointer artık
aynı byte'ları aliasing yapar: biri üzerinden yapılan bir write diğeri üzerinden
görünür. Bu, tek bir heap-metadata corruption'ını, overlap edilen bölgeye
yerleştirmeyi ayarladığın hangi nesne olursa olsun (function pointer'lar, length
field'ları ya da diğer allocator metadata'sı) üzerinde bir arbitrary read/write'a
dönüştürür.
Overlap güçlüdür çünkü free-list corruption'ını hedefleyen allocator integrity check'lerini atlatır: hiçbir şey yanlış unlink edilmez. Yalan tamamen size'dadır ve şişirilmiş chunk bir kez yeniden allocate edildiğinde alias, nesnenin tüm ömrü boyunca kalıcı olur.
Sibling ile fark
Bu not main-arena ptmalloc chunk'larını hedefler: overlap, şişirilmiş bir
unsorted-bin chunk'ının yeniden malloc ile servis edilmesinden doğar.
mmap overlapping chunks ise IS_MMAPPED chunk'lara
özgü ayrı bir code path'tir — orada overlap, corrupt edilmiş size'ın free
sırasında munmap_chunk()'a iki bitişik mmap bölgesini tek seferde munmap'letmesi
ve serbest kalan adres alanının taze bir büyük mmap ile yeniden kullanılmasıyla
oluşur. Bin reallocation değil, address-space reuse söz konusudur.
Walkthrough¶
shellphish'in how2heap overlapping_chunks.c'si (glibc_2.31) kanonik demo'dur. Üç
chunk allocate eder, ortadakini unsorted bin'e free eder, ardından o free edilmiş
chunk'ın size'ını üzerine yazarak kendisinden sonraki chunk'ı yutar:
p1 = malloc(0x78); /* 0x80 chunk */
p2 = malloc(0x4f8); /* 0x500 chunk -- the one we will inflate */
p3 = malloc(0x78); /* 0x80 chunk -- the victim we want to overlap */
free(p2); /* p2 goes to the unsorted bin */
/* the overflow / arbitrary write: enlarge p2's size to cover p3 */
int evil_chunk_size = 0x581;
*(p2 - 1) = evil_chunk_size; /* p2-1 is the size field (size_t units) */
/* re-allocate the now-oversized region */
p4 = malloc(0x508); /* served from the inflated unsorted-bin chunk */
/* p4 now overlaps p3: writes through p4 land in p3's user data */
Yeniden allocation'dan sonra, p4 hem eski p2 bölgesini hem de p3'ü kapsar.
p4 üzerinden yazmak, programın hâlâ p3 üzerinden okuduğu byte'ları değiştirir ve
tersi de geçerli — demo, overlap'i kanıtlamak için tam olarak bu aliasing'i assert
eder.
overlapping_chunks.c'nin beklenen davranışı
The CHUNK_SIZE of p2 is changed to allow for overlapping
Now we malloc a new chunk p4 ...
p4 is inside the chunk we allocated as p3
Writing 0x42... through p4 ...
p3 now reads: BBBBBBBB... <- p3 sees data we wrote via p4
Writing 0x43... through p3 ...
p4 now reads: ...CCCCCCCC <- p4 sees data we wrote via p3
Warning
Seçilen evil_chunk_size (burada 0x581) PREV_INUSE bit'ini set tutmalı ve
aligned kalmalı, aksi halde free/malloc consistency check'leri abort eder.
Modern glibc ayrıca uygun yerlerde bir unsorted-bin chunk'ının bir sonraki
chunk'ının PREV_INUSE'unun unset olmasını zorlar — how2heap kaynağı bunu sıkılaştıran
bir yamaya dikkat çeker — dolayısıyla tam size'lar ve bin path'i glibc sürümüne göre
önemlidir.
Detection¶
Öncül, chunk metadata'sına yapılan bir out-of-bounds write'tır; ASan ve guard-page
allocator'ları bunu corruption anında yakalar. Olaydan sonra, glibc'in kendi
malloc(): ... integrity mesajları ya da mangle edilmiş bir chunk üzerinde
malloc/free içinde bir crash, metadata kurcalamasına işaret eder.
Mitigation¶
Size field'ını yeniden yazan heap overflow / arbitrary write'ı engelle (bkz.
heap-buffer-overflow ve
arbitrary-write-primitive). Hardened allocator'lar,
chunk'lar arası heap canary'leri ve _FORTIFY_SOURCE çıtayı yükseltir; test
build'lerindeki ASan overlap'i mümkün kılan write'ı doğrudan yakalar.