nf_tables limited UAF (CVE-2022-32250)¶
Set oluşturma sırasında stateful bir expression'ı bir set'e bind etmek, set'in
->bindingslistesine birnft_set_bindingkaydeder, ama validation-başarısız cleanup expression'ı unlink etmeden free eder — geride, sonraki bir expression'ın kontrollü bir UAF write'a dönüştürdüğü bir dangling list node bırakır.
Mechanism¶
nftables embedded expression'larla bir set oluşturduğunda (NFT_MSG_NEWSET), nft_set_elem_expr_alloc() nft_expr_init() çağırır; bu da parent kodu NFT_EXPR_STATEFUL flag'ini yeniden kontrol etmeden önce expression'ın ->init handler'ını çalıştırır. nft_lookup/nft_dynset için bu ->init (nft_lookup_init → nf_tables_bind_set) list_add_tail_rcu() aracılığıyla reference edilen set'in ->bindings doubly-linked listesine bir struct nft_set_binding ekler.
Note
İhlal edilen invariant şu: "bir liste üzerindeki her node, onu içeren object free edilmeden önce kaldırılır." Embedded expression burada gerçekten stateful olmaya izinli değilse, parent onu reddeder ve expression'ı free etmek için nft_expr_destroy() çağırır — ama cleanup path'i az önce eklediği nft_set_binding'i list_del etmeyi unutur. Set'in ->bindings listesi artık free edilmiş bellekte yaşayan bir node içerir. Free edilen expression küçüktür: nft_lookup kmalloc-48'e (binding offset 16'da), nft_dynset kmalloc-96'ya (binding offset 56'da) düşer. İkinci bir expression eklemek, prev->next = new / new->prev = prev write'ları dangling node üzerinden düşen başka bir list_add_tail gerçekleştirir, yani free-sonra-reclaim edilmiş bir object'e kontrollü bir write. Fix, binding'i destroy path'inde unlink eder.
Walkthrough¶
"Settlers of Netlink" zinciri (NCC Group / Theori) tek bir limited UAF write'ı dört kez tetikleyerek tam bir LPE'ye dönüştürür. Kavramsal olarak:
// 1. NEWSET with an embedded lookup/dynset expr that is NOT a valid
// stateful expr here -> nf_tables_bind_set() adds the binding,
// validation fails, nft_expr_destroy() frees the expr but leaves
// the binding node on set->bindings (dangling list node)
new_set_with_bad_dynset(set);
// 2. reclaim the freed kmalloc-96 chunk with a controlled object
spray_reclaim(); // setxattr / FUSE / user_key_payload
// 3. add ANOTHER expr binding to the same set -> list_add_tail writes
// next/prev pointers THROUGH the dangling node = controlled write
new_set_with_second_dynset(set);
Four UAFs: leak -> arbitrary free -> fake set -> ROP
- UAF1 (leak): corrupting
list_addwrite'ı reclaim edilmiş biruser_key_payloadiçine bir kernel set adresi yerleştirir; bunukeyctl(KEYCTL_READ)userspace'e döndürür — bir kernel heap pointer leak'i. - UAF2 (arbitrary free): free edilen slot'u bir
cgroup_fs_context(kmalloc-96) ile reclaim et; UAF write'ı onunrelease_agent/path pointer'ını corrupt eder ki fd'yiclose()'lamak attacker-seçimi birnft_set'i free etsin — bir arbitrary-free primitive'i. - UAF3 (fake set / KASLR): o free edilen
nft_set'isetxattr/FUSE aracılığıyla reclaim ederek FAKESET1'i->udatagerçek bir set'e işaret ederek ve şişirilmiş bir->udlenile forge et; böylece set'i dump etmek komşu object'leri (örneğin birtty_struct) OOB-read eder ve kernel image base'ini leak eder — KASLR yenildi. - UAF4 (ROP): FAKESET2'yi sahte bir op tablosuna işaret eden corrupt bir
->opsile forge et; birNFT_EXPR_GCexpression'ı (nft_connlimit) çağırmakset->ops->gc_init()'i kontrollü register'larla çağırır →modprobe_path'i overwrite eden bir ROP zincirine pivot et, ardından bir root shell için module autoload'u tetikle.
Warning
Write primitive'i limited'tır: list_add_tail yalnızca iki pointer boyutunda değer yazmana izin verir (list next/prev) ve bu değerler tam olarak kontrol etmediğin kernel adresleridir. Tüm exploit, bu zayıf write'ı dikkatle seçilmiş reclaim object'leri aracılığıyla daha güçlü primitive'lere aklamakla ilgilidir.
Detection¶
KASAN, reddedilen bir NFT_MSG_NEWSET'ten sonra nf_tables_bind_set / list_add içinde bir use-after-free write işaretler; ayrıca set->bindings'den reference edilen free edilmiş bir nft_expr chunk'ına karşı bir UAF read olarak da görünebilir. Set'lere non-stateful expression eklemek başlı başına anormaldir. Stateful expression'larla set oluşturmanın syzkaller tarzı fuzzing'i bunu yeniden üretir.
Mitigation¶
Bir user/network namespace'de CAP_NET_ADMIN gerektirir — unprivileged path'i kaldırmak için unprivileged user namespace'leri devre dışı bırakın. Fix (commit 520778042ccca019f3ffa136dd0ca565c486cedd, "disallow non-stateful expression in sets earlier") NFT_EXPR_STATEFUL kontrolünü expr->ops->init()'in önüne taşır ve binding'i destroy path'inde unlink eder; böylece validation başarısızlığından sonra hiçbir dangling node hayatta kalmaz. nf_tables_api.c'yi 5.18.1'e kadar etkiler (5.18.2'de ve backport'larda düzeltildi). Yamalı bir kernel'e güncelleyin.
Note
Aynı limited-write primitive farklı reclaim object'leriyle de exploit edilmiştir: free edilen nft_expr chunk'ı (lookup/dynset varyantına göre kmalloc-48/96) POSIX message-queue node'ları (mqueue / msg_msg, Theori) veya user_key_payload (NCC Group) ile reclaim edilir; her ikisi de bir KASLR leak'ine ardından modprobe_path overwrite'ına götürür.
References¶
- SETTLERS OF NETLINK: Exploiting a limited UAF in nf_tables (CVE-2022-32250) — NCC Group / Theori
- CVE-2022-32250 — Debian security tracker
- NVD — CVE-2022-32250
- torvalds/linux commit 520778042ccc — disallow non-stateful expression in sets earlier
- Theori — Linux kernel exploit CVE-2022-32250 with mqueue