Node.js-Sicherheitslücke: Node2Shell-Patch und Node.js-Version prüfen (WordPress)

Node2Shell ist der Spitzname von CVE-2026-111497, einer Node.js-Sicherheitslücke, deren Patch am 21. Oktober 2026 veröffentlicht wurde; der offizielle Titel beschreibt einen Out-of-Bounds-Write-Fehler im Brotli-Decoder von Google. Weil schon das Entpacken manipulierter Brotli-Daten zu Remotecodeausführung führen kann, prüfst du zuerst die Versionen von Node.js, Bun und libbrotli auf deinem Server und aktualisierst sie auf die gepatchten Versionen.
Auf einem Nginx + Apache-Server mit WordPress 7.1.3 habe ich geprüft, welche Pakete den Brotli-Decoder nutzen und auf welchem Weg Remote-Anfragen von WordPress über libbrotli laufen.
1️⃣ Die Node.js-Sicherheitslücke Node2Shell: wie der Fehler funktioniert und wen er betrifft
Der Name Node2Shell ist ein Spitzname, den das Forschungsteam des Sicherheitsunternehmens Imperva in einem Beitrag vom 22. Oktober vergeben hat. Der Titel der Meldung auf nodejs.org lautet [Brotli decoder out-of-bounds write permits remote code execution]. Der Fehler steckt im Quellcode des Google-Brotli-Decoders, den Node.js mitbringt (c/dec/decode.c), deshalb lässt er sich nicht durch Änderungen am Anwendungscode abstellen.
Laut dem NVD-Eintrag schreibt der Decoder von Google Brotli 1.0.3 bis 1.2.0 beim Entpacken eines manipulierten Streams über das Ende des Puffers hinaus und beschädigt den angrenzenden Zustandsspeicher des Decoders. Über diesen Fehler kann ein entfernter Angreifer ohne Anmeldung Speicherinhalte des Servers auslesen, den Dienst lahmlegen und beliebigen Code ausführen.
Es gibt zwei Schweregrad-Angaben. Die Meldung auf nodejs.org stuft die Lücke als High ein, und Google, das die CVE vergeben hat, bewertet sie nach CVSS 4.0 mit 9,2 (Critical). Die NVD hat ihre eigene Analyse mit Stand 23. Oktober noch nicht abgeschlossen, daher gibt es noch keinen NVD-Wert. Die Schwachstellentypen sind ein ungeprüfter Rückgabewert (CWE-252) und ein Schreibzugriff außerhalb der Grenzen (CWE-787).
Laut der Sicherheitsmeldung auf nodejs.org sind alle unterstützten Node.js-Release-Linien 22.x, 24.x und 26.x betroffen, die gepatchten Versionen sind 22.23.4, 24.22.1 und 26.12.1. Dieselbe Meldung stellt klar, dass Linien ohne Support bei jeder Sicherheitsmeldung grundsätzlich als betroffen gelten.
Jedes Programm, das denselben Decoder nutzt, ist betroffen, auch außerhalb von Node.js. Am 21. Oktober hat Google drei Versionen der Brotli-Bibliothek veröffentlicht, die dieselbe CVE beheben: 1.0.10, 1.1.1 und 1.2.1. Bun hat Brotli am 22. Oktober in der Ankündigung zu 1.4.4 in die Liste der Sicherheitskorrekturen aufgenommen, und ein Issue im Repository hält fest, dass diese Version die Korrektur mit dem brotli-Update enthält.
| Ziel | Betroffene Versionen | Korrigierte Version | Quelle |
|---|---|---|---|
| Node.js | Alle unterstützten Linien 22.x · 24.x · 26.x · Linien ohne Support | 22.23.4 · 24.22.1 · 26.12.1 | Sicherheitsmeldung von nodejs.org (2026-10-21) |
| Brotli-Bibliothek von Google | 1.0.3 ~ 1.2.0 | 1.0.10 · 1.1.1 · 1.2.1 | NVD-Eintrag · google/brotli-Releases (2026-10-21) |
| Bun | Brotli in der Liste der Sicherheitskorrekturen der Ankündigung zu 1.4.4 | 1.4.4 | Bun-Ankündigung (2026-10-22) |
| libbrotli-Paket der Distribution | Pakete auf Basis von Brotli 1.0.3 bis 1.2.0 | Die Version aus der Sicherheitsmeldung der Distribution (Stand 2026-10-23 noch keine) | Security-Tracker-Seite der jeweiligen Distribution |
Node.js selbst entpackt empfangene Brotli-Daten nicht von sich aus. Laut der Sicherheitsmeldung des Hosting-Anbieters Vercel liegen die angreifbaren Stellen dort, wo eine App oder Bibliothek komprimierte Daten von außen entpackt. Beispiele sind der eingebaute fetch(), wenn er eine br-Antwort empfängt, und Express 5 oder body-parser 2, wenn sie einen br-Anfragekörper entpacken; der Standard-HTTP-Server, der nur empfängt, entpackt nichts.
Eine tatsächliche Ausnutzung ist mit Stand 23. Oktober nicht bestätigt. Im Katalog bekannter ausgenutzter Schwachstellen der CISA (KEV, Version vom 21. Oktober) steht diese CVE nicht. Die Ausnutzungsstufe, die die CISA im CVE-Eintrag vergibt, wurde am 23. Oktober auf [Proof of Concept (PoC) öffentlich] geändert.
Wo der Decoder steckt, entscheidet, was zu tun ist. Die offiziellen Node.js-Binärdateien bringen den Decoder in der ausführbaren Datei mit, es gibt aber auch Builds wie das nodejs-Paket von Debian, die die libbrotli des Systems nutzen. Linux-Distributionen installieren denselben Quellcode als Paket libbrotli, das sich Programme wie curl teilen.
2️⃣ Auch WordPress- und PHP-Websites betroffen: der Weg, auf dem Remote-Anfragen Antworten mit libbrotli entpacken
Auch auf einem WordPress-Server ohne Node.js ist derselbe Decoder als Paket libbrotli installiert. Von WordPress, curl und PHP gibt es mit Stand 23. Oktober keine offizielle Aussage zu dieser CVE, und in der offiziellen Schwachstellenliste von curl steht sie auch nicht. Dieser Abschnitt bestätigt daher nur die Struktur – anhand des Core-Codes, der curl-Dokumentation und Messungen auf einem Testserver.
Auf dem gemessenen Nginx + Apache-Server verlangten fünf Pakete die Decoder-Bibliothek (libbrotlidec), und das Paket, das mit WordPress verbunden ist, ist libcurl.
rpm -q --whatrequires 'libbrotlidec.so.1()(64bit)' | sed 's/-[0-9].*//'freetype
brotli-devel
libjxl
libcurl
libsouplibcurl braucht libbrotli, um komprimierte HTTP-Antworten zu entpacken. Laut der offiziellen curl-Dokumentation entpackt eine mit der brotli-Bibliothek gebaute libcurl Antworten mit dem Content-Encoding br automatisch. Diese libcurl ruft die curl-Erweiterung von PHP direkt auf.
ldd /usr/lib64/libcurl.so.4 | grep brotli libbrotlidec.so.1 => /lib64/libbrotlidec.so.1 (0x00007f32803ed000)
libbrotlicommon.so.1 => /lib64/libbrotlicommon.so.1 (0x00007f327f858000)php -i | grep BROTLIBROTLI => YesDie libcurl-Version, die WordPress nutzt, steht in der Zeile [cURL-Version], wenn du im Adminbereich unter [Werkzeuge › Website-Zustand] auf dem Tab [Bericht] den Abschnitt [Server] aufklappst. Ob brotli unterstützt wird, zeigt diese Zeile nicht, deshalb prüfst du es wie oben mit Server-Befehlen.
Die Zeile [cURL-Version] im Bild zeigt die libcurl-Version zusammen mit der SSL-Bibliothek, mit der sie gebaut wurde. Laut curl-Dokumentation wird br ab 7.57.0 unterstützt; ist dieser Wert niedriger, kann libcurl br-Antworten nicht entpacken. Ist er gleich oder höher, prüfst du mit Server-Befehlen, ob brotli im Build enthalten ist.
Die Requests-Bibliothek im WordPress-Core setzt bei Anfragen an externe Adressen den Wert [CURLOPT_ENCODING] des curl-Handles auf einen leeren String. Laut curl-Dokumentation bedeutet ein leerer String, alle Komprimierungsverfahren anzunehmen, die diese libcurl unterstützt. Schickt der Gegenserver also eine mit br komprimierte Antwort, entpackt sie die libbrotli des WordPress-Servers.
grep -n CURLOPT_ENCODING wp-includes/Requests/src/Transport/Curl.php110: curl_setopt($this->handle, CURLOPT_ENCODING, '');
212: curl_setopt($this->handle, CURLOPT_ENCODING, 'none');Zeile 110 ist der Standardwert für alle Anfragen, und Zeile 212 läuft nur, wenn eine Anfrage, deren Entpacken fehlgeschlagen ist, ohne Komprimierung erneut gesendet wird. Von außen lässt sich das nicht direkt auslösen: Zuerst müsste ein Server übernommen werden, von dem WordPress Antworten holt – etwa bei der Update-Prüfung für Core und Plugins, bei oEmbed oder bei RSS-Feeds.
Der Ablauf beginnt mit br-komprimierten Daten von außen. Der Fehler wird in dem Schritt ausgelöst, in dem fetch() von Node.js, Express 5, fetch() von Bun oder die von der libcurl in WordPress aufgerufene libbrotli sie entpackt. Das führt zur Codeausführung auf dem Server. Die gepatchten Versionen blockieren diesen Ablauf, also tauschst du den Decoder gegen eine gepatchte Version aus.
Das mod_brotli von Apache bindet nur die Encoder-Bibliothek (libbrotlienc) ein, die Antworten komprimiert. Eine Konfiguration, die mit diesem Modul die Komprimierung aktiviert, liegt daher nicht auf dem Weg des Decoder-Fehlers.
ldd /usr/lib64/httpd/modules/mod_brotli.so | grep brotli libbrotlienc.so.1 => /lib64/libbrotlienc.so.1 (0x00007f34b4b79000)
libbrotlicommon.so.1 => /lib64/libbrotlicommon.so.1 (0x00007f34b4a79000)3️⃣ Node.js-Version prüfen: ob Node.js, Bun und libbrotli auf dem Server sind und in welcher Version
Zuerst verschaffst du dir einen Überblick, was auf dem Server installiert ist. Bei Node.js prüfst du die Version mit [node -v]; liegt der Wert unter 22.23.4, 24.22.1 bzw. 26.12.1, ist er betroffen.
| Deine Umgebung | Befehl zum Prüfen | Was behoben wird |
|---|---|---|
| Installer von nodejs.org · nvm · offizielles Docker-Image | [node -v] | Node.js selbst (22.23.4 · 24.22.1 · 26.12.1) |
| nodejs der Distribution · libbrotli-Zeile in ldd | [ldd $(command -v node)] | libbrotli-Paket |
| nodejs der Distribution · ldd-Ergebnis 0 | [ldd $(command -v node)] | nodejs-Paket (nach der Sicherheitsmeldung der Distribution) |
| Node.js Selector beim Shared Hosting (CloudLinux alt-nodejs) | Ankündigung des Anbieters · release-Nummer des Pakets | Vom Anbieter verteiltes Paket + App-Neustart |
| Bun | [bun --version] | Bun 1.4.4 oder neuer |
| Kein Node.js (nur WordPress) | libcurl mit [ldd] prüfen | libbrotli-Paket + Neustart von php-fpm · bis dahin vorübergehende Maßnahme |
| Managed Hosting | Ankündigung des Anbieters · Anfrage | Pakete, die der Anbieter aktualisiert |
Auf dem PC (Windows oder macOS) installierst du mit dem Installer der gepatchten Version von nodejs.org neu; bei nvm prüfst du die Version mit [nvm ls] und installierst dann über die Liniennummer neu, etwa mit [nvm install 24]. Die Tags 22, 24 und 26 des offiziellen Docker-Images node wurden am 22. Oktober mit den gepatchten Versionen neu gebaut, also ziehst du das Image neu, baust und deployst. Die Meldung von Vercel empfiehlt außerdem, selbst gebaute Container-Images, CI-Umgebungen und lokale Entwicklungsumgebungen auf die gepatchten Versionen zu bringen.
Bei einer Node.js-Installation über einen dnf-Modul-Stream zeigt [node -v] eine Version aus der Linie dieses Streams; auch bei gleicher Linie musst du aktualisieren, wenn die letzten Stellen unter der gepatchten Version liegen.
node -vv24.xx.xOb Node.js die libbrotli des Systems oder seinen eingebauten Decoder nutzt, prüfst du mit [ldd]. Enthält die Ausgabe eine libbrotlidec-Zeile, nutzt der Build die gemeinsame Bibliothek; steht dort wie unten 0, ist der Decoder eingebaut, und nur ein Update von Node.js selbst behebt den Fehler.
ldd $(command -v node) | grep -c brotli0Bei Bun prüfst du die Version mit [bun --version] und aktualisierst, wenn sie unter 1.4.4 liegt.
Auf RPM-basierten Systemen prüfst du die Version des Pakets libbrotli mit [rpm -q libbrotli]. Die Versionsnummern der Pakete vergibt die Distribution; vergleiche sie daher nicht direkt mit den Release-Nummern von Google, sondern mit der Version, die die Sicherheitsmeldung der Distribution für diese CVE-Nummer herausgibt.
rpm -q libbrotlilibbrotli-1.0.x-x.el9_x.x86_64Ob die Distribution eine Meldung veröffentlicht hat, prüfst du mit [dnf updateinfo info --cve CVE-2026-111497]. Ist die Ausgabe leer, gibt es in diesem Repository noch keine Meldung zu dieser CVE.
dnf updateinfo info --cve CVE-2026-111497Ein Paket, das nur die Korrektur übernimmt und die Versionsnummer beibehält, hinterlässt die CVE-Nummer im Änderungsprotokoll. Deshalb prüfst du mit [rpm -q --changelog libbrotli | grep CVE-2026-111497]; erscheint eine Zeile, ist das Paket gepatcht.
rpm -q --changelog libbrotli | grep CVE-2026-111497Mit Stand 23. Oktober gibt es für die libbrotli-Pakete von Debian, Ubuntu, Red Hat und AlmaLinux noch keine gepatchte Version für diese CVE. Bei Alpine ist nur für das nodejs-Paket eine gepatchte Version erschienen; Quelle ist die Security-Tracker-Seite der jeweiligen Distribution. Im Moment heißt die Reihenfolge daher: auf die Sicherheitsmeldung der Distribution achten und aktualisieren, sobald sie erscheint.
Bei Debian und Ubuntu heißt das Paket libbrotli1, und ich habe dieselben Punkte auf einem Debian-12-Testserver eines Managed-Hosting-Anbieters geprüft. Auch auf diesem Server band libcurl libbrotlidec ein, und die curl-Erweiterung von PHP unterstützte brotli. Das Änderungsprotokoll siehst du mit [zcat /usr/share/doc/libbrotli1/changelog.Debian.gz | grep CVE-2026-111497].
dpkg -l libbrotli1 | tail -1ii libbrotli1:amd64 1.1.0-2+0~20240305.8+debian12~1.gbp62df71 amd64 library implementing brotli encoder and decoder (shared libraries)ldd /usr/lib/x86_64-linux-gnu/libcurl.so.4 | grep brotli libbrotlidec.so.1 => /lib/x86_64-linux-gnu/libbrotlidec.so.1 (0x00007fa2baac6000)
libbrotlicommon.so.1 => /lib/x86_64-linux-gnu/libbrotlicommon.so.1 (0x00007fa2b9cb9000)4️⃣ Node.js aktualisieren: auf die gepatchte Version bringen und erneut prüfen
Beim Managed und Shared Hosting aktualisiert der Anbieter die Serverpakete. Kläre daher über seine Ankündigungen oder den Support, ob Node.js und libbrotli gepatcht sind. Betreibst du den Server selbst, bringst du Node.js auf die gepatchte Version aus der Meldung von nodejs.org und Bun mit [bun upgrade] auf 1.4.4 oder neuer.
Node.js aus einem Modul-Stream der Distribution aktualisierst du mit einem Befehl wie [dnf upgrade nodejs], sobald die gepatchte Version in diesem Repository angekommen ist. Hast du Node.js direkt von nodejs.org heruntergeladen und installiert, ersetzt du es durch die gepatchte Version von derselben Stelle. Linien ohne Support bekommen keine gepatchte Version, also wechselst du auf eine unterstützte Linie: 22, 24 oder 26.
Der Node.js Selector beim Shared Hosting (CloudLinux alt-nodejs) bringt eine eigene Kopie von brotli mit, deshalb behebt ein Update der libbrotli des Systems den Fehler nicht. Laut der Ankündigung von CloudLinux läuft die neue Binärdatei erst, wenn das vom Anbieter verteilte Paket installiert ist und die App neu gestartet wurde. Dieselbe Ankündigung stellt klar, dass sich bei Versionen ohne Support der Wert von [node --version] nicht ändert, sodass du die release-Nummer des Pakets prüfst.
Beim Paket libbrotli aktualisierst du das Paket, sobald die Sicherheitsmeldung der Distribution erscheint, und startest die Prozesse neu, die die Bibliothek im Speicher haben, damit sie die neue Bibliothek verwenden. Gemeinsame Bibliotheken werden beim Start eines Prozesses geladen, deshalb gehört auf einem WordPress-Server der Neustart von php-fpm zum selben Schritt.
Prüfe nach dem Upgrade, ob [node -v] mindestens die gepatchte Version zeigt und [rpm -q libbrotli] die Version aus der Meldung der Distribution. Ob die CVE-Nummer im Änderungsprotokoll steht, prüfst du mit [rpm -q --changelog libbrotli | grep CVE-2026-111497]. Hast du php-fpm neu gestartet, kontrollierst du auch den Status des Dienstes und die Antwort der Website.
systemctl is-active php-fpmactivecurl -s -o /dev/null -w '%{http_code}\n' 'https://example.com/?r=1'2005️⃣ Vorübergehende Maßnahme vor dem Patch: das br-Entpacken in Remote-Anfragen von WordPress abschalten
Die CVE-Seite von Red Hat empfiehlt, Apps so zu konfigurieren, dass sie br-Komprimierung aus nicht vertrauenswürdigen Quellen nicht annehmen oder nicht automatisch entpacken.
Auch bevor gepatchte Pakete erscheinen, kannst du den WordPress-seitigen Weg schon jetzt sperren. WordPress bietet den Hook [http_api_curl], der bei jeder Remote-Anfrage das curl-Handle übergibt. Setzt du dort [CURLOPT_ENCODING] auf null, ist das automatische Entpacken von libcurl abgeschaltet.
Legst du die Datei unten in den Ordner [wp-content/mu-plugins/], gilt sie für alle Remote-Anfragen; der Dateiname ist nur ein Beispiel. Zum Rückgängigmachen löschst du diese Datei.
<?php
// no-brotli-decode.php — WordPress entfernt in Anfragen libcurl Automatisch Entpacken Aus(libbrotli Patch Version erscheint bis)
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
add_action( 'http_api_curl', function ( $handle ) {
curl_setopt( $handle, CURLOPT_ENCODING, null );
}, 99 );Man denkt leicht, dass br wegfällt, wenn man [CURLOPT_ENCODING] auf [gzip, deflate] beschränkt. Dieser Wert entfernt br aber nur aus dem Accept-Encoding-Header der Anfrage; antwortet der Gegenserver mit br, entpackt libcurl das Empfangene trotzdem. Auf der Testseite wurde httpbin.org/brotli mit genau diesem Wert abgerufen, und die Antwort mit dem Content-Encoding br kam entpackt und lesbar an.
Laut curl-Dokumentation ist null der einzige Wert, der das automatische Entpacken abschaltet; mit null sendet libcurl keinen Accept-Encoding-Header und entpackt auch den empfangenen Body nicht. gzip- und deflate-Antworten entpackt die Requests-Bibliothek von WordPress selbst, sie bleiben lesbar; nur br-Antworten bleiben unentpackt.
Diesen Hook hat WordPress an die Stelle [curl.before_send] gehängt, die Requests direkt vor der Ausführung von curl aufruft. Er wird in Zeile 174 von Curl.php aufgerufen und curl läuft in Zeile 205, deshalb wird der leere String, den der Core in Zeile 110 gesetzt hat, dazwischen überschrieben.
Ob es greift, prüfst du mit der Testdatei unten. httpbin.org ist eine Testadresse, die die empfangenen Anfrage-Header zurückgibt und auch mit br und gzip komprimierte Antworten liefert, so siehst du auf einen Blick, was WordPress sendet und was es entpackt.
<?php
// br-check.php — WordPress entfernt Anfrage welche Komprimierung sendet ob entpackt wird Prüfen(httpbin.org Test Adresse)
$h = json_decode( wp_remote_retrieve_body( wp_remote_get( 'https://httpbin.org/headers' ) ), true );
echo 'Accept-Encoding: ', isset( $h['headers']['Accept-Encoding'] ) ? $h['headers']['Accept-Encoding'] : '(none)', PHP_EOL;
foreach ( array( 'brotli', 'gzip' ) as $p ) {
$r = wp_remote_get( 'https://httpbin.org/' . $p );
$readable = strpos( wp_remote_retrieve_body( $r ), ': true' ) !== false;
echo $p, ': Content-Encoding=', wp_remote_retrieve_header( $r, 'content-encoding' ), ' / ', strlen( wp_remote_retrieve_body( $r ) ), ' bytes / ', ( $readable ? 'readable' : 'not decoded' ), PHP_EOL;
}Vor dem Hinzufügen der Datei enthält Accept-Encoding br, und die br-Antwort wird entpackt und ist lesbar.
wp eval-file br-check.phpAccept-Encoding: deflate, gzip, br
brotli: Content-Encoding=br / 316 bytes / readable
gzip: Content-Encoding=gzip / 317 bytes / readableNach dem Hinzufügen der Datei fehlt der Accept-Encoding-Header, die br-Antwort bleibt unentpackt, und die gzip-Antwort ist weiter lesbar.
wp eval-file br-check.phpAccept-Encoding: (none)
brotli: Content-Encoding=br / 199 bytes / not decoded
gzip: Content-Encoding=gzip / 272 bytes / readableSobald du das gepatchte libbrotli-Paket der Distribution installiert und php-fpm neu gestartet hast, löschst du diese Datei und stellst so den ursprünglichen Zustand wieder her.
🔢 FAQ & empfohlene Inhalte
Sicherheit
OpenSSH-Sicherheitslücke CVE-2024-6387
Wie ich die OpenSSH-Lücke vorübergehend blockiert, dann den Patch eingespielt und geprüft habe.
Sicherheit
WordPress absichern: wp-admin schützen
Einstellungen, die den Adminbereich durch eine geänderte Login-URL, begrenzte Login-Versuche und gesperrtes XML-RPC schützen.
Server
Alma-Linux-Update: 9.4 → 9.8
Die Schritte, mit denen ich AlmaLinux 9.4 auf 9.8 aktualisiert habe, und was zu prüfen ist.
ℹ️ Affiliate-Hinweis
Die Inhalte dieser Website enthalten Affiliate-Links. Kauft ein Besucher über einen dieser Links ein Produkt oder eine Dienstleistung, erhält die Website vom Verkäufer eine Provision. Der Betrag, den der Käufer zahlt(bei Aktionsrabatten sinkt er ↓), steigt dadurch nicht. Angegebene Preise, Rabatte und Lagerbestände entsprechen dem Stand bei Erstellung und können abweichen; prüfe sie daher vor dem Kauf beim Verkäufer. Produkte werden nach eigenen Kriterien ausgewählt und bewertet, und Provisionen beeinflussen weder die Reihenfolge noch den Inhalt der Bewertungen.