Freelist barrier objects¶
Kontrollü ve free edilmesi zor objeleri (genellikle
struct user_key_payload) önceden yerleştir; böylece bir freelist corruption sonrası per-CPU freelist, geçersiz pointer'lara dalıp crash etmek yerine bilinen-iyi bir chunk üzerinde sonlanır.
Mechanism¶
Bir double-free ya da freelist corruption sonrası per-CPU freelist, attacker'ın seçtiği ya da garbage next-pointer'lar içerebilir. Allocator amaçlanan chunk'ın ötesine pop ederse bozuk bir pointer'ı dereference eder ve panic atar. Bir "barrier", bozuk freelist'in unmapped memory'ye değil de bunların üzerine çözülmesi için yerleştirilmiş, kontrollü ve allocator açısından geçerli free chunk'lardan oluşan bir settir.
Note
Barrier, probabilistic bir corruption'ı deterministic bir corruption'a dönüştürür. Cache'i, freelist next-pointer'ları geçerli olan (çünkü onları allocator'ın kendisi yazmıştır) objelerle seed'leyerek, attacker bir sonraki allocation'ın kontrollü bir chunk'a düşeceğini ve asla bozuk state'e geçmeyeceğini garantiler. Bu, corruption primitive'inin üzerine eklenen bir reliability/anti-crash önlemidir — kendisi hiçbir şeyi bozmaz.
Warning
İyi bir barrier object'i tek bir syscall'da spray edilebilir, size-controllable, content-controllable olmalı ve alakasız kernel aktivitesi tarafından kendiliğinden free edilmemeli. struct user_key_payload (add_key üzerinden "user" key tipi) bu profile uyar: payload'ın yaşam süresini yalnızca attacker'ın kendi keyctl çağrıları yönetir, arka plan kernel aktivitesi onu free etmez.
REVOKE ve RCU-deferred free
keyctl(KEYCTL_REVOKE, ...) payload'ı senkron olarak free etmez; revoke (ya da update), eski payload'ı call_rcu() ile bir RCU grace period sonuna kadar ertelenmiş free için schedule eder (bkz. rcu-callback-uaf-read-kaslr-defeat). Grace period boyunca chunk hâlâ allocator açısından geçerli ve dereference edilebilir kalır — barrier'ın bel bağladığı tam da bu "geçerli ama kontrollü" penceredir. Walkthrough'daki immediate KEYCTL_REVOKE, chunk'ları bu deferred-free state'ine sokarak onları cache'e geri yönlendirilebilir hâle getirir; "explicit REVOKE'a kadar kalıcı" ifadesi, bu deferred timing'i kapsamayan bir basitleştirmedir.
Walkthrough¶
Google'ın CVE-2023-3390 için hardened (lts/cos mitigation) hedefinde hazırladığı kernelCTF write-up'ından, barrier keyring payload'larıyla kurulur (snippet, yayınlanan exploit.c'den uyarlanmıştır):
#define KEY_SPRAY_CNT 10
/* init user_keys for freelist barrier */
for (int i = 0; i < KEY_SPRAY_CNT; i++) {
keys[i] = add_key("user", name_buf, buf, 0x1f8,
KEY_SPEC_PROCESS_KEYRING);
keyctl(KEYCTL_REVOKE, keys[i], 0, 0, 0);
}
Yayınlanan exploit'ten ayrıntılar:
0x1f8(504-byte) payload'luadd_key("user", ...)birstruct user_key_payloadallocate eder; write-up'a göre payload kmalloc-1k'ye düşer (geçici preparse buffer ise daha küçük olankmalloc-512'dir).- Key'ler, allocator açısından geçerli chunk'lardan oluşan bir rezervuar olarak hazır tutulur.
- Bug per-CPU freelist'i bozduktan sonra, barrier chunk'larını free etmek/yeniden kullanmak cache'i yeniden seed'ler; böylece bir sonraki allocation kontrollü bir chunk'a çözülür — "used as freelist barrier, which prevents kernel crashes after percpu freelist is corrupted."
Hardened kernel'larda bu neden önemli
Bozuk bir freelist'in aksi takdirde panic atacağı kernel'larda (mitigation config'leri), barrier, tek seferlik bir crash ile güvenilir, tekrarlanabilir bir exploit arasındaki farktır. Teknik bu yüzden genellikle tek başına değil, cross-cache ve UAF primitive'leriyle birlikte kullanılır.
Mitigation¶
CONFIG_SLAB_FREELIST_RANDOM / CONFIG_SLAB_FREELIST_HARDENED deterministik barrier yerleşimini zorlaştırır; unprivileged add_key kotasını sınırlamak (/proc/sys/kernel/keys/maxkeys, user keyring limitleri), keyring payload'larını kullanışlı barrier yapan sprayability'yi azaltır.