
pedit COW
CVE-2026-46331 (Spitzname „pedit COW“) ist eine lokale Linux-Kernel-Privilegienausweitungs-Schwachstelle im Traffic-Control-Subsystem. Ein unprivilegierter Benutzer (in einem unprivilegierten Netzwerk-Namespace) kann den act_pedit-Filter (Paket-Editor) konfigurieren, um einen partiellen Copy-on-Write (COW)-Schreibvorgang in den Seitencache auszulösen. Im Effekt schreibt der Kernel attacker-kontrollierte Daten in das In-Memory-Abbild einer Datei ohne die Seite als privat zu markieren und beschädigt dadurch die zwischengespeicherte Kopie der Datei. Entscheidend ist, dass der Exploit nur CAP_NET_ADMIN benötigt (erhältlich in einem Benutzer-Namespace) und die Datei auf der Festplatte nicht verändert. In der Praxis wurde am 17. Juni 2026 ein funktionierender Proof-of-Concept (PoC) namens packet_edit_meme veröffentlicht, der zeigt, wie man das Seitencache-Abbild einer setuid-Binärdatei (z.B. /bin/su) überschreibt, um eine Root-Shell zu öffnen. Die Schwachstelle rührt von einer fehlerhaften COW-Bereichsberechnung in tcf_pedit_act() her und wurde upstream (4. Juni 2026) durch Verschiebung der Prüfung des beschreibbaren Bereichs in die Pro-Schleife behoben.
act_pedit. Nicht gepatchte stabile Releases (einschließlich vieler Distro-Kernel) sind verwundbar.tc-pedit-Filter ein und überschreibt den ELF-Einstiegspunkt einer setuid-Binärdatei im Speicher mit Shellcode.skb_ensure_writable() in die Schlüsselschleife). Als Workaround das Modul act_pedit blockieren oder entladen oder unprivilegierte Benutzer-Namespaces deaktivieren (z.B. sysctl user.max_user_namespaces=0). Nach der Abhilfe Caches leeren (echo 3 > /proc/sys/vm/drop_caches), um vergiftete Seiten zu vertreiben.Dieser Bericht bietet eine detaillierte technische Analyse von CVE-2026-46331: Ursache, Ausnutzung, Erkennung und Abhilfestrategien, mit Verweisen auf Herstellerhinweise, CVEs und den öffentlichen Exploit.
Definition: CVE-2026-46331 ist ein Out-of-Bounds-Schreibfehler im Traffic Control (net/sched)-Subsystem des Linux-Kernels, speziell in der act_pedit-Aktion (Paket-Editor). Die Funktion tcf_pedit_act() berechnet einen „Copy-on-Write“-Bereich für Paketbearbeitungsoperationen bevor sie über typisierte Schlüssel iteriert, unter Verwendung eines statischen Hinweises tcfp_off_max_hint. Allerdings bestimmen einige Schlüssel (z.B. TCP/UDP-Headerbearbeitungen) ihren endgültigen Byte-Offset erst zur Laufzeit. Der Code überprüft die Beschreibbarkeit für diese dynamischen Offsets nie erneut. Infolgedessen können Schreibvorgänge außerhalb des zuvor COW-geschützten Bereichs auftreten: Ein Teil des Paketschreibvorgangs wird nie privat gemacht, was zu einem partiellen COW führt. Dieser fehlerhafte Schreibvorgang breitet sich in den gemeinsamen Seitencachespeicher einer Datei aus (falls die Paketpuffer zufällig auf Dateiseiten verweisen) und beschädigt das zwischengespeicherte Dateiabbild.
Hintergrund: Die Linux-Paket-Editor (pedit)-Aktion ermöglicht es Administratoren, beliebige Bytes innerhalb von Paketheadern (Link-, Netzwerk- oder Transportschicht) umzuschreiben, während Pakete einen konfigurierten tc-Filter durchlaufen. Es funktioniert durch Angabe eines Offsets (möglicherweise an einem Header verankert) und eines 32-Bit-Werts/Maske. Intern arbeitet pedit auf Socket-Puffern (sk_buff) und muss den Zielpaketspeicher beschreibbar machen, bevor er ihn modifiziert (über skb_ensure_writable() in COW-Manier). Idealerweise sollte der Kernel alle gemeinsam genutzten Seiten klonen (private Kopie erstellen), bevor er darauf schreibt, um zu vermeiden, dass an anderer Stelle verwendeter Speicher verändert wird.
Grundursache: In tcf_pedit_act() berechnet der Code den beschreibbaren Bereich fälschlicherweise nur einmal vorab, unter Verwendung von tcfp_off_max_hint (dem maximalen statischen Offset). Dieser Hinweis enthält keinen Laufzeit-Header-Offset, den typisierte Schlüssel bei der Verarbeitung des Pakets hinzufügen. Schlüssel wie TCP oder UDP können einen Offset basierend auf der Position des IP-Headers zur Laufzeit berechnen (z.B. wenn ein früherer Schlüssel den Netzwerk-Header verschiebt). Somit kann während der Schleife der tatsächliche Offset für einen Schlüssel den Bereich überschreiten, der als beschreibbar vorab zugewiesen wurde. Der Code schreibt dann über skb_store_bits() in den Paketspeicher, aber da die Seite außerhalb des zuvor COW-geschützten Bereichs nicht privat gemacht wurde, beschädigt der Schreibvorgang eine Seite, die noch mit dem Seitencache geteilt wird. Kurz gesagt: „Den beschreibbaren Paketbereich zu früh berechnen“ verursacht einen Out-of-Bounds-Schreibvorgang über mehrere Seiten. Negative Offsets (z.B. Bearbeitung von Ethernet-Headern beim Eingang) werden ebenfalls falsch behandelt, und sogar offset_valid() enthält keine Prüfung auf INT_MIN, was die Schwachstelle verstärkt.
Warum es passiert: Dieser Fehler ist im Wesentlichen ein Logikfehler in der Berechnung des Copy-on-Write-Bereichs. Der Kernel nahm an, dass der statische maximale Offset (zum Ladezeitpunkt bekannt) für alle Bearbeitungen ausreiche. Er versäumte es, den COW-Bereich zu aktualisieren, wenn Schlüssel mit dynamischen Offsets tatsächlich angewendet wurden. Nach einer Reihe von Bearbeitungsschritten konnte der endgültige Schreibvorgang außerhalb des vorab geprüften Bereichs liegen. Da Paketpuffer auf speichergemappte Dateiseiten verweisen können (z.B. über Zero-Copy-Mechanismen), kann dieser „partielle COW“-Schreibvorgang den Seitencache einer Datei auf der Festplatte erreichen, ohne die Festplatte zu verändern.
Components and Data Flow: Der verwundbare Code befindet sich im Linux-net/sched-Subsystem (act_pedit.c). Wenn ein Paket eine konfigurierte pedit-Regel erfüllt, wird tcf_pedit_act() aufgerufen. Intern ruft es genau einmal skb_ensure_writable(skb, X) auf, wobei X = tcfp_off_max_hint ist. Dadurch werden die ersten X Bytes des Pakets privat gemacht (COW). Dann wird in einer Schleife über jeden Schlüssel (Bearbeitungsoperation) der tatsächliche Schreiboffset des Schlüssels berechnet, indem der Laufzeit-Header-Offset zum angegebenen Offset des Schlüssels addiert wird, und ein 32-Bit-Wert in das Paket geschrieben. In Pseudocode:```c
u32 off_max = action->tcfp_off_max_hint;
skb_ensure_writable(skb, off_max);
for (i = 0; i < num_keys; i++) {
u32 hdr_off = compute_header_offset(skb, key[i].hdr_type);
u32 write_off = hdr_off + key[i].offset;
skb_store_bits(skb, write_off, &key[i].value, 4);
}
Da `hdr_off` nur bei der Verarbeitung jedes Schlüssels berechnet wird, berücksichtigte der anfängliche `skb_ensure_writable()`-Aufruf dies nicht. Wenn `hdr_off + key[i].offset` `off_max` überschreitet, greift der Code auf `skb_store_bits()` auf Fragmenten zurück, anstatt auf den linearen Hauptbereich, was bedeutet, dass er in eine Seite schreibt, die nicht privat gemacht wurde. Das ist der Fehlerpunkt.