flawopen.com/Teardowns/cve-2024-26642-linux-nftables-anonymous-set-timeout
CVE-2024-26642: Anonymes Set mit Timeout-Flag in Linux nf_tables
CVE-2024-26642 (CVSS 7.8 laut NIST): nf_tables_newset() akzeptierte anonyme Sets, die zusätzlich das Timeout-Flag trugen – eine Kombination, die kein Userspace-Werkzeug erzeugt. Dadurch unterlagen an eine Regel gebundene Sets dem Ablauf von Elementen und der Garbage Collection. Linux 6.8 weist sie mit -EOPNOTSUPP ab.
Eine Garderobe hat zwei Systeme. Nummerierte Haken gehören einen Abend lang genau einem Gast und werden geräumt, sobald er geht. Zeitmarken sammelt stündlich ein Mitarbeiter ein, der alles Abgelaufene entfernt. Das Formular erlaubt, für denselben Mantel beide Kästchen anzukreuzen: Der Haken wird dann beim Gehen des Gasts geräumt und zusätzlich nach dem Plan des Mitarbeiters. Zwei Leute halten sich für denselben Haken zuständig, und keiner kennt den Zeitplan des anderen. Die Lösung ist einfach: Das Formular lehnt dieses Kästchenpaar jetzt ab.
Kernkonzepte & Begriffe
nf_tables set- Kernel-Datenstruktur mit Adressen, Ports oder anderen Schlüsseln, gegen die nftables-Regeln prüfen; angelegt per Netlink-Nachricht
NFT_MSG_NEWSETund verarbeitet vonnf_tables_newset(). Anonymes Set (NFT_SET_ANONYMOUS)- Ein unbenanntes Set wie
{ 22, 80 }, das direkt in einer Regel steht. Es ist an genau diese Regel gebunden und wird in derselben Transaktion mit ihr zerstört. NFT_SET_TIMEOUT- Flag, das Set-Elementen eine Ablaufzeit gibt. Abgelaufene Elemente entfernt die Garbage Collection des Sets.
NFT_SET_EVAL- Flag für Sets, die aus dem Paketpfad aktualisiert werden, genutzt von alten
meter-Anweisungen. Der Fix lässt anonym + Timeout + Eval für sie weiter zu. CAP_NET_ADMIN in einem User-Namespace- nf_tables nimmt Konfiguration von jedem Prozess mit CAP_NET_ADMIN in seinem Netzwerk-Namespace an. Ein unprivilegierter Benutzer erhält dieses Recht, indem er einen User-Namespace anlegt, sofern die Distribution das erlaubt.
Ursachenanalyse
nf_tables_newset() in net/netfilter/nf_tables_api.c prüfte die Set-Flags nur auf unbekannte Bits und auf die Konflikte MAP+OBJECT und EVAL+OBJECT. NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT wurde akzeptiert, sodass ein Set, dessen Lebensdauer an eine einzige Regel und Transaktion gebunden ist, zusätzlich Elemente haben konnte, die ablaufen und von der Garbage Collection eingesammelt werden. Userspace erzeugt diese Kombination nie, und der Lebensdauer-Code war nicht dafür ausgelegt. Der Upstream-Fix lehnt sie mit -EOPNOTSUPP ab, sofern nicht zusätzlich NFT_SET_EVAL gesetzt ist. Den daraus folgenden Speicherfehler beschreibt die öffentliche Dokumentation nicht im Detail; das NIST bewertet ihn mit 7.8 (Vertraulichkeit, Integrität und Verfügbarkeit hoch), die Kernel-CNA mit 5.5 (nur Verfügbarkeit).
Schritt-für-Schritt Angriffsablauf
CAP_NET_ADMIN erlangen
Ein lokaler Benutzer legt einen User- und einen Netzwerk-Namespace an (wo unprivilegierte User-Namespaces erlaubt sind) und besitzt damit CAP_NET_ADMIN über den nf_tables-Zustand dieses Namespace.
Das ungültige Set anfordern
In einem Netlink-Batch sendet der Prozess NFT_MSG_NEWSET mit NFTA_SET_FLAGS = NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT und bindet das Set über einen Lookup-Ausdruck an eine neue Regel.
Der Kernel akzeptiert
nf_tables_newset() findet kein verbotenes Flag-Paar und legt ein regelgebundenes anonymes Set an, dessen Elemente eine Ablaufzeit haben.
Ungetestete Lebensdauerpfade
Commit, Abbruch oder Löschen der Regel lösen nun gleichzeitig den Abbau des Sets und die Garbage Collection der Elemente aus – in Pfaden, die der Transaktionscode von nf_tables nicht sicher behandelt.
Quellcode: Verwundbar vs. Sicher
/* net/netfilter/nf_tables_api.c: nf_tables_newset(), before the fix */
if (nla[NFTA_SET_FLAGS] != NULL) {
flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
NFT_SET_INTERVAL | NFT_SET_TIMEOUT |
NFT_SET_MAP | NFT_SET_EVAL |
NFT_SET_OBJECT | NFT_SET_CONCAT | NFT_SET_EXPR))
return -EOPNOTSUPP;
/* Only one of these operations is supported */
if ((flags & (NFT_SET_MAP | NFT_SET_OBJECT)) ==
(NFT_SET_MAP | NFT_SET_OBJECT))
return -EOPNOTSUPP;
if ((flags & (NFT_SET_EVAL | NFT_SET_OBJECT)) ==
(NFT_SET_EVAL | NFT_SET_OBJECT))
return -EOPNOTSUPP;
/* BUG: NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT is accepted. A rule-bound
* anonymous set now also gets per-element expiry and garbage
* collection, a combination nft never creates and the set
* lifetime code was not written for. */
}
/* net/netfilter/nf_tables_api.c: nf_tables_newset(), commit 16603605b667 */
if (nla[NFTA_SET_FLAGS] != NULL) {
flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
NFT_SET_INTERVAL | NFT_SET_TIMEOUT |
NFT_SET_MAP | NFT_SET_EVAL |
NFT_SET_OBJECT | NFT_SET_CONCAT | NFT_SET_EXPR))
return -EOPNOTSUPP;
/* Only one of these operations is supported */
if ((flags & (NFT_SET_MAP | NFT_SET_OBJECT)) ==
(NFT_SET_MAP | NFT_SET_OBJECT))
return -EOPNOTSUPP;
if ((flags & (NFT_SET_EVAL | NFT_SET_OBJECT)) ==
(NFT_SET_EVAL | NFT_SET_OBJECT))
return -EOPNOTSUPP;
/* FIX: anonymous sets are never used with timeouts from userspace,
* so reject the combination before the set exists. NFT_SET_EVAL
* stays allowed so legacy meters keep working. */
if ((flags & (NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT | NFT_SET_EVAL)) ==
(NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT))
return -EOPNOTSUPP;
}
Checkliste für Engineering & Systemsicherheit
- ✓Einen Kernel mit Commit 16603605b667 betreiben (Linux 6.8 oder ein Stable- bzw. Distributionskernel, dessen Changelog
CVE-2024-26642nennt). - ✓Wo Workloads sie nicht brauchen, unprivilegierte User-Namespaces abschalten (
kernel.unter Debian und Ubuntu,unprivileged_userns_clone=0user.oder Ubuntus AppArmor-userns-Beschränkung), damit lokale Benutzer nf_tables nicht erreichen.max_user_namespaces=0 - ✓Auf Hosts ohne nftables das Laden des Moduls
nf_tablesverhindern. - ✓In Kernel- und API-Code die erlaubten Flag-Kombinationen als explizite Allowlist formulieren und alles ablehnen, was kein unterstützter Client erzeugt, statt nur bekannte schlechte Paare abzulehnen.
- ✓Netlink-Schnittstellen mit syzkaller-Beschreibungen fuzzen, die jedes Flag-Bit abdecken, damit ungewöhnliche Kombinationen den Lebensdauer-Code der Objekte zuerst im Test erreichen.