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

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:
sourcenesnesindeki__proto__anahtarını görür.target['__proto__']ifadesini değerlendirir. JavaScript'te bu,targetnesnesinin prototipine, yaniObject.prototype'a erişmekle aynı anlama gelir.- Fonksiyon şimdi
merge(Object.prototype, {"isAdmin": true})işlemini yapar. - Sonuç:
isAdminözelliği,truedeğeriyleObject.prototype'a eklenir. Artık uygulamadaki her nesne,isAdminözelliğitrueolan bir prototipe sahiptir.
Aşağıdaki kod parçası, rangy-core.js kütüphanesinde bulunan gerçek bir prototype pollution zafiyetini göstermektedir.

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.
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:
- 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. - Tehlikeli Sink'leri Ara: Bu kullanıcı kontrollü nesneleri tüketen "sink" fonksiyonlarını ara. Özellikle özyinelemeli çalışan
merge,extend,clonegibi 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ıgrepile ara.- Geliştiricilerin yazdığı özel (custom) klonlama veya birleştirme yardımcı fonksiyonlarını dikkatle incele.
- 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:
Eğer bu tür bir "property name sanitization" yoksa, fonksiyon büyük olasılıkla zafiyetlidir.if (key === '__proto__') { continue; // veya return; } if (key === 'constructor' || key === 'prototype') { continue; }
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__,constructorvb.) doğrulamayan güvensiz özyinelemeli birleştirme (deep merge) fonksiyonlarıdır. - İstismar Vektörleri: Etkileri, yetki kontrollerini atlamaktan (örneğin,
isAdmin: trueenjekte etmek),constructorgibi 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.