Skip to content

Off-by-NULL / poison null byte

Bir sonraki chunk'ın size'ına tek bir NULL-byte overflow'u onun low byte'ını (ve prev_inuse bit'ini) temizler ve glibc'i canlı bir chunk üzerinden backward-consolidate etmeye kandırarak overlap oluşturur.

Mechanism

Birçok string routine'i bir byte fazla yazar — sonlandıran '\0'. O byte bir sonraki heap chunk'ının size field'ının en düşük anlamlı byte'ına düştüğünde, size'ı küçültür ve kritik olarak PREV_INUSE (low) bit'ini temizler. Allocator artık önündeki chunk'ın free olduğuna inanır. Sonraki bir free()'de glibc, chunk'ın önündeki prev_size field'ını kullanarak backward-consolidate etmeye çalışır — attacker'ın kontrol ettiği bir field — böylece hâlâ canlı bir allocation ile overlap eden bir bölgeyi unlink edip merge eder.

Note

Tek byte'lık write yalnızca bir bit kadar metadata'yı çevirir, ama bu glibc'in "kullanımdaki bir chunk komşularının inuse bit'leriyle sınırlanır" invariant'ını kırmaya yeter. how2heap'in poison_null_byte'ında off-by-one ((char *)victim2)[-8] = '\0'; yazar ve victim'in size byte'ını temizler. prev içine sahte bir 0x501 size'ı yerleştirilir, böylece poison'dan sonra chunk'ın kayıtlı size'ı ile bir sonraki chunk'ın prev_size'ı (0x500'e set edilmiş) uyuşur — glibc'in consistency check'ini geçer. Free etmek sonra backward consolidate eder ve overlapping chunk'lar üretir; bu da hâlâ allocate edilmiş prev2 üzerinde arbitrary read/write verir. Bu, house-of-einherjar'ın yalnızca-metadata kuzenidir (o, sadece bir NULL byte değil, tam bir pointer ister).

Walkthrough

Kanonik referans how2heap'in poison_null_byte.c'sidir:

// off-by-one NULL overflow: clear the low byte of victim's size field
((char *)victim2)[-8] = '\0';   // size 0x511 -> 0x500, PREV_INUSE cleared
// a fake size (0x501) was pre-placed and the next chunk's prev_size set to 0x500
free(victim);                   // backward consolidation over a live chunk
Build and run how2heap
$ git clone https://github.com/shellphish/how2heap
$ cd how2heap/glibc_2.31
$ make poison_null_byte
$ ./poison_null_byte
...
Now we malloc a chunk... it overlaps with prev2 (overlapping chunks)
Overlapping succeeded: we can now edit prev2 through a different pointer

Tam "fake size" sabitleri hedeflenen glibc sürümüne bağlıdır; how2heap'teki glibc_2.31 ve sonraki varyantlar bunları o sürümün prev_size-vs-size ve unlink check'lerini sağlamak için ayarlar.

Warning

Off-by-one, size field'ının low byte'ına isabet etmeli, dolayısıyla heap layout/alignment victim2'yi overflow eden buffer'dan tam 0x10 sonra yerleştirmeli. Yeni glibc, tekniğin etrafında ayarlanması gereken check'ler (corrupted size vs. prev_size, tcache key ve unlink integrity) ekledi — bir glibc sürümünden bayatlamış bir how2heap sabiti, başka bir sürümde basitçe malloc_printerr ile abort eder.

Detection

  • glibc'in kendi malloc_printerr check'leri ("corrupted size vs. prev_size", "free(): invalid next size") birçok naif denemeyi abort eder.
  • ASan, orijinal off-by-one'ı size 1'lik bir heap-buffer-overflow olarak raporlar.
  • String işleme kodunu len boyutlu buffer'lara len + 1 write'ları ve rezerve NUL byte'ı olmayan strcpy/strcat için denetle.

Mitigation

  • Buffer'ları terminator'ı içerecek şekilde boyutlandır; doğru sınırlarla strlcpy/snprintf tercih et. Altta yatan off-by-one'ı tedavi etmek kök düzeltmedir.
  • 1-byte'lık write'ı yakalamak için test sırasında _FORTIFY_SOURCE=2/3 ve ASan ile build et.
  • Güncel glibc, poison'ı overlapping chunk'lara dönüştürme çıtasını yükselten prev_size/size consistency ve unlink check'leri ekler.

References