Skip to content

nf_tables nft_chain_lookup_byid cross-CPU reclaim UAF (CVE-2023-31248)

nftables'taki nft_chain_lookup_byid(), döndürdüğü chain'in hâlâ active olup olmadığını asla kontrol etmez, dolayısıyla bir rule freed bir nft_chain'e reference tutabilir — bir cross-CPU allocation race ile sürülen ve güvenilir biçimde reclaim edilen bir use-after-free.

Mechanism

CVE-2023-31248, Linux nf_tables subsystem'inde bir n-day use-after-free'dir (Mingi Cho tarafından raporlandı). "Cross-CPU allocation" çerçevesi, Theori/Mingi Cho & Wongi Lee'nin Utilizing Cross-CPU Allocation to Exploit Preempt-Disabled Linux Kernel araştırmasından gelir; bu araştırma, free/reuse penceresi preempt-disabled bir section'da çalışsa bile freed bir object'i reclaim etmek için bu bug'ı case study olarak kullanır.

Note

nftables'ın dayatması gereken invariant şu: bir lookup yalnızca current generation'da hâlâ active olan object'leri döndürmelidir (nft_active_genmask / genmask üzerinden doğrulanır). nft_chain_lookup_byid(), NFTA_VERDICT_CHAIN_ID tarafından reference edilen bir chain'i çözmek için pending transaction'ları gezer, ama genmask/active kontrolünü atlar. Yani tek bir batch içinde bir chain'i silip aynı zamanda verdict'i NFT_JUMP/NFT_GOTO olan ve o (artık-inactive) chain'i ID ile reference eden bir rule oluşturabilirsin. nft_verdict_init() bunu kabul eder ve chain->use'u artırır, yok edilmeye giden bir chain'e canlı bir reference tutar — chain memory free edilip reclaim edildiğinde bir use-after-free.

Cross-CPU açısı önemlidir çünkü reclaim race, vulnerable allocation ile spray object'i farklı CPU'lardan aynı per-CPU slab freelist'ine zorlayarak sıkılaştırılabilir; bu da pencereyi normalde vurması zor kılan preempt-disabled critical section'ları yener.

Walkthrough

struct nft_chain, nf_tables_addchain() içinde kzalloc() ile allocate edilir ve kmalloc-cg-128'e düşer. Exploit, eksik-kontrol path'ini sürer:

1. Batch A: add table+chain; add a chain C; in the SAME batch, delete C and
   add a rule R in a base chain whose verdict is NFT_GOTO/NFT_JUMP with
   NFTA_VERDICT_CHAIN_ID = C's id.
     -> nft_chain_lookup_byid() returns the inactive C; chain->use is bumped.
2. Free C's backing memory (drive the transaction so the inactive chain is
   destroyed / freed) while R still references it -> dangling nft_chain.
3. Reclaim the freed kmalloc-cg-128 slot by spraying controlled objects
   (e.g. msg_msg) — cross-CPU allocation lands the spray on the freed slot.
4. Fake the chain's rule list / nft_expr / nft_expr_ops so that
   nft_chain_validate() dereferences attacker data and calls a controlled
   expression ->validate callback.
Privilege escalation kuyruğu

Bir expression ops->validate (ya da fake chain üzerinden ulaşılan eşdeğer function pointer) kontrolüyle exploit, prepare_kernel_cred(0) ardından commit_creds() çağıran bir ROP chain'e pivot eder ve root verir. Bug, herhangi bir user/network namespace'inde CAP_NET_ADMIN gerektirir; unprivileged kullanıcılar bunu unshare(CLONE_NEWUSER|CLONE_NEWNET) ile elde edebilir.

Warning

Exploitation, unprivileged user-namespace oluşturmanın izinli olmasını gerektirir (kernel.unprivileged_userns_clone=1 ya da distro varsayılanı). Bunu devre dışı bırakmak birincil yerel attack surface'i kaldırır.

Detection

  • Bir chain'i silip aynı transaction'da onu ID ile reference eden nftables batch'lerini denetle.
  • nf_tables_chain_destroy() içinde WARN_ON(chain->use > 0)'ın tetiklenmesi reference-count anomalilerinin bir sinyalidir.

Mitigation

  • nft_chain_lookup_byid() içine active/genmask kontrolü eklenerek yamalandı (stable'da düzeltildi; ≥ 6.2.y backport'ları).
  • Unprivileged user namespace'leri kısıtla; cross-CPU reclaim'i daha az güvenilir kılmak için CONFIG_SLAB_FREELIST_RANDOM/HARDENED ve kmalloc-cg cache ayrımını devreye al.

References