Skip to content

overlapping chunks

Free edilmiş bir chunk'ın size field'ını bozmak, böylece sonraki bir malloc hâ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
İki pointer birbirinin write'larını raporlar; tek bir memory bloğunun iki bağımsız allocation üzerinden erişilebilir olduğunu doğrular.

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.

References