Angriff auf Node.js durch Manipulation von JavaScript-Objektprototypen

Forscher des Helmholtz-Zentrums für Informationssicherheit (CISPA) und des Royal Institute of Technology (Schweden) analysierten die Anwendbarkeit der JavaScript-Prototyp-Verschmutzungstechnik, um Angriffe auf die Node.js-Plattform und darauf basierende beliebte Anwendungen zu erstellen, die zur Codeausführung führen.

Die Prototyp-Verschmutzungsmethode nutzt eine Funktion der JavaScript-Sprache, die es Ihnen ermöglicht, dem Stammprototyp eines beliebigen Objekts neue Eigenschaften hinzuzufügen. Anwendungen können Codeblöcke (Gadgets) enthalten, deren Betrieb durch eine ersetzte Eigenschaft beeinflusst wird; der Code kann beispielsweise ein Konstrukt wie „const cmd = options.cmd ||“ enthalten „/bin/sh“‘, dessen Logik sich ändert, wenn es dem Angreifer gelingt, die Eigenschaft „cmd“ im Root-Prototyp zu ersetzen.

Ein erfolgreicher Angriff erfordert, dass die Anwendung externe Daten verwenden kann, um eine neue Eigenschaft im Stammprototyp des Objekts zu erstellen, und dass die Ausführung auf ein Gadget trifft, das von der geänderten Eigenschaft abhängt. Das Ändern des Prototyps erfolgt durch die Verarbeitung der Diensteigenschaften „__proto__“ und „constructor“ in Node.js. Die Eigenschaft „__proto__“ gibt den Prototyp der Klasse des Objekts zurück und die Eigenschaft „constructor“ gibt die Funktion zurück, die zum Erstellen des Objekts verwendet wurde.

Wenn der Anwendungscode die Zuweisung „obj[a][b] = value“ enthält und die Werte aus externen Daten gesetzt werden, kann ein Angreifer „a“ auf den Wert „__proto__“ setzen und die Installation einer eigenen Eigenschaft erreichen mit dem Namen „b“ und dem Wert „value“ im Stammprototyp des Objekts (obj.__proto__.b = value;), und die im Prototyp festgelegte Eigenschaft ist in allen Objekten sichtbar. Wenn der Code Ausdrücke wie „obj[a][b][c] = value“ enthält, können Sie dies auch tun, indem Sie „a“ auf den Wert „constructor“ und „b“ auf „prototype“ in allen vorhandenen Objekten setzen Definieren Sie eine neue Eigenschaft mit dem Namen „c“ und dem Wert „value“.

Beispiel für die Änderung des Prototyps: const o1 = {}; const o2 = neues Objekt(); o1.__proto__.x = 42; // Eigenschaft „x“ im Root-Prototyp console.log (o2.x) erstellen; // Von einem anderen Objekt auf die Eigenschaft „x“ zugreifen // Die Ausgabe wird 42 sein, da der Root-Prototyp durch Objekt o1 geändert wurde, das auch in Objekt o2 verwendet wird

Beispiel für anfälligen Code: function enterPoint (arg1, arg2, arg3){ const obj = {}; const p = obj[arg1]; p[arg2] = arg3; Rückkehr p; }

Wenn die Argumente der Funktion „entryPoint“ aus Eingabedaten gebildet werden, kann ein Angreifer den Wert „__proto__“ an arg1 übergeben und eine Eigenschaft mit einem beliebigen Namen im Root-Prototyp erstellen. Wenn Sie arg2 den Wert „toString“ und arg3 den Wert 1 übergeben, können Sie die Eigenschaft „toString“ (Object.prototype.toString=1) definieren und die Anwendung während des Aufrufs von toString() zum Absturz bringen.

Beispiele für Situationen, die zur Ausführung von Angreifercode führen könnten, sind die Erstellung der Eigenschaften „main“, „shell“, „exports“, „contextExtensions“ und „env“. Beispielsweise kann ein Angreifer eine „Haupt“-Eigenschaft im Stammprototyp eines Objekts erstellen und darin den Pfad zu seinem Skript schreiben (Object.prototype.main = „./../../pwned.js“) und Diese Eigenschaft wird zum Zeitpunkt der Ausführung im Code des Konstrukts require("my-package") aufgerufen, wenn das enthaltene Paket die Eigenschaft "main" in package.json nicht explizit definiert (wenn die Eigenschaft nicht definiert ist, es wird vom Root-Prototyp abgerufen). Die Eigenschaften „shell“, „exports“ und „env“ können auf ähnliche Weise ersetzt werden: let rootProto = Object.prototype; rootProto["exports"] = {".:"./changelog.js"}; rootProto["1"] = "/path/to/npm/scripts/"; // Aufruf auslösen require("./target.js"); Object.prototype.main = "/path/to/npm/scripts/changelog.js"; Object.prototype.shell = "node"; Object.prototype.env = {}; Object.prototype.env.NODE_OPTIONS = „—inspect-brk=0.0.0.0:1337“; // Aufruf auslösen require("bytes");

Die Forscher analysierten 10 NPM-Pakete mit der größten Anzahl an Abhängigkeiten und stellten fest, dass 1958 von ihnen keine Haupteigenschaft in package.json haben, 4420 relative Pfade in ihren Anforderungsanweisungen verwenden und 355 direkt die Befehlsersetzungs-API verwenden.

Ein funktionierendes Beispiel ist ein Exploit zum Angriff auf das Parse-Server-Backend, der die Eigenschaft evalFunctions überschreibt. Um die Identifizierung solcher Schwachstellen zu vereinfachen, wurde ein Toolkit entwickelt, das statische und dynamische Analysemethoden kombiniert. Beim Testen von Node.js wurden 11 Gadgets identifiziert, mit denen sich Angriffe organisieren lassen, die zur Ausführung des Codes des Angreifers führen. Neben Parse Server wurden auch zwei ausnutzbare Schwachstellen in der NPM-CLI identifiziert.

Source: opennet.ru

Kommentar hinzufügen