Der Versuch gelang nicht bei jedem Durchlauf: In der Linux-arm64-Umgebung von Imperva waren mehrere Anläufe nötig, bis Code ausgeführt wurde. Das Ergebnis bezieht sich auf diesen einen Build und dieses eine Anwendungssetup; andere Architekturen sowie die Reihen 22.x und 26.x wurden nicht geprüft. Auf der korrigierten Version 24.22.1 wurden dieselben Daten zurückgewiesen, Code wurde nicht ausgeführt.

Damit geht die Demonstration über den ersten öffentlichen Proof of Concept hinaus, der Codeausführung nur auf einem absichtlich geschwächten Test-Build erreichte. Auf einem Standard-Build berichtete dessen Autor lediglich von einem Absturz und einem begrenzten Auslesen von Prozessspeicher, nicht von Codeausführung, und erklärte, ein funktionierender Angriff auf einen normalen Build sei nicht gelungen. Eine Ausnutzung in freier Wildbahn war mit Stand 22. Oktober nicht bekannt.

Betroffene Anwendungen

Der Fehler steckt im Brotli-Decoder, den Node.js mitliefert, und betrifft damit Anwendungen auf den Reihen 22.x, 24.x und 26.x – nicht nur den getesteten Build. Ein Dienst erreicht den Decoder von beiden Seiten eines HTTP-Austauschs: wenn er eine Brotli-komprimierte Antwort über das eingebaute fetch() von Node.js liest, oder wenn eine Middleware einen Brotli-komprimierten Request-Body dekodiert.

Die Body-Parser von Express 5 – etwa express.json(), express.urlencoded(), express.text() und express.raw() – dekodieren Brotli-Request-Bodies, wenn sie auf eine Route angewendet werden, zusätzlich zu gzip und deflate. Ein einfacher Node.js-HTTP-Server dekodiert einen Brotli-Request-Body nicht allein dadurch, dass er ihn empfängt; es braucht also diese Middleware oder einen expliziten Aufruf. Imperva reproduzierte die Codeausführung sowohl über den Request-Body- als auch über den Antwortpfad und testete dabei Express 5.0.0 bis 5.3.0 sowie body-parser 2.0.0 bis 2.3.0 in festgelegten Versionen, nicht jedoch jede Zwischenversion.

Ursache des Fehlers

Imperva führt die Ursache darauf zurück, wie der Decoder mit einem unmöglichen Punkt in einem Brotli-Strom umgeht: Der Strom kündigt die Blocktypen, die er verwendet, während des Dekodierens an, und die Testeingabe erreichte einen Blockwechsel, den die Struktur des Stroms selbst ausschloss. Der verwundbare Decoder interpretierte das als Anforderung weiterer Eingabedaten und arbeitete in einem ungültigen Zustand weiter. In einem separaten Test mit einem Memory-Sanitizer führte das Fortsetzen aus diesem Zustand zu einem Schreibzugriff über einen vorgesehenen Puffer hinaus – der Out-of-Bounds-Write hinter der Schwachstelle.

Node.js behob das Problem durch Übernahme eines Upstream-Patches aus dem Brotli-Projekt, der den Decoder an dieser Stelle mit einem Formatfehler abbrechen lässt, statt weiterzulaufen.

Maßnahmen

Abhilfe schafft nur der Wechsel auf eine gepatchte Version. Das Update muss auch Container-Images und andere Artefakte erreichen, die ein Node-Binary mitbringen – ein Paketinventar kann diese übersehen. Die End-of-Life-Reihen 20.x und älter erhalten den Fix nicht und sollten auf eine unterstützte Reihe umgestellt werden.

Der Sicherheitshinweis nennt keinen Workaround innerhalb von Node.js; eine Anwendung, die nicht sofort aktualisieren kann, hat in der Laufzeitumgebung also keinen Rückfallweg. Imperva empfahl, nicht vertrauenswürdigen Brotli-Verkehr auf betroffenen Pfaden zu begrenzen, wo das praktikabel ist, und betonte, dass allein das Update die Schwachstelle beseitigt. Das Unternehmen ergänzte zudem Regeln in seiner eigenen Web Application Firewall und prüfte sie gegen die nachgestellten Angriffe.

Vercel, das Node.js für seine Kunden betreibt, erklärte, am 21. Oktober Regeln hinzugefügt zu haben, die den Angriff in eingehenden Anfragen blockieren. Diese Regeln erfassen keine Brotli-Antworten, die eine Anwendung von anderen Servern abruft, und ersetzen das Update nicht.