Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 35

Prototype Pollution: Nesne Birleştirme ve Özyinelemeli Atama

Recommended resources

Önceki dersimizde, bir HTTP isteğindeki fazladan alanların, bir nesne örneğinin beklenmedik özelliklerini (isAdmin: true gibi) nasıl değiştirebildiğini ve bunun Mass Assignment zafiyetine nasıl yol açtığını inceledik. Bu saldırı, hedeflenen tek bir nesnenin durumunu bozuyordu. Bu derste ise, etki alanını çok daha genişleten ve JavaScript'in temel mekaniklerinden birini istismar eden bir tekniğe odaklanacağız: Prototype Pollution.

Bu dersin sonunda, tek bir nesneyi değil, o nesnenin ve uygulamadaki diğer tüm nesnelerin "planını" (prototype) hedef alarak, JavaScript'teki nesne birleştirme ve özyinelemeli atama desenleri üzerinden prototype pollution zafiyetlerini nasıl tespit edeceğini ve bu zafiyetin etkilerini nasıl değerlendireceğini öğreneceksin. Bu, sadece yetki atlamaya değil, uzaktan kod çalıştırmaya (RCE) kadar varabilen son derece güçlü bir saldırı vektörüdür.

JavaScript'in Kalbindeki Mekanizma: Prototip Zinciri

JavaScript'in sınıf tabanlı (class-based) dillerden farklı olarak prototip tabanlı (prototypal) bir dil olduğunu biliyorsun. Bu, her nesnenin, özelliklerini ve metotlarını miras aldığı bir "prototip" nesnesine gizli bir bağlantı (__proto__) içerdiği anlamına gelir. Bir nesnenin bir özelliğine erişmeye çalıştığınızda, JavaScript motoru önce nesnenin kendisinde bu özelliği arar. Bulamazsa, nesnenin prototipi (__proto__) üzerinde aramaya devam eder. Bu zincir, en tepedeki Object.prototype'a ulaşana kadar devam eder.

Prototype Pollution, bir saldırganın, genellikle güvensiz bir nesne birleştirme (merge) işlemi aracılığıyla, bu zincirin en tepesindeki Object.prototype'ı değiştirmesidir. Object.prototype'a yeni bir özellik eklendiğinde veya mevcut bir özellik değiştirildiğinde, bu değişiklik prototip zinciri aracılığıyla uygulamadaki neredeyse tüm nesnelere yansır.

Aşağıdaki diyagram, bu saldırının akışını mükemmel bir şekilde özetlemektedir.

Bu diyagram, bir saldırganın `__proto__` anahtarını içeren bir HTTP POST isteği göndererek güvensiz bir birleştirme fonksiyonunu nasıl istismar ettiğini gösteriyor. Bu işlem, JavaScript çalışma zamanındaki genel `PROTOTYPE` nesnesini "kirletir" ve `role="admin"` gibi bir özelliği ekler. Daha sonra, tamamen alakasız bir işlemde (örn: `DELETE` isteği), `securityObject.role` kontrolü yapılırken bu kirletilmiş özellik miras alınır ve yetkisiz eyleme izin verilir.

Zafiyetin Kaynağı: Güvensiz Özyinelemeli Birleştirme (Unsafe Recursive Merge)

Prototype pollution zafiyetinin en yaygın kaynağı, iki nesneyi özyinelemeli olarak birleştiren "deep merge" veya "extend" fonksiyonlarıdır. Bu fonksiyonlar, kaynak nesnedeki her özelliği hedef nesneye kopyalamak için tasarlanmıştır. Eğer kaynak nesnedeki bir özellik de bir nesne ise, fonksiyon özyinelemeli olarak kendini çağırır ve iç içe geçmiş nesneleri de birleştirir.

Tehlike, bu fonksiyonların kopyaladıkları özelliklerin isimlerini (anahtarları) kontrol etmemesiyle başlar. Saldırgan, __proto__ adında özel bir anahtar içeren bir JSON payload'ı gönderdiğinde, zafiyetli fonksiyon bu anahtarı sıradan bir özellik gibi ele alır.

Örneğin, merge(target, source) fonksiyonunu düşünelim. Saldırganın kontrolündeki source nesnesi şu şekilde olsun:
JSON.parse('{"__proto__": {"isAdmin": true}}')

Zafiyetli merge fonksiyonu şu adımları izler:

  1. source nesnesindeki __proto__ anahtarını görür.
  2. target['__proto__'] ifadesini değerlendirir. JavaScript'te bu, target nesnesinin prototipine, yani Object.prototype'a erişmekle aynı anlama gelir.
  3. Fonksiyon şimdi merge(Object.prototype, {"isAdmin": true}) işlemini yapar.
  4. Sonuç: isAdmin özelliği, true değeriyle Object.prototype'a eklenir. Artık uygulamadaki her nesne, isAdmin özelliği true olan bir prototipe sahiptir.

Aşağıdaki kod parçası, rangy-core.js kütüphanesinde bulunan gerçek bir prototype pollution zafiyetini göstermektedir.

Bu görsel, `rangy.util.extend` adlı zafiyetli bir fonksiyonu ve istismarını gösteriyor. Fonksiyon, `for (var i in props)` döngüsü içinde özellik adını (`i`) doğrulamadan, özyinelemeli olarak nesneleri birleştirir. Alt kısımdaki PoC kodu, `JSON.parse` ile `__proto__` içeren bir payload oluşturur, bu payload ile `extend` fonksiyonunu çağırır ve daha önce `test` özelliğine sahip olmayan bir `victim` nesnesinin artık bu özelliğe sahip olduğunu (`hacked`) kanıtlar.

Kodda, extend fonksiyonunun 10. satırda özyinelemeli çağrı yapmadan önce i değişkeninin __proto__ olup olmadığını kontrol etmediğini görebilirsin. 18. satırdaki rangy.util.extend({}, payload, true) çağrısı, payload içindeki __proto__ nesnesini alır ve boş {} nesnesinin prototipine, yani Object.prototype'a uygular. Bu nedenle, 19. satırda, saldırıdan önce bu özellikle hiçbir ilgisi olmayan victim nesnesi, victim.test çağrıldığında zincir üzerinden "hacked" değerini miras alır.


Pratik Gösterim: Lodash Merge ve Yetki Atlatma

Teoriyi pratiğe dökmek için, lodash gibi popüler bir kütüphanenin eski sürümlerinde bulunan benzer bir zafiyetin nasıl istismar edildiğini inceleyelim. Aşağıdaki video, bu süreci adım adım ve uygulamalı olarak gösteriyor.

CodeBreakers: From Dev to Hacker 0x02 - Deep Merge & Prototype Pollution Nedir ? - Bölüm 2@usirin

Bu videoda, Mehmet İnce ve Umut Sırın, "CodeBreakers" serisinde prototype pollution zafiyetini masaya yatırıyor. Videoyu izlerken, kullanıcıdan gelen verinin (request.body) güvensiz bir lodash.merge fonksiyonuna nasıl ulaştığına ve bunun nasıl bir yetki atlatma zafiyetine dönüştüğüne odaklan.

İlk olarak, kavramsal giriş bölümünü izleyerek, kullanıcı kontrollü bir değişkenin bir birleştirme işleminde nasıl temel risk faktörü haline geldiğini anla. Ardından, zafiyetin mantığı açıklanırken, __proto__ anahtarının JSON içinde gönderilmesinin lodash.merge fonksiyonunu nasıl Object.prototype'ı değiştirmeye zorladığına dikkat et. Canlı exploit gösteriminde, curl komutuyla __proto__.canDelete: true payload'ının nasıl gönderildiği ve bunun sonucunda normalde reddedilen bir silme işleminin nasıl başarılı olduğu gösteriliyor. Son olarak, exploit sonrası analiz bölümünde, canDelete özelliği User nesnesinde tanımlı olmamasına rağmen, prototip zinciri üzerinden bu değere nasıl erişildiğinin teknik açıklaması yapılıyor.

Bu video, basit bir mantık atlatma senaryosunu (yetkisiz silme) gösteriyor. Ancak prototype pollution'ın etkileri çok daha yıkıcı olabilir.


İleri Seviye İstismar: Pollution'dan Uzaktan Kod Çalıştırmaya (RCE)

Bir saldırgan, Object.prototype'a sadece isAdmin: true gibi basit değerler değil, aynı zamanda uygulamanın iç mantığını bozacak veya RCE'ye yol açacak başka özellikleri de enjekte edebilir. Senin gibi ileri seviye bir güvenlik uzmanı için asıl ilginç olan kısım burasıdır.

Örneğin, JavaScript'teki her fonksiyonun, o fonksiyonu oluşturan Function constructor'ına işaret eden bir constructor özelliği vardır. Function constructor'ı, bir string parametre alıp onu kod olarak derleyip çalıştırabilir.

Eğer bir saldırgan, Object.prototype'ı kirleterek, uygulamanın bir yerinde çağrılan bir özelliğin constructor'a işaret etmesini sağlayabilirse, bu durum RCE'ye yol açabilir. Bu, son derece karmaşık bir zincirleme saldırısıdır ve React'in Server Components mimarisinde bulunan kritik bir zafiyet olan CVE-2025–55182'nin temelini oluşturur.

Aşağıdaki makale, bu ileri seviye istismar tekniğinin arkasındaki teoriyi açıklıyor.

React2Shell: CVE-2025–55182

Bu makale, React'teki bir zafiyet üzerinden prototip zinciri manipülasyonunun nasıl RCE'ye ulaştığını detaylandırıyor. Makalenin tamamı karmaşık olsa da, bizim için önemli olan, constructor özelliğinin nasıl bir silaha dönüştüğünü gösteren çekirdek fikirdir.

Makalede, çekirdek zafiyet başlığı altındaki bölümü oku. Özellikle moduleExports[metadata[2]] satırının prototip zinciri boyunca nasıl arama yaptığına ve bir saldırganın someFunction.constructor'a erişmesinin neden tehlikeli olduğuna odaklan. Bu bölüm, Function constructor'ının nasıl keyfi kod çalıştırabilen bir mekanizmaya dönüştüğünü açıklıyor. Bu, prototype pollution'dan RCE'ye giden yolu anlamak için kritik bir bilgidir. Zamanın olursa, devamındaki istismar zinciri bölümlerini de inceleyerek, bu teorik zafiyetin gerçek bir multipart/form-data isteği ile nasıl tetiklendiğini görebilirsin. Bu, _response nesnesinin kirletilmesi gibi tekniklerin, saldırganın Function constructor'ını hedefli bir kodla nasıl beslediğini gösterir.


Kod Denetimi Stratejileri

JavaScript kod tabanında prototype pollution zafiyetlerini ararken şu adımları izleyebilirsin:

  1. Potansiyel Kaynakları Belirle: Kullanıcıdan gelen ve nesne yapısı içeren tüm girdileri (sources) bul. Bunlar genellikle JSON.parse(req.body), querystring.parse(), veya bir framework tarafından otomatik olarak ayrıştırılan JSON gövdeleridir.
  2. Tehlikeli Sink'leri Ara: Bu kullanıcı kontrollü nesneleri tüketen "sink" fonksiyonlarını ara. Özellikle özyinelemeli çalışan merge, extend, clone gibi fonksiyonlar en riskli olanlardır.
    • lodash.merge, $.extend(true, ...) (jQuery), Object.assign'in döngü içinde kullanımı gibi yaygın kütüphane fonksiyonlarını grep ile ara.
    • Geliştiricilerin yazdığı özel (custom) klonlama veya birleştirme yardımcı fonksiyonlarını dikkatle incele.
  3. Güvenlik Kontrolünü Doğrula: Sink olarak belirlediğin fonksiyonun içine bak. Fonksiyon, birleştirdiği özelliğin adını kontrol ediyor mu? Özellikle şu kontrollerin varlığını ara:
    if (key === '__proto__') {
      continue; // veya return;
    }
    if (key === 'constructor' || key === 'prototype') {
      continue; 
    }
    
    Eğer bu tür bir "property name sanitization" yoksa, fonksiyon büyük olasılıkla zafiyetlidir.

Sonuç ve Sonraki Adımlar

Bu derste, JavaScript'in prototip mekanizmasının nasıl istismar edilebileceğini ve basit bir nesne birleştirme işleminin tüm uygulamayı etkileyen global bir zafiyete nasıl dönüşebileceğini gördük.

Anahtar Çıkarımlar:

  • Etki Alanı: Mass assignment tek bir nesneyi etkilerken, prototype pollution Object.prototype üzerinden uygulamadaki neredeyse tüm nesneleri etkiler.
  • Kök Neden: Zafiyetin kökeni, özellik adlarını (__proto__, constructor vb.) doğrulamayan güvensiz özyinelemeli birleştirme (deep merge) fonksiyonlarıdır.
  • İstismar Vektörleri: Etkileri, yetki kontrollerini atlamaktan (örneğin, isAdmin: true enjekte etmek), constructor gibi tehlikeli özellikleri manipüle ederek uzaktan kod çalıştırmaya (RCE) kadar uzanır.
  • Kod Denetimi Odağı: Kod denetiminde, kullanıcı girdisini alan ve nesneleri özyinelemeli olarak birleştiren veya kopyalayan tüm kod yollarını bulup, bu fonksiyonların özellik adlarını güvenli bir şekilde filtreleyip filtrelemediğini kontrol etmek esastır.

Bu derste JavaScript nesnelerinin yapısını manipüle etmeyi öğrendik. Bir sonraki dersimizde, dinamik tipli dillerin bir başka tehlikeli özelliğine odaklanacağız: verilerin tiplerini manipüle etmek. Özellikle gevşek karşılaştırma operatörlerinin (==) kullanıldığı yerlerde ortaya çıkan Type Juggling ve Type Coercion zafiyetlerinin, katı güvenlik kontrollerini nasıl atlatabildiğini inceleyeceğiz.