
Riproduce CVE-2023-32233, un use-after-free di nf_tables nel kernel Linux che porta a un'escalation locale dei privilegi. Include il codice sorgente del PoC, l'analisi della causa principale, la scomposizione della catena di sfruttamento e note difensive su rilevamento e mitigazione.
Cosa contiene questo repository: la mia riproduzione pratica e lo studio di CVE-2023-32233, un use-after-free nel sottosistema Netfilter nf_tables del kernel Linux che consente l'escalation locale dei privilegi, divulgato pubblicamente a maggio 2023 da Patryk Sondej e Piotr Krysiuk.
Riconoscimenti: il PoC (exploit.c) e il write-up originale sono di
@Liuk3r. Il mio lavoro in questo
repository consiste nel riprodurre l'exploit nel mio laboratorio (Ubuntu 23.04,
kernel 6.2.0-20) e nel documentare la causa principale e la catena di
sfruttamento qui sotto. Tutti i test sono stati eseguiti su macchine di
laboratorio dedicate.
nf_tables elabora gli aggiornamenti di configurazione come un batch atomico. La validazione di ogni operazione rispetto alle modifiche di stato delle operazioni precedenti nello stesso batch è insufficiente. In concreto:
nft_rule contenente un'espressione lookup su un
nft_set anonimo che contiene alcuni elementi.NFT_MSG_DELRULE — elimina la regola, che implicitamente elimina
l'espressione lookup e il nft_set anonimo;NFT_MSG_DELSETELEM — elimina un elemento del set anonimo già eliminato.nf_tables_commit_release() accoda le risorse su
nf_tables_destroy_list, elaborate successivamente da
nf_tables_trans_destroy_work():
prima nft_commit_release() → nf_tables_rule_destroy() →
nft_lookup_destroy() → nft_set_destroy() → kvfree() libera il
nft_set;
poi, per NFT_MSG_DELSETELEM, nf_tables_set_elem_destroy() chiama
nft_set_elem_ext() che dereferenzia il nft_set liberato:
Se set->ops->elemsize viene corrotto, una posizione di memoria scelta
dall'attaccante viene interpretata come un nft_set_ext — la primitiva su cui si
basa tutto il resto.
nf_tables_trans_destroy_work() in esecuzione su
un thread worker in background: inserire una grande operazione di distruzione
del set come ritardo controllato, tenere occupate le altre CPU e riallocare il
chunk nft_set liberato dalla stessa CPU con un nft_set di tipo diverso
(elemsize diverso) → type confusion.nft_set_ext corrotti con offset fuori intervallo, così
nf_tables_set_elem_destroy() scorre i chunk adiacenti come una lista di
nft_expr da distruggere.nft_log con NFTA_LOG_PREFIX
controllato; nft_log_destroy() libera priv->prefix, fornendo una primitiva
di allocazione sovrapposta in kmalloc-{8..192} (limitata dal byte NULL
all'inizio).nft_object->udata per rimuovere la restrizione del
byte NULL sulla lettura dangling.È interessante notare che le primitive scelte evitano qualsiasi cosa la CFI bloccherebbe — non è necessario alcun hijacking di chiamate indirette in nessun passaggio.
SLAB_FREELIST_HARDENED), INIT_ON_FREE e le restrizioni sul riutilizzo
degli slab per-CPU attaccano tutte il passaggio 1; la CFI (dove presente)
limita il passaggio 3+.nf_tables che contengono DELRULE+DELSETELEM su set anonimi; triage dei crash
per nft_set_elem_ext / nf_tables_trans_destroy_work nello stack.nftables richiede CAP_NET_ADMIN,
ma gli user namespace non privilegiati lo concedono — ecco perché
kernel.unprivileged_userns_clone=0 è un parametro di hardening significativo.Testato su Ubuntu 23.04 (Lunar Lobster), kernel 6.2.0-20-generic.
sudo apt install gcc libmnl-dev libnftnl-dev
gcc -Wall -o exploit exploit.c -lmnl -lnftnl
Il profilo integrato è pensato per i kernel binari di Ubuntu 23.04
(linux-image-6.2.0-20-generic 6.2.0-20.20). Per testare altri kernel, estrai i
simboli in un file profile:
modprobe nf_tables
egrep ' (nft_counter_ops|nft_counter_destroy|free_percpu|modprobe_path)(\s|$)' /proc/kallsyms > profile
Il layout del codice macchina di nft_counter_destroy() varia in base a
compilatore e opzioni; vedere ORIGIN.md per il riferimento completo
dei parametri (nft_counter_destroy_call_offset/mask/check) e per le manopole di
regolazione della gara (race_lead_sleep ecc.). La probabilità di successo
riportata è ≥80% su sistemi Intel bare-metal in idle; alcune microarchitetture
(es. Alder Lake) richiedono ulteriori regolazioni.
Il PoC lascia il kernel in uno stato instabile con memoria corrotta dopo un'esecuzione riuscita. Testare solo su un sistema dedicato e usa-e-getta o su uno snapshot di una VM — mai su qualcosa che contenga dati a cui tieni.
exploit.c — sorgente del PoC (di @Liuk3r)ORIGIN.md — write-up originale della vulnerabilità e dello sfruttamento (di Liuk3r)README.md — questo file: riproduzione + le mie note di analisiRicerca condotta a scopo difensivo/educativo in un ambiente di laboratorio isolato.
static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
void *elem)
{
return elem + set->ops->elemsize;
}
nft_dynset per due tipi di expr stateful:
nft_counter — fa trapelare nft_counter_ops → base di nf_tables.ko
(aggira KASLR per il modulo);nft_quota — il puntatore consumed consente lettura arbitraria
(NFT_MSG_GETSETELEM → nft_quota_do_dump() → NFTA_QUOTA_CONSUMED) e
scrittura arbitraria (nft_overquota() aggiunge skb->len a
*priv->consumed sul traffico di loopback).modprobe_path ("/sbin/modprobe" → "//tmp/modprobe") →
esecuzione di un processo root con contenuto controllato dall'attaccante.