nf_tables Flipping Pages double-free (CVE-2024-1086)¶
nft_verdict_init()pozitif bir drop error kabul ediyor, bu yüzden bir rule'un verdict'iNF_DROP(skb'yi free eder) olarak maskelenipNF_ACCEPT(free edilen skb'yi işlemeye devam eder) olarak geri okunabiliyor — page-level bir cross-cache attack ile root'a yükseltilen bir double-free.
Mechanism¶
Bir netfilter hook'u nf_hook_slow()'a bir verdict döndürür. Düşük bitler action'dır (NF_DROP, NF_ACCEPT, NF_QUEUE, …) ve NF_VERDICT_MASK ile çıkarılır; NF_DROP için üst bitler NF_DROP_GETERR() ile alınan bir errno taşır. Bir nftables rule'unun ürettiği verdict nft_verdict_init() içinde inşa edilir; bu fonksiyon userspace'in oraya ne koyabileceğini kısıtlaması gereken yerdir.
Note
Kırılan invariant şu: "tek bir verdict değerinin tek bir tutarlı anlamı vardır." nft_verdict_init() verdict code'unu doğruladı ama drop-error field'ında pozitif bir değere izin verdi. 0xffff0000 seç: 0xffff0000 & NF_VERDICT_MASK == 0 NF_DROP olarak decode edilir, bu yüzden nf_hook_slow() kfree_skb_reason(skb, ...) çalıştırır ve sk_buff'ı free eder. Ama NF_DROP_GETERR(0xffff0000) caller'ın NF_ACCEPT (devam et) gibi ele aldığı pozitif bir sayı döndürür, bu yüzden paket path'i zaten free edilmiş skb'yi kullanmaya — ve sonunda tekrar free etmeye — devam eder. Tek bir verdict hem "drop+free" hem de "accept+continue" anlamına gelir ve sk_buff'ın (ve onun ->head backing allocation'ının) bir double-free'ini verir. Fix (f342de4e2f33) nft_verdict_init() içinde pozitif drop error'larını reddeder.
Walkthrough¶
Tetikleyici data-only'dir: verdict register'ı özel hazırlanmış değeri tutan bir nftables rule'u insert et, ardından hook üzerinden bir paket yönlendir.
// conceptual: a rule that yields a verdict of 0xffff0000
// NF_VERDICT_MASK -> NF_DROP (kfree_skb path)
// NF_DROP_GETERR -> +1 (looks like NF_ACCEPT to the caller)
nft_add_rule_immediate_verdict(table, chain, 0xffff0000);
send_packet_through_hook(); // skb freed once, then used+freed again
skb'nin ->head'i, boyutu paket uzunluğunu izleyen bir kmalloc allocation'ıdır, bu yüzden attacker paketleri boyutlandırarak double-freed object'in hangi slab cache'ine düşeceğini seçer — kmalloc-256'dan buddy allocator'dan doğrudan gelen order-4 (64 KiB) page'lere kadar.
Flipping Pages: page-level cross-cache to Dirty Pagedirectory
"Flipping Pages" writeup'ı (Notselwyn / pwning.tech) double-free'i object granularity yerine page granularity'sinde sürer:
- skb'yi öyle boyutlandır ki
->headper-CPU page (PCP) allocator'a double-freed edilen bir order-0 page olsun. - PCP freelist'i ve page refcount'unu drain et/hokkabazlık yap ki buddy allocator aynı physical page'i iki farklı amaç için iki kez versin.
- Page'i bir kez bir PTE/PMD page-table page olarak, bir kez de attacker-writable data olarak reclaim et; böylece "data" görünümüne yazmak canlı page-table entry'lerini düzenler — arbitrary physical read/write veren Dirty Pagedirectory primitive'i.
- Bunu
modprobe_path'i bulup overwrite etmek (veyacred'i patch'lemek) için kullan; filesystem'e dokunmadan root elde et.
Page-table double-mapping, slab freelist corruption kontrollerinden kaçınır ve yeniden derlemeye gerek kalmadan v5.14–v6.6 kernel'leri arasında çalışır. Notselwyn, kernelCTF image'lerinde %99.4 başarı bildiriyor. Bkz. cross-cache attack ve Dirty Pagetable.
Warning
kfree_skb tabanlı double-free'ler timing'e duyarlıdır: skb'nin iki free arasındaki ömrünü genişletmek için IP fragmentation/reassembly kullanılır; böylece page deterministik olarak yeniden kapılabilir.
Detection¶
KASAN, bir netfilter NF_DROP verdict'inden sonra skb free path'inde kök salan bir double-free / use-after-free rapor eder; pozitif-errno verdict değeri (üst bitler set, düşük bitler sıfır) imzadır. Hardened-page-allocator freelist randomization page-level varyantını daha gürültülü yapar ama engellemez.
Mitigation¶
Bir user/network namespace'de CAP_NET_ADMIN gerektirir — unprivileged trigger'ı kaldırmak için unprivileged user namespace'leri devre dışı bırakın (user.max_user_namespaces=0). Gerçek fix, pozitif drop error'larını reddeden f342de4e2f33 commit'idir ("netfilter: nf_tables: reject QUEUE/DROP verdict parameters"). Şubat 2024 yamasının ötesinde bir kernel'e güncelleyin.