SLAB freelist hardening¶
SLUB free-list pointer'larını, pointer'ın kendi storage adresine anahtarlanmış per-cache random bir XOR ile gizler ve klasik freelist-overwrite heap primitive'ini kırar.
Mechanism¶
Note
CONFIG_SLAB_FREELIST_HARDENED, SLUB allocator'ının dahili free-list
metadata'sını sertleştirir. Invariant: freed bir slab object'inde gömülü
next-object pointer'ı asla plaintext olarak saklanmaz ve obfuscation key'i
pointer'ın yaşadığı yere bağlanır, böylece o word'ü üzerine yazabilen bir
attacker oraya seçtiği bir plaintext adresi koyamaz.
SLUB, singly-linked free list'ini freed object'lerin içinde saklar: bir free object'in ilk word'ü bir sonraki free object'in adresini tutar. Klasik bir heap exploit o word'ü (overflow veya use-after-free yoluyla) üzerine yazar, böylece bir sonraki allocation attacker'ın seçtiği bir adresi döner — bir arbitrary-write veya fake-object primitive'i.
Hardening, saklanan değeri iki secret ile dönüştürür:
s->random— per-kmem_cacherandom bir değer (get_random_long()), attacker tarafından bilinmeyen.ptr_addr— pointer'ı tutan slot'un adresi.
Encoding bir triple XOR'dur:
static inline void *freelist_ptr(const struct kmem_cache *s, void *ptr,
unsigned long ptr_addr)
{
#ifdef CONFIG_SLAB_FREELIST_HARDENED
return (void *)((unsigned long)ptr ^ s->random ^ ptr_addr);
#else
return ptr;
#endif
}
ptr_addr'ı dahil etmek incelikli kısımdır. Yalnızca ptr ^ random ile,
aynı key bir cache'teki her pointer'ı gizler, böylece tek bir {plaintext,
obfuscated} çiftinin sızması random'ı geri kazandırır ve attacker'ın
herhangi bir pointer'ı forge etmesine izin verir. Storage adresini karışıma
katmak, etkili key'i konuma bağımlı yapar: slot A'ya yazılan değer slot B'de
tekrar oynatılamaz ve forge edilmiş bir target, hem s->random'ı hem de
pointer'ın saklanacağı tam adresi bilmeyi gerektirir.
Walkthrough¶
Savunma mm/slub.c içinde yaşar. Per-cache key, allocator descriptor'ına
eklenir:
struct kmem_cache {
/* ... */
unsigned long random; /* only with CONFIG_SLAB_FREELIST_HARDENED */
};
/* at cache creation */
s->random = get_random_long();
Her freelist traversal/store, slot adresini geçirir ki XOR simetrik olsun (encode == decode):
/* get the decoded next pointer */
next = freelist_ptr(s, *(void **)ptr_addr, (unsigned long)ptr_addr);
/* store the encoded next pointer */
*(void **)ptr_addr = freelist_ptr(s, next_obj, (unsigned long)ptr_addr);
Çalışan bir kernel'de enabled olduğunu teyit edin:
Warning
XOR'da ptr_addr olmadan, bir obfuscated pointer'ın artı onun bilinen
plaintext target'ının tek bir info leak'i s->random'ı kolayca verir
(random = obf ^ ptr) ve tüm şemayı yener. Storage-address terimi,
attacker'ı ek olarak forge edilmiş pointer'ın nereye yazılacağını bilmeye
zorlayan şeydir.
Patch, ihmal edilebilir overhead raporladı — yazarın benchmark'ında ~%0.07 mertebesinde.
Note
Yukarıdaki snippet, orijinal 2017 patch'ini yansıtır (ptr ^ s->random ^
ptr_addr). Sonraki bir iyileştirme, ptr_addr terimini bir byte-swap ile
karıştırdı (ptr ^ s->random ^ swab(ptr_addr)), böylece numerik olarak
bitişik slot adreslerinin encoded pointer'lardan sızması daha da zorlaştı.
Ayrıntı için bkz. freelist-poisoning.
Detection¶
Exploitation açısından, hardening aktifken freed bir SLUB object'inin ilk word'ü
tanınabilir bir kernel pointer'ı yerine yüksek-entropili bir değer olacaktır.
Defansif olarak, struct kmem_cache içinde random'ın varlığı ve
/proc/config.gz'de config option için =y olması, mitigation'ın compile
edildiğini gösterir.
Mitigation¶
Bypass'lar ve sınırlamalar:
- Leak gerektirir. Hem
s->random'ı hem de target slot adresini leak edebilen bir attacker, geçerli bir obfuscated değer hesaplayabilir ve freelist-overwrite primitive'ini sürdürebilir; hardening çıtayı runtime info gerektirmeye yükseltir, primitive'i imkânsız yapmaz. - Yalnızca freelist pointer'ını korur. Diğer in-object veya bitişik slab metadata'sı ve non-freelist heap-grooming teknikleri dokunulmamış kalır. slab-freelist-randomization (free-list order'ını randomize eder) ve init-on-free, hardened-usercopy gibi komşu önlemlerle birlikte çalışır.
- Tamamlayıcı, tek başına değil. Defense in depth için slab-freelist-randomization ve init-on-alloc ile birleştirin.