Skip to content

House of Tangerine

Top chunk'ın (wilderness) size'ını corrupt et; böylece sonraki oversized bir istek sysmalloc'un eski top'u unsorted bin/tcache'e free etmesine yol açar — hiç free() olmayan bir heap overflow'u leak + tcache-poisoning primitive'ine çevirir.

Mechanism

Invariant: sysmalloc heap'i genişletirken eski top chunk'ı free eder

House of Tangerine, House of Orange'ın tcache çağına yönelik "freeless" (free'siz) bir modernizasyonudur. Suistimal ettiği temel allocator davranışı sysmalloc'ta yaşar: bir istek mevcut top chunk'tan karşılanamayıp heap büyütüldüğünde (brk/mmap), glibc fencepost'ları yerleştirdikten sonra eski top chunk'ı _int_free aracılığıyla freelist'e geri koyar. Kullanıcı free()'sine gerek yoktur.

Bunu silaha dönüştürmek için attacker, top chunk'ın size field'ına overflow eder ve onu sysmalloc'un geçerlilik kapısını hâlâ geçen bir değere küçültür:

  • MINSIZE'dan büyük,
  • PREV_INUSE (bit 0) set,
  • page-aligned (0x1000'in katı) — aksi halde glibc sysmalloc tutarlılık kontrolü sırasında "corrupted top size" ile abort eder.

Sonra forge edilen top size'dan daha büyük bir istek heap'i büyümeye zorlar. sysmalloc yeni top'u oyar, eski bölgeyi fencepost'lar ve eski top chunk'ı free eder:

  • Free edilen size tcache/fastbin aralığına düşerse oraya gider; daha büyükse unsorted bin'e girer; orada fd/bk'sı artık bir main_arena pointer'ı tutar — chunk geri okunduğunda bir address-leak / libc leak primitive'i.
  • Kontrol edilen chunk'ları yeniden corrupt edip yeniden free etmek tcache poisoning verir: safe-linking'i yenmek için bir heap leak ile (glibc ≥ 2.32), attacker encryption key'i çözer, fd = target XOR (chunk_addr >> 12) ayarlar ve iki allocation sonra malloc keyfi bir adres döndürür.

glibc 2.34 ve 2.39 üzerinde test edildi (x86-64, x86, aarch64). free gerektirmediği için "no-delete" CTF/menu binary'lerine uygundur.

Walkthrough

shellphish/how2heap glibc_2.39/house_of_tangerine.c demosu (born0monday & Sir_X tarafından) yalnızca malloc çağrıları artı out-of-bounds write'lar (OOB) kullanır — free yok.

// Sizes chosen so the old top, once freed, becomes a 0x40 tcache chunk.
#define PAGESIZE       sysconf(_SC_PAGESIZE)
#define CHUNK_HDR_SZ   (2*sizeof(size_t))
#define CHUNK_SIZE_1   0x40

// 1) Allocate so our writable chunk sits adjacent to the top chunk header.
char *a = malloc(SIZE_1);          // a few setup chunks bring us up to `top`

// 2) OOB-write the top chunk size: shrink it, keep PREV_INUSE, keep page align.
//    *top_size_ptr = <page-aligned value | 1>;
//    e.g. reduce remaining wilderness to PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1

// 3) Request more than the forged top size -> sysmalloc grows heap and
//    _int_free()s the old top. With the right sizes it lands in tcache/unsorted.
char *grow = malloc(CHUNK_SIZE_3); // forces the old top into a bin

// 4a) LEAK: allocate back the unsorted-bin chunk and read its fd/bk (main_arena).
// 4b) POISON: OOB-write the freed tcache entry's fd. Safe-linking (>=2.32):
//     fd = target ^ (chunk_addr >> 12);   // needs a heap leak for chunk_addr>>12
//     malloc(); malloc();                 // 2nd malloc returns `target`

Footgun'lar

  • Forge edilen top size'ın page alignment'ı modern glibc'de zorunludur. Page-aligned olmayan bir top size sysmalloc'ta "corrupted top size"'a takılır.
  • Fencepost hesabı. sysmalloc fencepost'lar için iki chunk header (2*CHUNK_HDR_SZ, yani 0x20) çıkarır; free edilen eski-top size'ı forged_size - fencepost'tur, dolayısıyla hedef bin size'larını ham forge değerinden değil bundan hesapla.
  • Safe-linking (glibc ≥ 2.32). tcache fd'si chunk_addr >> 12 ile XOR-obfuscate edilir. Bir heap leak olmadan kullanılabilir bir fd forge edemezsin; 4a adımındaki unsorted-bin leak'i genellikle libc adresini sağlar ve ayrı bir heap leak (ya da alignment side-channel) heap base'ini sağlar.
  • free olmaması burada tcache e->key double-free kontrollerinin alakasız olduğu anlamına gelir — chunk allocator'ın kendisi tarafından free edilir.
Expected behavior
[*] old top freed by sysmalloc -> unsorted bin holds main_arena ptr
[*] leaked libc base: 0x7f....000
[*] poisoned tcache fd -> target
[+] malloc() returned attacker target: 0x... == &win

Detection

  • Page-aligned ve daha önce gözlemlenen wilderness'tan küçük bir top-chunk size'ının hemen ardından oversized bir istek gelmesi imzadır.
  • fd'si (safe-linking sonrası) heap-dışı bir adrese decode olan bir unsorted/tcache chunk'ı, bu primitive'in downstream'inde poisoning'e işaret eder.

Mitigation

  • glibc'nin sysmalloc "corrupted top size" kontrolü (top size aklı başında ve page-aligned olmalı) corruption yüzeyini sınırlar ama ortadan kaldırmaz.
  • Safe-linking (≥ 2.32) tcache fd forge'u kullanılabilir olmadan önce ek bir heap leak'i zorunlu kılar.
  • Out-of-line metadata allocator'ları ve son chunk ile top chunk arasındaki guard page'ler wilderness header'a overflow'u engeller.

References