Skip to content

tcache house of spirit

Size'ı tcache aralığına düşen, kontrol ettiğin bir fake chunk'a bir pointer'ı free ederek, sonraki aynı-size malloc'un senin forge ettiğin region'ı döndürmesini sağlamak.

Mechanism

Note

_int_free'deki tcache free yolu, fastbin/smallbin yolundan çok daha kısadır. Size'ı geçerli bir tcache index'ine eşlenen bir chunk için, glibc mevcut chunk'ın alignment'ını ve (2.29'dan beri) key'ini doğrular, sonra tcache_put'u çağırır — next chunk'ın size'ını veya PREV_INUSE bit'ini kontrol etmeden. how2heap notunun ifadesiyle:

"tcache_put is called without checking if next chunk's size and prev_inuse are sane."

Bunu, fastbin'e karşı çalışan ve fastbin consolidation/size kontrollerinin geçmesi için takip eden chunk'ın sağlıklı olmasını gerektiren legacy house of spirit ile karşılaştır. Dolayısıyla tcache yolunda yalnızca tek bir forge edilmiş size alanına ihtiyacın var: fake_chunk->size'ı tcache bandına ayarla ve buffer'ına bir pointer'ı free() et. Free edilen "chunk" tcache freelist'ine push edilir ve bir sonraki istekte yeniden verilir.

Fake size üzerindeki tek kısıtlamalar: tcache kategorisine düşmeli (chunk.size <= 0x410; x64'te malloc arg <= 0x408) ve mmapped / non-main-arena flag bit'leri temiz olmalı. PREV_INUSE düşük bit'i tcache için yok sayılır. Free edilen pointer'ın gönderme yaptığı data region 16-byte aligned olmalıdır.

Walkthrough

shellphish how2heap tcache_house_of_spirit.c'den (glibc 2.35):

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main() {
    setbuf(stdout, NULL);

    malloc(1);                          // force tcache setup

    unsigned long long *a;              // attacker-controlled pointer
    unsigned long long fake_chunks[10] __attribute__((aligned(0x10)));

    // forge the size field. 0x40 -> tcache; PREV_INUSE bit is ignored,
    // but IS_MMAPPED (2nd lsb) and NON_MAIN_ARENA (3rd lsb) must stay clear.
    fake_chunks[1] = 0x40;              // chunk.size

    a = &fake_chunks[2];               // points at the fake chunk's user data (16-byte aligned)

    free(a);                           // pushes the fake chunk into tcache[0x40]

    void *b = malloc(0x30);            // 0x30 -> rounds to 0x40 -> returns the fake chunk
    printf("malloc(0x30): %p\n", b);
    assert((long)b == (long)&fake_chunks[2]);
}

Beklenen çıktı: malloc(0x30), &fake_chunks[2]'nin adresini yazdırır ve assert tutar — malloc, attacker'ın kontrol ettiği region'ı döndürdü.

Gerçek bir bug'da, fake_chunks yazabileceğin bir region'dır (bir global, free edilen pointer üzerinden ulaşılabilen bir stack buffer ya da başka bir heap objesi) ve a, programın free() etmeye kandırıldığı pointer'dır. malloc forge ettiğin region'ı döndürdüğünde, kontrol etmek istediğin bellekle overlap eden bir allocation'a sahip olursun.

Warning

malloc istek size'ı, yazdığın aynı internal size'a yuvarlanmalıdır (x64'te 0x300x380x40). Data pointer 16-byte aligned olmalı yoksa tcache_get'in aligned_OK kontrolü (malloc(): unaligned tcache chunk detected) tetiklenir. glibc 2.29+'ta yeni forge edilmiş bir chunk'ta key bulunmaz, dolayısıyla double-free kontrolü zararsızca geçer — ama fake region'ın tesadüfen tcache_key içerirse, free bin'i tarar.

Mitigation

  • glibc'nin tcache free yolu, hız için next-chunk doğrulamasını kasıtlı olarak atlar, dolayısıyla teknik güncel glibc'de hayatta kalır; forge-edilmiş-size gereksinimi tek yapısal kısıtlamadır.
  • aligned_OK (2.32+, Safe-Linking ile) forge edilmiş chunk'ların 16-byte alignment'ını zorlar.
  • Bağımlılık, attacker'ın kontrol ettiği bir free() hedefi artı yazılabilir bir size alanıdır; sahte free'yi önlemek (doğru pointer kaynağı / hardened allocator'lar) gerçek düzeltmedir.

References