Skip to content

Null-byte overflow

Komşu bir object'in size/length metadata'sına yapılan tek byte'lık (çoğunlukla NUL) off-by-one write, allocator'ın chunk sınırlarına dair görüşünü bozar; bu da overlapping allocation'lar ve arbitrary read/write primitive üretir.

Mechanism

Note

Allocator'lar bir buffer'ın hemen ardından gelen size/length field'ına güvenir. buf[len-1]'de durmak yerine buf[len]'e yazan bir loop, ya da sona terminating '\0''ı bir byte ileri ekleyen bir string rutini, sonraki object'in metadata'sının ilk byte'ını overwrite eder. Little-endian size field'ları en anlamlı byte'larını daha yüksek adreslerde tuttuğu için, en düşük byte'ı 0x00 ile clobber etmek bir size'ı yalnızca küçültür (örn. 0x511 -> 0x500) — allocator'ın reddetmediği, küçük ve "makul" görünen bir corruption. Bunun sonucunda allocator, bir chunk'ın nerede bitip diğerinin nerede başladığı konusunda gerçekle çelişir.

Kırılan invariant şu: bir chunk'ın kayıtlı size'ı, sonraki chunk'ın header'ına olan boşlukla eşleşmeli. Poison'lanmış bir null byte bunu sessizce ihlal eder, dolayısıyla sonraki bir free/coalesce hâlâ kullanımda olan memory'yi merge eder ve overlapping chunk'lar üretir.

Walkthrough

Klasik userland örneği shellphish'in how2heap poison_null_byte'ıdır (glibc). Şekli, özüne indirgenmiş hâliyle:

  1. Henüz hiçbir şey consolidate olmasın diye prev, victim ve bir barrier chunk olmak üzere komşu chunk'lar allocate et.
  2. Forge edilmiş bir "previous chunk"a erişilebilir olsun diye unsorted/large bin'leri kuracak şekilde free/relink yap.
  3. prev'in size'ını 0x10 kadar küçült ve boşluğu köprülemek için sonraki header'ın prev_size'ını ayarla:
/* off-by-one NUL lands on victim's size LSB: 0x511 -> 0x500 */
((char *)victim)[-8] = '\x00';
  1. victim'i free etmek, forge edilmiş previous chunk'a karşı backward consolidation'ı tetikler. fd/bk groom edildiği için unlink check geçer; böylece glibc büyük bir region'ı unsorted bin'e merge eder.
  2. Ardından gelen iki malloc artık overlapping pointer'lar döndürür — birinden write yapmak diğerinin içeriğini düzenler (ve dolayısıyla oraya denk gelen herhangi bir hassas pointer'ı da).
how2heap expected outcome
The new chunk and the victim chunk now overlap.
We can edit the contents of the victim chunk via the new chunk:
new chunk data: AAAAAAAA   victim chunk data: AAAAAAAA

Kernel'de aynı primitive slab object'lerine de uygulanır: bir kmalloc-N object'inin sonundan taşan bir-byte'lık overflow, komşu object'in ilk byte'larına denk gelir. O komşu bir length/count field ile başlıyorsa (elastik object'ler, msg_msg vb. için yaygın), bu field'ı küçültmek veya sıfırlamak sonraki read/write'ları desenkronize eder; uygun bir heap grooming sonrası komşu victim object'lere overlapping ya da oversized erişim verir.

Warning

Modern glibc (>= 2.29) chunksize(p) != prev_size (next_chunk) ve unlink integrity check'leri ekledi, dolayısıyla naive poison-null-byte başarısız olur; çalışan sürümler bunları sağlamak için bin pointer'larını önceden hazırlar (glibc'ye özgü how2heap varyantlarına bakın). Kernel'de hardened freelist check'leri ve CONFIG_SLAB_FREELIST_HARDENED benzer şekilde hangi komşu field'ın corrupt edilmesinin güvenli olduğunu kısıtlar.

Detection

ASan/KASAN bir-byte'lık OOB write'ı doğrudan işaretler. Allocator integrity assertion'ları (malloc(): corrupted ..., slab redzone/poison uyuşmazlıkları) bir sonraki free'deki desync'te tetiklenir.

Mitigation

  • Loop/copy'yi bounds açısından düzelt (off-by-one kök nedendir); length-aware string API'leri kullan.
  • Heap hardening: glibc unlink/size check'leri, CONFIG_SLAB_FREELIST_HARDENED, redzone'lar ve randomize edilmiş freelist'ler (bkz. freelist poisoning) maliyeti yükseltir.

References