CVE-2026-40931: Der compressing-Patch für CVE-2026-24884 lässt sich komplett umgehen — per git-geliefertem Symlink
7. Juni 2026. Eine Anfang Juni öffentlich aufgearbeitete Schwachstelle (CVE-2026-40931, High) hebt den Patch für die ältere Path-Traversal-Lücke CVE-2026-24884 in der Node-Bibliothek compressing vollständig aus: Der Fix prüft Pfade nur als Zeichenkette und übersieht, dass ein Verzeichnis-Segment auf der Platte ein Symlink sein kann. Der eigentliche Dreh ist der Lieferweg: Der Symlink steckt nicht im Archiv, sondern wird per git clone vorab auf der Maschine platziert, weil Git Symlinks originalgetreu wiederherstellt. Beim späteren Entpacken folgt fs.writeFile dem vorgepflanzten Symlink und schreibt außerhalb des Zielordners, bis hin zu /etc/passwd — ohne Interaktion über den normalen Entwickler-Workflow hinaus, besonders in CI/CD.
TL;DR — 90 Sekunden
- Betroffen?
compressing>= 2.0.0, <= 2.1.0und<= 1.10.4. Konkret: jeder Node-Dienst oder CI-Job, der mitcompressingein nicht vertrauenswürdiges Archiv in ein Verzeichnis entpackt, in dem zuvor ein Repository geklont wurde.- Risiko?
Arbitrary File Write außerhalb des Zielverzeichnisses (CWE-59, Link Following) → Überschreiben sensibler Dateien, daraus Privilege Escalation und RCE (z. B. via
.bashrc, Startup-Skripte, Binaries); CVSS v3 8.4 (High).- Sofortmaßnahme?
Auf
compressing@>=2.1.1bzw.>=1.10.5aktualisieren; CI-Pipelines auditieren, die externe Repos klonen und danach Archive entpacken.- Empfehlung?
Mid-Market: Dependency-Pin ziehen, Extraktion in restriktiver Umgebung. Enterprise/Kubernetes: Extraktions-Jobs unprivilegiert und mit Read-only-Root fahren,
lstat-Prüfung in eigener Entpack-Logik nachrüsten.- Kritikalität?
high (referenziert das Hero-Badge — vollständiger Patch-Bypass mit File-Write-Primitive; im Wochenfenster patchen).
Was ist das Problem?
compressing ist eine in der Node-Welt verbreitete Bibliothek zum Packen und Entpacken von Archiven (tar, gzip, zip). Die ältere Lücke CVE-2026-24884 war ein klassisches Symlink-Path-Traversal: Ein im Archiv eingebetteter Symlink konnte beim Entpacken aus dem Zielverzeichnis herausweisen, sodass Schreibzugriffe an beliebige Stellen des Dateisystems landeten. Der Patch dafür führte eine Hilfsfunktion isPathWithinParent() ein, die prüft, ob der aufgelöste Zielpfad mit dem erlaubten Zielverzeichnis beginnt.
Genau diese Prüfung ist der Fehler. Sie arbeitet rein auf der Zeichenkette: path.resolve() ist in Node ein reiner String-Manipulator, der .. und . rechnerisch auflöst — er schaut nie auf die Platte. Ob ein Segment namens config ein echtes Verzeichnis oder ein Symlink ist, weiß path.resolve() nicht. Liegt das Ziel bei /app/out und löst der Eintrag zu /app/out/config/passwd auf, besteht die startsWith-Prüfung, auch wenn /app/out/config in Wahrheit ein Symlink auf /etc ist. Der Sicherheitsprüfer validiert eine logische Zeichenkette, der OS-Kernel führt anschließend einen physischen Schreibvorgang aus, der dem Symlink folgt. Diese Divergenz zwischen „geprüft“ und „ausgeführt“ ist die ganze Schwachstelle.
Der zweite, entscheidende Teil ist der Lieferweg. Der Angreifer muss nichts ins Archiv einbetten. Stattdessen wird der Symlink vorab auf der Opfer-Maschine platziert: Ein angreifer-kontrolliertes Git-Repository enthält einen Symlink (etwa config_file → /etc/passwd). Git behandelt Symlinks als First-Class-Objekte und stellt sie beim git clone originalgetreu wieder her. Entpackt die Anwendung danach einen Tarball mit einem Eintrag namens config_file, besteht die String-Prüfung — und der Write läuft durch den vorgepflanzten Symlink.
Wer ist betroffen?
| Betroffen | Nicht betroffen | Bedingungen / verschärfend |
|---|---|---|
Anwendungen/CI mit compressing>= 2.0.0, <= 2.1.0 oder <= 1.10.4, die ein nicht vertrauenswürdiges Archiv entpacken | Anwendungen auf compressing >= 2.1.1 bzw. >= 1.10.5 | Im Zielverzeichnis existiert (oder wird zuvor geklont) ein angreifer-kontrollierter Symlink mit passendem Namen |
| CI/CD-Jobs, die externe Repos klonen und danach Archive im selben Arbeitsverzeichnis verarbeiten | Pipelines, die Extraktion strikt von Klon-Verzeichnissen trennen und unter unprivilegiertem User isolieren | Git stellt den Symlink beim git clone automatisch wieder her — kein Social Engineering, keine Zusatzinteraktion nötig |
| Prozesse, die als Root/Admin entpacken | Entpack-Prozesse mit Read-only-Root-FS und ohne Schreibrechte außerhalb eines Scratch-Volumes | Hochprivilegierte Extraktion erlaubt Überschreiben von /etc/passwd, /etc/shadow, Binaries, Startup-Skripten |
Eigene Entpack-Logik, die Pfade nur per path.resolve()/startsWith validiert | Logik, die jedes Pfad-Segment per fs.lstatSync() gegen die Platte prüft (wie node-tar) | Die String-Prüfung kennt den Plattenzustand nicht |
Der relevante Filter für unseren Stack: compressing taucht häufig transitiv in Build- und Tooling-Ketten auf, nicht nur als direkte Dependency. Ein npm ls compressing über alle Services und CI-Images ist deshalb aussagekräftiger als ein Blick in die jeweilige package.json.
Auswirkungen
Die Primitive ist ein Arbitrary File Write außerhalb des Zielverzeichnisses. Das GitHub-Advisory bewertet die Lücke mit CVSS v3 8.4 (High): lokaler Angriffsvektor, geringe Komplexität, keine Rechte und keine Nutzerinteraktion vorausgesetzt, mit hoher Auswirkung auf Vertraulichkeit, Integrität und Verfügbarkeit. „Lokal“ ist hier irreführend harmlos — der Lieferweg über git clone macht aus der lokalen Primitive einen Lieferketten-Vektor: Wer ein präpariertes Repo klont und im normalen Workflow ein Archiv entpackt, ist getroffen.
Aus einem File Write folgt in der Praxis schnell mehr. Über das Überschreiben von .bashrc, .profile oder Startup-Skripten wird daraus Codeausführung beim nächsten Login oder Boot; über das Überschreiben von Binaries oder Konfigurationsdateien Privilege Escalation. In CI/CD ist die Lage besonders unangenehm, weil automatisierte Jobs routinemäßig fremde Repos klonen und Archive ohne menschliche Prüfung verarbeiten — genau die Bedingung, die der Exploit braucht.
Der eigentliche Lehrwert liegt in der Patch-Geschichte. CVE-2026-40931 ist kein neuer Bug-Typ, sondern ein unvollständiger Fix: Die erste Korrektur bekämpfte das Symptom (Symlinks im Archiv) mit einer Zeichenketten-Prüfung, ließ aber den Plattenzustand außen vor. Ein „Partial Fix Bypass“ ist deshalb so wertvoll für die eigene Praxis, weil er zeigt, dass ein eingespielter Patch nicht gleich „erledigt“ heißt — die Frage ist immer, ob die Prüfung dieselbe Realität betrachtet wie die Ausführung.
Mitigation / Sofortmaßnahmen
Hinweis: Die folgenden Schritte sind unsere operative Empfehlung auf Basis des dokumentierten Advisories — keine vom Hersteller zertifizierte Anleitung.
Operational Decision Block
- Sofort handeln, wenn … ein Dienst nicht vertrauenswürdige Archive mit
compressing <= 2.1.0/<= 1.10.4entpackt und dabei in einem Verzeichnis arbeitet, in dem auch geklont wird. - Wartungsfenster möglich, wenn …
compressingnur intern erzeugte, vertrauenswürdige Archive verarbeitet — dann ist der Update-Pfad Routine, aber fällig. - Reine Awareness, wenn …
compressingnicht im Dependency-Baum steht (pernpm lsverifiziert).
Schritt 1 — Version anheben
# Auf gepatchte Version aktualisieren
npm install compressing@latest
# oder explizit pinnen:
# >= 2.1.1 (2.x-Linie)
# >= 1.10.5 (1.x-Linie)
npm ls compressing # auch transitive Treffer pruefen
Schritt 2 — CI-Klon-/Entpack-Reihenfolge entkoppeln
# Archive NICHT in das Verzeichnis entpacken, in das zuvor geklont wurde.
# Stattdessen: dediziertes, leeres Scratch-Verzeichnis pro Job.
work="$(mktemp -d)"
tar -xf payload.tar -C "$work" --no-same-owner
# Extraktion unprivilegiert ausfuehren, nicht als root.
Schritt 3 — Eigene Entpack-Logik state-aware machen
// Statt reiner String-Pruefung jedes Segment auf der Platte verifizieren:
const fs = require('fs');
const path = require('path');
function assertNoSymlinkInPath(childPath, parentPath) {
const dest = path.resolve(parentPath);
const target = path.resolve(childPath);
if (target !== dest && !target.startsWith(dest + path.sep)) {
throw new Error('path escapes destination');
}
let cur = dest;
for (const part of path.relative(dest, target).split(path.sep)) {
if (!part || part === '.') continue;
cur = path.join(cur, part);
try {
if (fs.lstatSync(cur).isSymbolicLink()) {
throw new Error(`symlink segment detected: ${cur}`);
}
} catch (err) {
if (err.code === 'ENOENT') break; // Pfad existiert noch nicht -> ok
throw err;
}
}
}Detection / Prüfung
Inventur — wo steckt compressing?
# Ueber alle Repos/Images: betroffene Versionen finden
grep -REl '"compressing"' --include=package-lock.json .
# je Treffer die aufgeloeste Version pruefen:
npm ls compressing
Laufzeit — Schreibzugriffe außerhalb des Scratch-Volumes
Falco-Regel (Beispiel), die Writes durch Entpack-Prozesse außerhalb des erlaubten Pfads meldet:
- rule: compressing extraction writes outside scratch
desc: Node-Prozess schreibt ausserhalb des erlaubten Entpack-Verzeichnisses
condition: >
open_write and proc.name=node
and not fd.name startswith /scratch/
and (fd.name startswith /etc/ or fd.name startswith /root/ or fd.name startswith /home/)
output: "Write outside scratch (file=%fd.name proc=%proc.cmdline)"
priority: WARNING
Tetragon-Alternative: eine TracingPolicy auf security_inode_link/security_path_symlink im Build-Namespace, die Symlink-Erstellung mit Zielen außerhalb des Arbeitsverzeichnisses protokolliert. Für die schnelle Prüfung reicht oft schon, einen Test-Klon eines bekannten PoC-Repos in einer Sandbox zu fahren und zu beobachten, ob die gepatchte Version die Extraktion mit einer Sicherheits-Exception abbricht.
Betreiberempfehlung
Die Linie ist über alle Betriebsmodelle dieselbe: Patchen ist nötig, aber die strukturelle Lehre ist die Trennung von Klon- und Entpack-Verzeichnissen plus unprivilegierte Extraktion.
Mittelstand
Den compressing-Pin auf >= 2.1.1 / >= 1.10.5 ziehen und in der CI ein dediziertes Scratch-Verzeichnis pro Job erzwingen. Wer fremde Repos klont und danach Archive verarbeitet, sollte beides räumlich trennen — das kostet wenig und nimmt dem Vektor die Grundlage.
Enterprise
SBOM-gestützt alle Dienste mit compressing im Baum (auch transitiv) erfassen, den Patch über die Release-Pipeline ausrollen und Extraktions-Jobs grundsätzlich unprivilegiert mit Read-only-Root und einem einzigen beschreibbaren Scratch-Mount fahren.
Kubernetes
Entpack-Workloads mit readOnlyRootFilesystem: true, runAsNonRoot: true und einem emptyDir-Scratch laufen lassen; damit läuft der File Write ins Leere, selbst wenn eine ungepatchte Version übersehen wurde. Eine Falco-/Tetragon-Regel auf Writes außerhalb des Scratch-Mounts ergänzt die Defense-in-Depth.
Deklarative Stacks (NixOS/Talos/Flatcar)
Den Versions-Pin deklarativ verankern und Build-Runner so härten, dass Extraktionsschritte keine Schreibrechte außerhalb ihres Arbeitsbereichs haben. Der unveränderliche Host nimmt dem Überschreiben von Systemdateien ohnehin die Wirkung.
Was wir konkret getan haben
Wir haben unsere Node- und Tooling-Images gegen compressing inventarisiert, die wenigen transitiven Treffer auf die gepatchte Linie gezogen und in den betroffenen Pipelines die Klon- von den Entpack-Verzeichnissen getrennt. Extraktion läuft bei uns ohnehin unprivilegiert mit Read-only-Root und einem einzigen Scratch-Mount; diese Schicht hätte den File Write auch bei einer übersehenen Version aufgefangen — was die zweite Lehre dieses Falls ist.
Die erste Lehre ist die über Patches: CVE-2026-40931 ist der Beweis, dass „Patch eingespielt“ nicht „Problem gelöst“ bedeutet, wenn die Prüfung in einem anderen Modell denkt als die Ausführung. Der erste Fix prüfte Zeichenketten, das Dateisystem aber führt physische Pfade aus. Wer eigene Entpack-, Upload- oder Pfad-Validierungslogik betreibt, sollte sie state-aware machen: jedes Segment per lstat gegen die Platte prüfen, wie es node-tar vormacht — nicht der String entscheidet, sondern was wirklich auf der Platte liegt. Und der git-clone-Lieferweg ist die unangenehme Pointe gegenüber der Worm-Welle der letzten Tage: Dort galt „Klonen sicher, Öffnen nicht“; hier ist das Klonen selbst der Lieferweg, weil Git den Symlink originalgetreu mitbringt.
Häufige Fragen zu CVE-2026-40931 (compressing)
Reicht npm install --ignore-scripts gegen CVE-2026-40931?+
Nein. Diese Lücke braucht keinen Install-Hook — sie wird beim Entpacken eines Archivs durch die Anwendung selbst ausgelöst. --ignore-scripts adressiert einen anderen Vektor. Hier hilft nur das Versions-Update (>= 2.1.1 / >= 1.10.5) plus die Trennung von Klon- und Entpack-Verzeichnis.
Wie prüfe ich, ob compressing transitiv in meinem Stack steckt?+
npm ls compressing über jedes Projekt und jedes CI-Image; der Treffer in einer transitiven Dependency zählt genauso wie eine direkte. Ergänzend über alle package-lock.json greppen und je Fund die aufgelöste Version gegen <= 2.1.0 / <= 1.10.4 abgleichen.
Sind reine Laufzeit-Container ohne git betroffen?+
Der git-clone-Lieferweg entfällt dort, aber ein im Image vorhandener oder über ein gemountetes Volume eingebrachter Symlink kann denselben Effekt haben. Patchen bleibt richtig, und Archive gehören in ein dediziertes, leeres Scratch-Verzeichnis.
Müssen Kubernetes-Pods, die nur intern erzeugte Archive entpacken, sofort handeln?+
Das Risiko ist niedriger, weil Archiv und Zielverzeichnis vertrauenswürdig sind. Trotzdem den Versions-Pin im nächsten Wartungsfenster ziehen — der Aufwand ist minimal. Wer ohnehin mit readOnlyRootFilesystem: true und emptyDir-Scratch fährt, hat die Auswirkung schon entschärft.
Warum gilt das als High, obwohl der Angriffsvektor Local ist?+
Weil der git-clone-Lieferweg aus der lokalen Primitive einen Lieferketten-Vektor macht: Ein präpariertes Repo plus normaler Entwickler-/CI-Workflow genügen, ganz ohne Zusatzrechte oder Interaktion. Das GitHub-Advisory bewertet die Lücke entsprechend mit CVSS v3 8.4.
Was unterscheidet diese Lücke von der Miasma-/IronWorm-Welle?+
Das ist kein selbst-replizierender Worm und kein Install-Hook-Missbrauch, sondern ein Bibliotheks-Patch-Bypass mit File-Write-Primitive. Gemeinsamer Nenner ist nur der Lieferketten-Charakter über git/CI — und die Pointe: bei der Worm-Welle galt „Klonen sicher, Öffnen nicht“, hier ist das Klonen selbst der Lieferweg.
Fazit
CVE-2026-40931 ist keine spektakuläre neue Angriffsklasse, sondern eine lehrreiche: ein Sicherheits-Patch, der die richtige Gefahr mit der falschen Methode bekämpft. Die String-Prüfung sah sicher aus, kannte aber den Plattenzustand nicht, und der Angreifer liefert den entscheidenden Plattenzustand bequem per git clone. Die operative Schwere ist real, aber gut beherrschbar: Version anheben, Klon- und Entpack-Verzeichnisse trennen, unprivilegiert entpacken. Die wichtigste Empfehlung ist die strukturelle — eigene Pfad-Validierung state-aware machen und Patches daran messen, ob die Prüfung dieselbe Realität betrachtet wie die Ausführung. Weder dramatisieren noch verharmlosen: ein klarer Patch-Fall mit einer Pointe, die über diesen einen Bug hinaus trägt.
Quellen
- GitHub Advisory Database: CVE-2026-40931 / GHSA-4c3q-x735-j3r5 — Complete Bypass of CVE-2026-24884 Patch via Git-Delivered Symlink Poisoning in compressing (High, CVSS 8.4)
- Cyber Security News: Node.js Compression Library Vulnerable Again After CVE-2026-24884 Bypass (03.06.2026)
- node-modules/compressing — Repository und Release-Notes (gepatchte Versionen 2.1.1 / 1.10.5)
- CWE-59: Improper Link Resolution Before File Access (Link Following)
Wir prüfen, mitigieren und validieren Ihre Node- und CI-Pipelines gegen Symlink- und Lieferketten-Vektoren.
SBOM-Inventur über alle Services und Images, Patch-Rollout im Wartungsfenster, Trennung von Klon- und Entpack-Verzeichnissen, unprivilegierte Extraktion mit Read-only-Root — und eine PoC-Validierung, die zeigt, dass die gepatchte Version wirklich abbricht.
Das ist Plattformbetrieb statt Beratung-on-paper: Wir härten Ihre CI/CD-Strecke gegen genau diese „Patch-eingespielt-aber-nicht-erledigt“-Fälle.

