nft_tables ROP exploitation (CVE-2023-0179)¶
nft_payload_copy_vlan()içindeki 8-bit integer underflow, VLAN'a bağlı bir stack OOB write veriyor; önce kernel adreslerini leak ediyor, sonra nftables jumpstack'ini corrupt ederek ROP'a pivot yapıyor ve root'a ulaşıyor. (Aynı bug,nf-tables-nft-chain-nft-object-uafslug'ında "UAF" olarak yanlış etiketlenmiş; bu not canonical'dır.)
Mechanism¶
Note
nftables, kuralları nft_do_chain() içinde kernel stack'inde duran küçük ve sabit boyutlu
bir register file'a karşı evaluate ediyor. nft_payload_copy_vlan() bir VLAN header'ı
hedef register'a yeniden kuruyor ve kaç byte kopyalanacağını bir u8 ethlen üzerinde
aritmetikle hesaplıyor. skb_vlan_tag_present(skb) true olduğunda (host bir 802.1Q VLAN
içindeyse), boyut hesabı unsigned 8-bit ethlen'i underflow ediyor; yani küçük olması
gereken bir değer ~250–254'e wrap oluyor. Bu durumda copy, seçilen register'ı çok aşarak
doğrudan komşu stack belleğine yazıyor — chain evaluation'ı resume etmek için kullanılan
jumpstack de buna dahil.
Kırılan invariant şu: kopyalanan miktar hedef register'ın sınırları içinde kalmalı, ama unsigned underflow "çok büyük" bir length'in bounds mantığından fark edilmeden geçmesine yol açıyor. Zafiyetli şekil kabaca şöyle:
/* net/netfilter/nft_payload.c, simplified */
u8 ethlen = len;
...
if (offset + len > VLAN_ETH_HLEN + vlan_hlen)
ethlen -= offset + len - VLAN_ETH_HLEN + vlan_hlen; /* underflows u8 */
offset = 19, len = 4 ile çıkarma işlemi underflow oluyor ve ethlen 251 oluyor; böylece
seçilen register'dan itibaren ~251 byte'a kadar yazılıyor.
Walkthrough¶
Saldırının CAP_NET_ADMIN'e ihtiyacı var; unprivileged bir kullanıcı bunu yeni bir user
namespace'e girerek elde ediyor. Ayrıca skb'nin bir VLAN tag taşıması gerekiyor, bu yüzden PoC
bir VLAN interface kuruyor.
- Namespace'lere gir ve
CAP_NET_ADMINkazan:
unshare(CLONE_NEWUSER | CLONE_NEWNET); /* map self to uid 0 inside */
/* configure a VLAN device so skb_vlan_tag_present() is true */
-
Leak. payload destination olarak
NFT_REG32_00'ı seç ki oversized copy, stack içeriğini register file'ın içine taşırsın; sonra bu register'larınft_dynsetexpression'larıyla birnft_set'e depolayarak geri oku. Set'i dump etmek (nft list ruleset) stack ve heap/text pointer'larını ortaya çıkarıyor ve KASLR'ı kırıyor. -
Corrupt. destination olarak
NFT_REG32_15ile yeniden tetikle. Artık ~251-byte'lık write, register'ları aşıpnft_do_chain()'inNFT_JUMPverdict'leri boyunca sakladığı jumpstack girdilerine (chain,rule,last_rulepointer'ları) ulaşıyor. Bir dizi jump verdict ile jumpstack'i önceden artırmak, corrupt edilebilir slot'u konumlandırıyor. -
Pivot. chain evaluation resume olduğunda, forge edilmiş
rulepointer dereference ediliyor ve "expression"ları evaluate ediliyor. Onu (leak edilmiş bir adrese yerleştirilen) saldırgan verisine yönlendirmek execution flow kontrolünü veriyor; ardından bir stack pivot,commit_creds(prepare_kernel_cred(0))'ı (ya da eşdeğerini) çağıran bir ROP chain çalıştırıyor ve userspace'e root olarak dönüyor.
Conceptual leak readout
Warning
The exact byte counts and register layout are kernel-version-specific; the PoC was
validated on Linux 6.1.6. The bug is reachable from commit f6ae9f1 through
696e1a48b1a1 (mainline up to 6.2-rc), but only when CONFIG_VLAN_8021Q is built and a
VLAN tag is present. Treat the offsets above as illustrative, not copy-paste exploit
constants.
Detection¶
Unprivileged creation of nftables rulesets that use nft_payload with VLAN extraction on a
freshly created user+net namespace is suspicious. Auditd on unshare(CLONE_NEWUSER) plus
unexpected VLAN interface creation by non-root narrows it.
Mitigation¶
- Update past the fix: the patch corrects the operator so the size cannot underflow
(
... - VLAN_ETH_HLEN - vlan_hleninstead of+ vlan_hlen). Fixed by commit696e1a48b1a1b01edad542a1ef293665864a4dd0("netfilter: nft_payload: incorrect arithmetics when fetching VLAN header bits"); reachable in 6.2-rc and backported. - Disable unprivileged user namespaces:
sysctl -w kernel.unprivileged_userns_clone=0removes theCAP_NET_ADMINacquisition path.