
Eine Linux-bezogene Schwachstelle
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, der lokale Privilegienerweiterung ermöglicht, im Mai 2023 von Patryk Sondej und Piotr Krysiuk öffentlich offengelegt.
Danksagung: Der PoC (exploit.c) und der ursprüngliche Bericht 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 Grundursache sowie die Exploit-Kette unten zu dokumentieren. Alle Tests
wurden auf dedizierten Labormaschinen durchgeführt.
nf_tables verarbeitet Konfigurationsupdates als atomaren Batch. Die Validierung jeder Operation gegen die Zustandsänderungen vorheriger Operationen im selben Batch ist unzureichend. Konkret:
nft_rule, die einen lookup-Ausdruck auf einem
anonymen nft_set enthält, das einige Elemente hält.NFT_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 die nf_tables_destroy_list ein, die später von
nf_tables_trans_destroy_work() verarbeitet wird:
zuerst gibt nft_commit_release() → nf_tables_rule_destroy() →
nft_lookup_destroy() → nft_set_destroy() → kvfree() das
nft_set frei;
dann ruft für NFT_MSG_DELSETELEM nf_tables_set_elem_destroy()
nft_set_elem_ext() auf, das das freigegebene nft_set dereferenziert:
Wenn set->ops->elemsize korrumpiert ist, wird eine vom Angreifer gewählte Speicherstelle
als nft_set_ext interpretiert — das Primitive, auf dem alles andere
aufbaut.
nf_tables_trans_destroy_work(), das auf einem
Hintergrund-Worker-Thread läuft: Füge eine große Set-Destroy-Operation als
kontrollierte Verzögerung ein, halte andere CPUs beschäftigt und allokiere den
freigegebenen nft_set-Chunk auf derselben CPU mit einem nft_set eines anderen
Typs (andere elemsize) neu → Typverwechslung.nft_set_ext-Header mit Offsets außerhalb des gültigen Bereichs, sodass
nf_tables_set_elem_destroy() benachbarte Chunks als Liste von
zu zerstörenden nft_expr durchläuft.nft_log-Ausdrücke mit kontrolliertem NFTA_LOG_PREFIX;
nft_log_destroy() gibt priv->prefix frei, was ein Primitive für überlappende
Allokationen in kmalloc-{8..192} ergibt (zunächst auf NULL-Bytes beschränkt).Bemerkenswerterweise vermeiden die gewählten Primitive alles, was CFI blockieren würde — in keinem Schritt ist ein Hijacking indirekter Aufrufe nötig.
SLAB_FREELIST_HARDENED), INIT_ON_FREE und Einschränkungen der Pro-CPU-Slab-
Wiederverwendung greifen alle Schritt 1 an; CFI (wo vorhanden) beschränkt Schritt 3 und folgende.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 Härtungsparameter.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 integrierte 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 die Tuning-Parameter für das Rennen
(race_lead_sleep usw.). Die berichtete Erfolgswahrscheinlichkeit liegt bei ≥80 % auf
im Leerlauf befindlichen 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. Teste nur auf einem dedizierten, wegwerfbaren System oder VM-Snapshot — niemals auf etwas mit Daten, die dir wichtig sind.
exploit.c — PoC-Quellcode (von @Liuk3r)ORIGIN.md — ursprünglicher Schwachstellen- und Exploit-Bericht (von Liuk3r)README.md — diese Datei: Reproduktion + meine Analyse-NotizenForschung zu Verteidigungs-/Bildungszwecken in einer isolierten Laborumgebung durchgeführt.
static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
void *elem)
{
return elem + set->ops->elemsize;
}
nft_object->udatanft_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 — 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-Traffic).modprobe_path ("/sbin/modprobe" → "//tmp/modprobe") →
Ausführung eines Root-Prozesses mit vom Angreifer kontrolliertem Inhalt.