nf_tables OOB write (CVE-2022-34918)¶
nft_set_elem_init()içindeNFT_DATA_VALUEveNFT_DATA_VERDICTarasındaki bir type confusion, bir CAP_NET_ADMIN kullanıcısının attacker kontrollü veriyi fazla küçük bir slab allocation'ının ötesine yazmasına olanak veriyor.
Mechanism¶
The invariant that breaks
Bir element bir nftables set'ine eklendiğinde, kernel boyutu element template uzunluğundan (tmpl->len) türetilen bir slab buffer allocate eder, ardından element verisini ayrı olarak izlenen bir uzunluk (set->dlen) kullanarak içine memcpy'lar. Örtük invariant şudur: extension alanına kopyalanan byte'lar, template'in onlar için ayırdığı byte'ları asla aşmaz.
nft_data_init() kullanıcı tarafından sağlanan veriyi iki şekilden birine parse eder:
NFT_DATA_VALUE— ham byte'lar (bir key/value), uzunlukset->dlen'e karşı doğrulanır.NFT_DATA_VERDICT— 16-byte'lık birstruct nft_verdict(bir chain jump/accept/drop), tarihsel olarakdesc->len == set->dlenuzunluk kontrolünden muaf.
Bir set, bir data uzunluğu (örneğin dlen = 8) bildirilerek oluşturulur. Element template'i sonra o boyutta value-tipli bir descriptor'dan inşa edilir. Ama veri bir verdict olarak parse edilir; onun desc->len'i (sizeof(struct nft_data) = 16) daha büyüktür ve set->dlen ile hiç uzlaştırılmamıştır. Allocation value path'i için boyutlandırılırken kopya uzunluğu verdict path'ini izler — klasik bir size-confusion heap overflow. Pratikte overflow bounded'dır (RandoriSec writeup'ında ~48 byte'a kadar). Attacker farkı ve free-komşusu veriyi kontrol ettiğinden, bu güçlü bir lineer write primitive'idir.
Walkthrough¶
Bug, set-element add path'inde yaşar. Kavramsal olarak:
/* net/netfilter/nf_tables_api.c (pre-patch, simplified) */
err = nft_data_init(ctx, &elem.data.val, sizeof(elem.data), &desc,
nla[NFTA_SET_ELEM_DATA]);
/* desc.type may be NFT_DATA_VERDICT (len 16) or NFT_DATA_VALUE (len == set->dlen) */
/* template length comes from the *value* assumption ... */
tmpl = ...; /* sized for set->dlen */
/* ... but the verdict copy uses the larger desc.len */
elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
elem.key_end.val.data, elem.data.val.data,
timeout, expiration, GFP_KERNEL);
nft_set_elem_init() içinde allocation ve kopya uyuşmaz:
void *elem = kzalloc(set->ops->elemsize + tmpl->len, gfp); /* small */
...
memcpy(nft_set_ext_data(ext), data, set->dlen); /* writes set->dlen bytes */
Tetikleyici taslağı (hepsi netlink/NFNL_SUBSYS_NFTABLES üzerinden, CAP_NET_ADMIN gerektirir):
- Yeni bir user+network namespace'e gir (
unshare -Urn) ki unprivileged bir process, namespace'lenmiş netfilter instance'ı üzerindeCAP_NET_ADMINkazansın. NFT_MSG_NEWTABLE/NFT_MSG_NEWSET— küçük bir bildirilmiş data uzunluğuna sahip bir set oluştur.NFT_MSG_NEWSETELEM—NFTA_SET_ELEM_DATA'sı bir verdict olarak encode edilen bir element ekle ki uzunluk kontrolü atlansın ve kopya uzunluğu allocation'ı aşmaya zorlansın.- Komşu slab object'i kontrollü byte'larla overwrite edilir.
Confirming the overflow with KASAN
KASAN-instrumented bir kernel, memcpy'da out-of-bounds store'u rapor eder:
BUG: KASAN: slab-out-of-bounds in nft_set_elem_init
Write of size N at addr ffff8880........ by task poc/NNN
nft_set_elem_init+0x...
nf_tables_newsetelem+0x...
nfnetlink_rcv_batch+0x...
Allocated by task NNN:
kmalloc / nft_set_elem_init
Tam write boyutu seçilen set->dlen ve verdict uzunluğuna bağlıdır; yukarıdaki sayıları açıklayıcı kabul edin.
Footgun: this is an allocator-adjacent linear write
Primitive, element allocation'ının ardından kendi kmalloc cache'inde ne gelirse onun içine taşar. Güvenilir exploitation, kullanışlı bir victim object'in (örneğin bir length/pointer field'ı olan bir elastic object) hemen element'ten sonra oturması için heap grooming gerektirir. Bkz. elastic objects.
Detection¶
nft_set_elem_init/nf_tables_newsetelemiçinde kök salan KASAN slab-out-of-bounds raporları.- Unprivileged process'lerin user namespace oluşturup
NFNL_SUBSYS_NFTABLESbatch'leri vermesini denetleme.
Mitigation¶
- Fix,
desc->len != set->dlenuzunluk kontrolünü tek tip uygular veNFT_DATA_VERDICTmuafiyetini kaldırır. - Unprivileged user namespace'leri kısıtlayın (onları açan distro'larda
kernel.unprivileged_userns_clone=0;user.max_user_namespaces=0), nf_tables bug'ları için standartCAP_NET_ADMINkapısı. - Mitigation'ların (CONFIG_RANDOM_KMALLOC_CACHES, vb.) overflow-sonrası aşamanın çıtasını nasıl yükselttiği için bkz. netfilter nf_tables hardened exploitation.