Skip to content

netfilter / nf_tables hardened exploitation

"Flipping Pages": bir CVE-2024-1086 nf_tables double-free'sini slab'ın dışına ve page allocator'ın içine escalate edin, bir userland page'e bir page table'ı double-allocate edin ve slab freelist hardening'ini hiç yenmeden SMAP/SMEP/KASLR/CFI'dan bağımsız data-only bir arbitrary physical read/write kazanın.

Mechanism

Note

Modern kernel'ler slab'ı ağır biçimde hardene eder — CONFIG_SLAB_FREELIST_HARDENED freelist pointer'larını XOR'lar ve valide eder (freelist_pointer_corrupted()), CONFIG_SLAB_FREELIST_RANDOM layout'u randomize eder, hardened usercopy kopyaları bounds-check eder ve CFI indirect call'ları kısıtlar. Notselwyn'in içgörüsü, slab'la savaşmayı bırakmak ve bug'ı, bu metadata hardening'inin hiçbiri olmayan buddy/page allocator üzerinde bir primitive'e çevirmektir.

Root cause (CVE-2024-1086): nft_verdict_init(), pozitif bir değeri bir drop error olarak kabul eder. NF_DROP böyle bir error ile döndürüldüğünde, nf_hook_slow() skb'yi free eder (kfree_skb_reason()), ama NF_DROP_GETERR() üst bit'leri çıkarır ve NF_ACCEPT'e benzeyen bir değer döndürür, böylece caller processing'e devam eder ve aynı skb'yi tekrar free eder — bir double-free (örneğin crafted verdict 0xffff0000).

Escalation chain'i iki allocator invariant'ına dayanır:

  1. Bir slab object'in double-free'i, onun backing page'inin double-free'sine launder edilebilir. Slab'ın refcount/occupancy'sini kontrol ederek ikinci free'yi meşru bir free gibi gösterirsiniz ("allocate the page again before the 2nd free, as the free will look like a non-double-free free considering there is an actual object in the page"), böylece detection logic'i ateşlemez.
  2. Buddy allocator, free edilen page'i sonra ne isterse ona geri verir, page-table allocation'ları dahil. Page table'lar (PMD/PTE), kmalloc değil, alloc_pages tarafından allocate edilir, böylece aynı physical page aynı anda hem kontrollü bir data page hem de canlı bir page table olabilir — "Dirty Pagedirectory" / page-table-as-data primitive'i.

Kritik olarak, PTE'ler bir PMD içinde physical address ile referans verilir. SMAP yalnızca virtual access mode'u inceler; "when a PTE entry in a PMD is a userland page, it will not be detected by SMAP." MMU'nun bir page table olarak dolaştığı bir page'e PTE-shaped qword'ler yazmak böylece arbitrary physical frame'leri process'e map'ler — SMAP/SMEP'i tamamen atlayan data-only bir R/W.

Walkthrough

Exploit data-only ve version-independent'tır; aşağıdaki adımlar pwning.tech writeup'ını yansıtır (başarı oranı %99.4, n=1000, v6.4.16 üzerinde).

1. Double-free'i tetikleyin. Verdict'i, NF_DROP_GETERR() accept-benzeri bir değer döndürsün diye pozitif bir error'lu bir drop'u encode eden bir nf_tables rule ekleyin:

verdict = NF_DROP | (drop_error << 16)   ; e.g. 0xffff0000
=> nf_hook_slow(): kfree_skb_reason(skb)  (1st free)
=> caller sees NF_ACCEPT-like, keeps using/freeing skb (2nd free)

2. Freelist corruption'ı maskeleyin. İlk free'den hemen sonra, temiz skb object'leri allocate edin, corrupt edilen freelist pointer'ını gizlemek için onları free edin, sonra orijinal double-free edilmiş object'i reallocate edin. Bu, o cache'ten bir sonraki allocation sırasında freelist_pointer_corrupted()'ın poison'lanmış next-pointer'a takılmasını engeller.

3. Slab → page boundary. Exploit, skb head'lerinin (kmalloc) ve page-table page'lerinin (alloc_pages) buluştuğu slab-to-buddy boundary'sinde çalışır, stability için cross-cache attack'lerden kasıtlı olarak kaçınır. Per-CPU page (PCP) freelist'lerini drain eder ve bir refill zorlar:

PCP draining:
  free order-4 pages to the buddy freelists
  trigger PCP refill -> 16 order-0 pages carved from the just-freed order-4 region
  => the double-freed page is now reissued as individual order-0 pages,
     reliably collidable with PTE/PMD allocations

4. Kontrollü bir page üzerine bir page table'ı double-allocate edin. Bir PMD page ve bir PTE page'i, hâlâ userland'de map'li/kontrollü bir page ile aynı physical address'e resolve olacak şekilde alın.

map a large anon range and fault it in to force PMD/PTE allocation
arrange one PT page == a userland data page (same physical frame)

5. Dirty Pagedirectory R/W. PTE page'i user data olarak da görünürken, PTE-format entry'leri (physaddr | flags) yazın ve onları ikinci bir VMA üzerinden dereference edin:

write PTE values for userland pages in VMA 0x40000000 - 0x80000000
read/write the corresponding userland pages in VMA 0x8000000000 - 0x10000000000
=> arbitrary physical read/write (SMAP-immune: PTEs referenced by physaddr)

6. Data-only privesc. Physical memory'yi kernel data pattern'leri için tarayın ve modprobe_path'i (ya da cred'leri) overwrite edin — bkz. modprobe path overwrite. Physical-KASLR, 16 MiB'lık CONFIG_PHYSICAL_START hizalaması üzerinden brute-force edilir ve çalışan kernel get-sig ile fingerprint'lenir, böylece per-version offset gerekmez; exploit disk'e hiç dokunmaz.

Warning

Tüm yaklaşım, page-level double-free'nin asla bir double-free olarak saptanmamasına bağlıdır: launder-allocation'larının sırası (adım 2) ve PCP drain hacmi (adım 3) timing/occupancy'ye duyarlıdır. Bir yanlış sayım, poison'lanmış bir freelist bırakır ve sessizce başarılı olmak yerine bir sonraki allocation'da panic eder.

Bu, nf_tables CVE-2024-1086 chain'inin hardened-bypass kilit taşıdır; bug'ın kendisi için nf-tables flipping-pages double-free'e, ve page-table primitive'i izole halde için dirty pagetable / page-table data-only attack ve page-level cross-cache attack'e bakın.

Detection

  • nf_tables rule'ları yüklemek, (muhtemelen user) bir namespace'te CAP_NET_ADMIN gerektirir — unprivileged unshare(CLONE_NEWUSER|CLONE_NEWNET)'i takip eden netlink NFT_MSG_NEW* trafiğini izleyin.
  • KFENCE / KASAN / slab_debug, double-free'yi ilk free'de slab seviyesinde yakalar.
  • Ani order-4 free'leri takip eden order-0 page-table allocation patlamaları, PCP-drain imzasıdır.

Mitigation

  • CVE-2024-1086 fix'ini uygulayın (nft_verdict_init'te verdict sanitization).
  • nf_tables'e giden unprivileged path'i kaldırmak için kernel.unprivileged_userns_clone=0 set edin / user namespace'leri kısıtlayın; kullanılmıyorsa nf_tables modülünü blocklist'leyin.
  • CONFIG_RANDOM_KMALLOC_CACHES, CONFIG_SLAB_VIRTUAL/cache isolation ve init_on_free maliyeti artırır; ancak page-level teknik slab hardening'ini kasıtlı olarak atlar, yani onu fiilen körelten page-allocator seviyesindeki savunmalardır (örneğin SLAB_VIRTUAL, page-table protection).

References