Skip to content

nf_tables register validation flaws (CVE-2022-1015/1016)

nftables register index'lerinin eksik bounds validation'ı, bir CAP_NET_ADMIN kullanıcısının register array'inin ötesine yazmasına (CVE-2022-1015, OOB write) ve initialize edilmemiş register/stack verisini leak etmesine (CVE-2022-1016, infoleak) olanak veriyor.

Mechanism

Registers as a fixed-size scratch array

nf_tables expression'ları küçük, sabit boyutlu bir 32-bit register array'i aracılığıyla iletişim kurar, struct nft_regs (data member'ı 0x50 = 80 byte'tır). Her expression hangi register'dan okuduğunu (sreg) veya hangisine yazdığını (dreg) bildirir. Kernel, rule-build zamanında register_index * 4 + access_length'ın o 80-byte'lık array içinde kaldığını doğrulamalıdır — aksi hâlde paket başına evaluate edilen bir expression, register penceresinin dışını okur veya yazar.

İki ayrı correctness invariant'ı söz konusudur:

  • CVE-2022-1015 (bounds): nft_validate_register_store() / nft_validate_register_load() reg * NFT_REG32_SIZE + len > sizeof_field(struct nft_regs, data)'yı kontrol eder. reg tamamen kullanıcı kontrollü bir u32'den alındığında, reg * 4 çarpımı wrap edebilir ve/veya index daha sonra bir u8 dreg/sreg'e daraltıldığında, validation'dan geçen bir değer runtime'da out-of-range bir register'a eşlenebilir — register block'una komşu kernel stack'ine bir OOB write.
  • CVE-2022-1016 (initialisation): register array'i, bir expression chain çalışmadan önce tamamen sıfırlanmaz. Chain'in hiç yazmadığı bir register'ı okuyan bir expression eski stack içeriğini döndürür; bu da dışarı kopyalanabilir (örneğin bir verdict/payload'a), kernel belleğini leak eder ve KASLR'yi yener.

Walkthrough

Store-tarafı validation (pre-patch, basitleştirilmiş):

/* net/netfilter/nf_tables_api.c */
int nft_validate_register_store(const struct nft_ctx *ctx,
                                enum nft_registers reg, ...,
                                unsigned int len)
{
    ...
    if (reg * NFT_REG32_SIZE + len >       /* NFT_REG32_SIZE == 4 */
        sizeof_field(struct nft_regs, data))   /* == 0x50 */
        return -ERANGE;
    ...
}

nft_parse_register() kullanıcı değerini eşler: legacy register'lar NFT_REG_1..NFT_REG_4 ölçeklenir, daha yeni NFT_REG32_00.. ise 4 kadar aşağı kaydırılır. 0xffffffff'e yakın özel hazırlanmış bir index, aritmetik kontrolden geçebilir ama evaluation sırasında gerçekte kullanılan u8 register field'ına daraltıldığında 80-byte'lık array'in dışına işaret edebilir.

CVE-2022-1015 fix'i (6e1acfa387b9, "netfilter: nf_tables: validate registers coming from userspace."), overflow eden product yerine register değerini düzgün valide eder. Bug, nft_parse_register_store() v5.12'de (345023b0db3) eklendiğinde reachable oldu; 5.12'den fix'e kadarki kernel'ler etkilenir.

Public exploitation altitude'da (David Bouman), aynı OOB primitive birkaç expression üzerinden ifade edilir: nft_payload/nft_payload_set register file'a göre OOB read/write yapar ve sıfır shift'li bir nft_bitwise, OOB-read data'sını geçerli bir register'a kopyalayarak exfiltrate edilebilir kılar — "bitwise" pivot adlandırması buradan gelir. Chain kernel base'i leak eder, sonra bir stack return address'ini commit_creds()/prepare_kernel_cred() çağıran bir ROP chain ile overwrite eder.

Tetikleyici taslağı (netlink, CAP_NET_ADMIN gerektirir, örneğin unshare -Urn'de):

  1. NFT_MSG_NEWTABLE / NFT_MSG_NEWCHAIN — bir base chain kur.
  2. NFTA_*_DREG/SREG'i sınır register index'ini taşıyan bir expression (örneğin nft_payload, nft_bitwise, nft_immediate) ile NFT_MSG_NEWRULE.
  3. CVE-2022-1015: expression'ın paket başına store'u nft_regs'in dışına düşer, komşu kernel stack'ini corrupt eder — bir ROP'a pivot için kullanılabilir kontrollü bir write. Bkz. nft tables ROP exploitation (CVE-2023-0179 ile alternatif bir ROP yolu; farklı bir root cause — nft_payload_copy_vlan() VLAN underflow'u —, buradaki register OOB'nin exploit'i değil).
  4. CVE-2022-1016: yazılmamış bir register oku ve onu emit et; yanıt initialize edilmemiş kernel stack byte'larını taşır.
Conceptual CVE-2022-1016 leak path

rule: payload-load -> [reg N never initialised] -> compare/return
response NLA contains stale bytes of struct nft_regs / kernel stack
-> kernel pointer recovered -> KASLR defeated
Tam register index'i ve leak edilen offset'ler kernel build'ine bağlıdır; yeniden üretilmedikçe bunları kavramsal tutun.

Two CVEs, two patches

CVE-2022-1015 (OOB write) ve CVE-2022-1016 (uninitialised-register infoleak) birlikte açıklandı ama bağımsız fix'lerdir. Bir kernel biri için yamalanmış ama diğerine hâlâ vulnerable olabilir; her iki commit'i de kontrol edin.

Detection

  • nft_*_eval'in nft_regs komşularına yazdığı yere yakın KASAN out-of-bounds raporları.
  • Bildirilen register index'leri u32 aralığının üst ucunda oturan rule'lar.

Mitigation

  • CVE-2022-1015, register bounds'ı wrap olmadan doğrulayarak ve out-of-range index'leri reddederek düzeltilir.
  • CVE-2022-1016, evaluation öncesinde register array'ini sıfırlayarak düzeltilir; böylece yazılmamış register'ların okunması eski belleği leak edemez.
  • CAP_NET_ADMIN dayanağını kaldırmak için unprivileged user namespace'leri kısıtlayın.

References