
Reproduktion und Ursachenanalyse von CVE-2023-32233, einem Use-after-free in Linux-Kernel-nf_tables, der lokale Rechteausweitung ermöglicht, mit PoC und Exploit-Hinweisen.
Worum es in diesem Repo geht: meine praktische Reproduktion und Untersuchung von CVE-2023-32233, einem Use-After-Free im Netfilter-nf_tables-Subsystem des Linux-Kernels, das lokale Rechteausweitung ermöglicht und im Mai 2023 von Patryk Sondej und Piotr Krysiuk öffentlich bekannt gegeben wurde.
Danksagung: der PoC (exploit.c) und der ursprüngliche Write-up stammen von
@Liuk3r. Meine Arbeit in diesem Repo
besteht darin, den Exploit in meinem eigenen Labor (Ubuntu 23.04, Kernel 6.2.0-20)
zu reproduzieren und die Ursache sowie die Exploit-Kette unten zu dokumentieren.
Alle Tests wurden auf dedizierten Laborrechnern durchgeführt.
nf_tables verarbeitet Konfigurationsaktualisierungen als atomaren Batch. Die Validierung jeder Operation gegen die Zustandsänderungen von vorherigen Operationen im selben Batch ist unzureichend. Konkret:
nft_rule, die einen lookup-Ausdruck auf einem
anonymen enthält, das einige Elemente hält.nft_setNFT_MSG_DELRULE — löscht die Regel, was implizit den
lookup-Ausdruck und das anonyme nft_set löscht;NFT_MSG_DELSETELEM — löscht ein Element des bereits gelöschten
anonymen Sets.nf_tables_commit_release() reiht Ressourcen
in nf_tables_destroy_list ein, die später von
nf_tables_trans_destroy_work() verarbeitet werden:
zuerst nft_commit_release() → nf_tables_rule_destroy() →
nft_lookup_destroy() → nft_set_destroy() → kvfree() gibt das
nft_set frei;
dann ruft für NFT_MSG_DELSETELEM nf_tables_set_elem_destroy()
nft_set_elem_ext() auf, was das freigegebene nft_set dereferenziert:
static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
void *elem)
{
return elem + set->ops->elemsize;
}
Wenn set->ops->elemsize korrumpiert ist, wird eine vom Angreifer gewählte
Speicherstelle als nft_set_ext interpretiert — das Primitiv, auf dem alles
Weitere aufbaut.
nf_tables_trans_destroy_work(), das auf einem
Hintergrund-Worker-Thread läuft: eine große Set-Destroy-Operation als
kontrollierte Verzögerung einfügen, andere CPUs beschäftigt halten und den
freigegebenen nft_set-Chunk von derselben CPU mit einem nft_set eines
anderen Typs (anderes elemsize) neu belegen → Typverwechslung.nft_set_ext-Header mit außerhalb des gültigen Bereichs
liegenden Offsets erzeugen, sodass nf_tables_set_elem_destroy() benachbarte
Chunks als Liste von nft_expr durchläuft, um sie zu zerstören.nft_log-Ausdrücke mit kontrolliertem NFTA_LOG_PREFIX sprayen;
nft_log_destroy() gibt priv->prefix frei, was ein überlappendes
Allokationsprimitiv in kmalloc-{8..192} liefert (zunächst durch NULL-Bytes
eingeschränkt).nft_object->udata zurückgewinnen, um die NULL-Byte-Beschränkung
beim Dangling-Read aufzuheben.nft_dynset-Element-Spray für zwei zustandsbehaftete Ausdruckstypen:
nft_counter — leakt nft_counter_ops → Basis von nf_tables.ko
(umgeht KASLR für das Modul);nft_quota — der consumed-Zeiger ermöglicht beliebiges Lesen
(NFT_MSG_GETSETELEM → nft_quota_do_dump() → NFTA_QUOTA_CONSUMED)
und beliebiges Schreiben (nft_overquota() addiert skb->len zu
*priv->consumed bei Loopback-Verkehr).modprobe_path überschreiben ("/sbin/modprobe" → "//tmp/modprobe") →
Ausführung eines Root-Prozesses mit vom Angreifer kontrolliertem Inhalt.Bemerkenswert ist, dass die gewählten Primitive alles vermeiden, was CFI blockieren würde — zu keinem Zeitpunkt ist eine Indirect-Call-Übernahme erforderlich.
SLAB_FREELIST_HARDENED), INIT_ON_FREE und Einschränkungen der
per-CPU-Slab-Wiederverwendung greifen alle Schritt 1 an; CFI (wo vorhanden)
beschränkt Schritt 3+.nf_tables-Batch-Netlink-Nachrichten,
die DELRULE+DELSETELEM auf anonymen Sets enthalten; Crash-Triage für
nft_set_elem_ext / nf_tables_trans_destroy_work im Stack.nftables erfordert CAP_NET_ADMIN,
aber unprivilegierte User-Namespaces gewähren es — deshalb ist
kernel.unprivileged_userns_clone=0 ein sinnvoller Hardening-Schalter.Getestet unter 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
Das eingebaute Profil zielt auf die Ubuntu-23.04-Binärkernel
(linux-image-6.2.0-20-generic 6.2.0-20.20). Um andere Kernel zu testen,
extrahiere Symbole in eine profile-Datei:
modprobe nf_tables
egrep ' (nft_counter_ops|nft_counter_destroy|free_percpu|modprobe_path)(\s|$)' /proc/kallsyms > profile
Das Maschinencode-Layout von nft_counter_destroy() variiert je nach Compiler
und Optionen; siehe ORIGIN.md für die vollständige
Parameterreferenz (nft_counter_destroy_call_offset/mask/check) und
Race-Tuning-Schalter (race_lead_sleep usw.). Die berichtete
Erfolgswahrscheinlichkeit liegt bei ≥80 % auf unbelasteten Bare-Metal-Intel-
Systemen; einige Mikroarchitekturen (z. B. Alder Lake) benötigen zusätzliches
Tuning.
Der PoC hinterlässt den Kernel nach einem erfolgreichen Lauf in einem instabilen Zustand mit korrumpiertem Speicher. Nur auf einem dedizierten, entbehrlichen System oder VM-Snapshot testen — niemals auf etwas mit Daten, die dir wichtig sind.
exploit.c — PoC-Quellcode (von @Liuk3r)ORIGIN.md — ursprünglicher Write-up zu Schwachstelle & Ausnutzung (von Liuk3r)README.md — diese Datei: Reproduktion + meine AnalysenotizenForschung für defensive/edukative Zwecke in einer isolierten Laborumgebung durchgeführt.