SecurityDennis7 min Lesezeit1.334 Wörter

Node2Shell (CVE-2026-111497): Node.js Brotli-Lücke prüfen und fixen

Node2Shell, CVE-2026-111497: Brotli-Lücke in Node.js, Bun und mehr (CVSS 9.2). So prüfst du Server und Container, patchst und entschärfst Express 5 und fetch() sofort.

Bei Node2Shell zählt zuerst, ob ein Dienst fremde Daten entpackt. Die Antwort lautet häufiger “ja”, als man denkt: Ein kleiner Express-5-Dienst, der JSON entgegennimmt, nutzt genau einen der Standardpfade, über die die Lücke erreichbar ist. Node.js hat am 21. Oktober 2026 Sicherheitsupdates veröffentlicht, die CVE-2026-111497 beheben.

Der Fehler sitzt im Brotli-Decoder, den Node.js mitbringt. Eine präparierte Brotli-Datei kann dort einen Schreibzugriff hinter das Ende eines internen Puffers auslösen. Das Ergebnis reicht je nach Fall vom Absturz über Speicherauszug bis zur Codeausführung. Google bewertet die Lücke als Numbering Authority mit CVSS v4.0 9.2, das Node.js-Team selbst stuft die Auswirkung auf die Laufzeit mit “high” ein. Betroffen ist neben Node.js auch Bun, Python-Pakete, Envoy und .NET, sobald sie die verwundbare Brotli-Bibliothek nutzen.

Was passiert beim Dekodieren?

Brotli ist ein Kompressionsformat, das im Web neben gzip steht (Content-Encoding: br). Ein Brotli-Strom kann zwischen verschiedenen Blocktypen wechseln. Meldet ein Strom einen solchen Wechsel, obwohl er laut eigener Struktur nur einen Blocktyp deklariert hat, ist das ein ungültiger Zustand und der Decoder müsste ihn ablehnen.

Genau das tat die betroffene Version nicht. Sie gab für diesen Formatfehler denselben Rückgabewert zurück wie für “ich brauche mehr Eingabedaten”. Der Streaming-Decoder wartete also brav auf mehr, hängte weitere Bytes an einen internen Puffer von nur acht Byte an und schrieb dabei über dessen Ende hinaus in benachbarte Decoder-Daten. Der Fix (Commit 0ed4268) führt einen eigenen Fehlercode ein, BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH, und bricht ab. Interessant: Die Korrektur stand im Brotli-Repository schon Ende Februar 2026, ohne als Sicherheitsfix markiert zu sein.

Wie realistisch ist Remote Code Execution?

Auch hier streiten die Quellen. Imperva berichtet von Codeausführung auf dem offiziellen Image von Node.js 24.22.0 (Linux, arm64), allerdings erst nach mehreren Versuchen und ohne veröffentlichten Exploit. Ein anderes Team kam lokal auf drei von drei Läufen, aber nur mit unverändertem System-Brotli und vorhersagbarem Speicherlayout; blindes Remote-Ausführen mit aktivem ASLR klappte nicht. Eine weitere Analyse hält nur einen Denial-of-Service für realistisch. Sicher reproduzierbar ist der Absturz des Prozesses.

Mein Standpunkt: Ein Dienst, der auf eine Anfrage hin abstürzt, ist schon ein Problem, und ein CVSS von 9.2 plus ein öffentlicher PoC seit dem 22. Oktober reichen mir als Grund zum Handeln. Von Fake-PoC-Repositories auf GitHub lässt du besser die Finger, bei prominenten CVEs stecken dort regelmäßig Schädlinge drin.

Bist du betroffen?

Zwei Fragen entscheiden: Läuft eine verwundbare Version, und entpackt dein Code Brotli aus nicht vertrauenswürdiger Quelle?

Version prüfen

Gepatcht sind diese Stände:

SoftwareGepatcht ab
Node.js 2222.23.4
Node.js 2424.22.1
Node.js 2626.12.1
Bun1.4.4
Python Brotli / brotlicffi1.2.1 / 1.2.1.0
Brotli-Bibliothek1.0.10, 1.1.1, 1.2.1
Envoy1.40.0, 1.39.3, 1.38.6

Node.js 20, 23 und 25 sind am Ende ihrer Lebenszeit und bekommen keinen Fix. Wer noch auf 20 läuft, muss auf 22 oder 24 umziehen, es gibt keinen Patch-Release.

node --version

Die Version meint die Laufzeit, in der dein Dienst wirklich läuft. In Containern also:

docker exec <container> node --version

Bei Verwirrung: process.versions.brotli zeigt auch auf gepatchten Versionen 1.1.0 oder 1.2.0 an, daran kannst du den Patchstand nicht ablesen. Entscheidend ist die Node-Version selbst.

Besondere Vorsicht bei Distributions-Paketen. Das nodejs-Paket von Debian nutzt die System-Brotli-Bibliothek, der Fix muss also im Paket libbrotli1 ankommen. Zum Zeitpunkt der ersten Meldungen gab es für Debian 12 einen Backport (1.0.9-2+deb12u1), andere Releases, Fedora, RHEL 8 bis 10 und SUSE hinkten noch hinterher. Gleicher Befehl, anderes Ergebnis, daher immer gegen das Advisory deiner Distribution prüfen:

apt policy libbrotli1

Erreichbarkeit prüfen

Ohne Brotli-Dekodierung aus fremder Quelle bleibt die Lücke totes Gewicht. Die bekannten Wege:

  • Express 5 mit express.json(), raw(), text() oder urlencoded(): Das darunterliegende body-parser 2.x entpackt Content-Encoding: br standardmäßig, weil inflate auf true steht. Express 4 mit body-parser 1.x antwortet mit 415 und ist über diesen Weg nicht erreichbar.
  • fetch() in Node.js: Brotli-Antworten werden automatisch dekomprimiert. Ein bösartiger oder gekaperter Server, den dein Dienst abfragt, kann also den Client angreifen. Das betrifft Webhooks, Scraper, Feed-Reader oder alles, was externe APIs anspricht.
  • zlib.brotliDecompress() und createBrotliDecompress() mit Nutzerdaten wie Uploads oder Importen.
  • Weitere Frameworks, die Bodys eigenständig entpacken: Koa (über co-body), @koa/bodyparser, koa-body, Egg, Strapi und Fastify mit @fastify/compress.

Ein reiner http.createServer() ohne Middleware dekodiert eingehende Brotli-Bodys nicht von allein.

npm ls express body-parser
grep -rnE "inflate|createBrotliDecompress|brotliDecompress|bodyParser" src/

Auf einem Server mit vielen Diensten lohnt sich außerdem ein Blick auf alles, was eine eigene Node-Laufzeit mitbringt: Electron-Apps, Entwicklerwerkzeuge, CI-Runner, Serverless-Funktionen. Auch bun build --compile-Programme behalten den alten Decoder, bis sie neu gebaut werden. Das gilt ebenso für .NET-NativeAOT-Anwendungen. Der npm-Paketname brotli hat übrigens nichts mit der Lücke zu tun, das ist eine unabhängige JavaScript-Bibliothek.

Fix: Node.js aktualisieren

Der Hersteller nennt keinen Workaround, das Update ist der Weg. Je nachdem, wie du Node installiert hast:

# nvm
nvm install 24 && nvm use 24

# Docker: Image neu bauen, nicht nur ziehen
docker compose build --pull --no-cache
docker compose up -d --force-recreate

# Debian-Paketquelle
sudo apt update && sudo apt install --only-upgrade nodejs libbrotli1

Danach jeden Dienst neu starten (pm2, systemd, Container, Worker, Cronjobs) und mit node --version im laufenden Prozess gegenprüfen. Ein docker pull allein tauscht keinen laufenden Container aus. Homebrew hinkte nach dem Release bei den Paketen hinterher, wer es nutzt, sollte die Version nach dem Update gegenchecken. Bei Python:

pip install -U Brotli brotlicffi
pip show Brotli brotlicffi

Brotli kommt oft als transitive Abhängigkeit mit (etwa über HTTP-Clients), schau deshalb auch in virtuelle Umgebungen, conda und CI-Images.

Entschärfen, wenn du nicht sofort patchen kannst

Das sind Überbrückungen, keine Lösungen.

  • Eingehende Bodys: Brotli am Reverse Proxy ablehnen, zum Beispiel bei nginx:
if ($http_content_encoding ~* "br") { return 415; }

Auch gestapelte Kodierungen wie gzip, br solltest du verwerfen. Erlaube nur identity, gzip und deflate.

  • Express: express.json({ inflate: false }). Das lehnt auch gzip-komprimierte Bodys ab, prüf also vorher, ob deine Clients komprimiert senden.
  • Ausgehende Abfragen: Das Proxy-Filter hilft hier nicht. Prüf, welche fetch()-Aufrufe auf fremde Server zeigen, und begrenze ausgehende Verbindungen des Dienstes. Accept-Encoding ohne br zu senden ist keine Garantie, ein Server kann trotzdem Brotli schicken.
  • WAF: Vercel und Microsoft Azure haben Regeln nachgelegt. Bei Cloudflare stehen die neuen Regeln standardmäßig auf “Log”, sie blockieren also nichts, bis du die Aktion änderst.
  • Schadensbegrenzung: Den Dienst als unprivilegierten Benutzer laufen lassen und Geheimnisse nicht in der Prozessumgebung ablegen. Läuft der Dienst zwar nicht als root, hat aber alle API-Schlüssel als Umgebungsvariablen im Prozess, nützt dem Angreifer auch ohne root-Rechte schon ein einzelner Treffer.

Ausnutzung erkennen

Der eigentliche Überlauf ist für Laufzeit-Sensoren unsichtbar, sichtbar wird erst, was danach passiert. Warnzeichen sind ein Node-Prozess, der abstürzt und kurz darauf ein anderes Programm startet, Kindprozesse wie sh oder bash unter einem Webdienst, umgeleitete Sockets auf Standardstreams und unerwartete ausgehende Verbindungen. Abstürze eines Dienstes ohne erkennbare Ursache nach auffälligen Requests mit Content-Encoding: br sind ebenfalls ein Anhaltspunkt. Eine Aufnahme in den CISA-KEV-Katalog gab es in der ersten Woche nicht, später ist von bestätigten Angriffsversuchen die Rede.

Fazit

Node2Shell trifft Dienste, die Brotli aus unsicherer Quelle entpacken, und das ist mit Express 5 und fetch() schneller der Fall, als man denkt. Patch auf 22.23.4, 24.22.1 oder 26.12.1, Container neu bauen und alle Prozesse neu starten. Wer noch auf Node 20 läuft, hat kein Patch-Fenster mehr und sollte jetzt migrieren, nicht nächstes Quartal. Bis dahin gilt: Content-Encoding: br am Proxy blocken und ausgehende Verbindungen einschränken.

Häufige Fragen

Reicht es, Express 4 statt 5 zu nutzen? ▾

Für den Eingangsweg über body-parser ja, Version 1.x antwortet auf br mit 415. Die Lücke ist damit aber nicht weg: fetch() und direkte zlib-Aufrufe sind unverändert erreichbar, und Express 4 bekommt kein neues Feature, das du dafür bräuchtest. Updaten bleibt Pflicht.

Warum zeigt process.versions.brotli keine gepatchte Nummer? ▾

Node.js übernimmt den Fix als einzelnen Commit in seine mitgelieferte Kopie, ohne die Versionsnummer der Bibliothek anzufassen. Die Anzeige bleibt auf 1.1.0 oder 1.2.0 stehen, selbst nach dem Update. Prüf deshalb die Node-Version, nicht die Brotli-Version.

Ich nutze den Debian-Node.js. Warum ist 22.23.4 dort nicht automatisch sicher? ▾

Das Debian-Paket verwendet die System-Brotli-Bibliothek. Die Versionsnummer von Node sagt dort nichts, solange libbrotli1 nicht aktualisiert ist. Nach dem Upgrade beider Pakete zählt der Patchstand laut Advisory, nicht die Zahl vor dem Punkt.

Ist mein Python-Dienst auch betroffen? ▾

Wenn er das PyPI-Paket Brotli oder brotlicffi in einer Version bis 1.2.0 nutzt und Brotli aus fremder Quelle entpackt, ja. Gleiches gilt für Bun. Beide kompilieren denselben Decoder. Öffentliche Codeausführung wurde dort bisher nicht gezeigt.

Hilft es, Brotli im Browser oder Proxy abzuschalten? ▾

Nur für den Eingangsweg. Server, die du per fetch() abfragst, entscheiden selbst, wie sie ihre Antwort komprimieren, und dein Accept-Encoding ist nur eine Bitte. Gegen manipulierte Antworten hilft nur die gepatchte Laufzeit.

Weitere Artikel