Skip to main content

Welcome to a Grasp lesson

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

Lesson 36

Dinamik Dillerde Tip Manipülasyonu İstismarlarını Tespit Etme

Harika bir ilerleme kaydettik. Önceki dersimizde, JavaScript nesnelerinin "planını" hedef alarak, Object.prototype'ı kirletmenin ve bunun sonucunda yetki atlama hatta uzaktan kod çalıştırma gibi kritik zafiyetlere yol açmanın yollarını inceledik. Bu saldırı, verinin yapısını manipüle etmeye dayanıyordu. Bu derste ise, odağımızı dinamik tipli dillerin bir başka temel fakat tehlikeli özelliğine çevireceğiz: verinin tipini manipüle etmek.

Bu dersin sonunda, JavaScript ve PHP gibi dillerdeki gevşek karşılaştırma operatörlerinin (==) neden olduğu tip zorlaması (type coercion) mekanizmasını ve bunun bir saldırgan tarafından katı kontrolleri atlatmak için nasıl bir tip hokkabazlığına (type juggling) dönüştürülebileceğini öğreneceksin. Bu, özellikle kimlik doğrulama ve yetkilendirme gibi kritik mantık akışlarında sıklıkla gözden kaçan, ancak tespiti uzmanlık gerektiren bir zafiyet sınıfıdır.

Gevşekliğin Tehlikesi: == ve === Arasındaki Fark

Kod denetiminde karşılaştığımız en temel ancak en önemli ayrımlardan biri, gevşek eşitlik (==) ve katı eşitlik (===) operatörleri arasındaki farktır. JavaScript ve PHP gibi dinamik tipli dillerde bu ayrım, güvenlik açısından kritik sonuçlar doğurur.

  • Katı Eşitlik (===): Bu operatör, iki operandın hem değerini hem de tipini karşılaştırır. Tipler farklıysa, doğrudan false döner. Örneğin, 5 === "5" ifadesi, bir Number ve bir String karşılaştırıldığı için false olarak sonuçlanır. Bu, beklenen ve güvenli davranıştır.

  • Gevşek Eşitlik (==): Bu operatör ise karşılaştırma yapmadan önce, operandların tipleri farklıysa, birini diğerinin tipine dönüştürmeye çalışır. Bu otomatik ve örtük tip dönüştürme işlemine type coercion denir. İşte güvenlik açıkları tam da bu noktada başlar, çünkü bu dönüştürme kuralları sezgisel değildir ve beklenmedik sonuçlara yol açabilir.

Bir saldırganın bu örtük dönüştürme kurallarını istismar ederek bir karşılaştırmanın sonucunu manipüle etmesi ise type juggling olarak adlandırılır.


JavaScript'te Type Coercion'ın Tuhaf Dünyası

JavaScript'in gevşek karşılaştırma kuralları oldukça karmaşıktır ve birçok beklenmedik duruma yol açar. ECMAScript standardı, x == y karşılaştırması için bir dizi kural tanımlar. En yaygın ve tehlikeli olanlarından bazıları şunlardır:

  • Sayı ve String: String, sayıya dönüştürülür. 99 == "99" ifadesi 99 == 99 olur ve true döner. Ancak "99a" gibi bir string, sayıya dönüştürüldüğünde NaN (Not a Number) olur ve NaN hiçbir şeyle eşit olmadığı için (kendisiyle bile!) sonuç false olur.
  • Sayı ve Boolean: Boolean, sayıya dönüştürülür (true -> 1, false -> 0). 1 == true ifadesi 1 == 1 olur ve true döner.
  • Nesne ve Primitif Tip: Nesne, [ToPrimitive] adlı soyut bir operasyonla primitif bir tipe (genellikle String veya Number) dönüştürülür. Bu operasyon önce nesnenin valueOf() metodunu, ardından toString() metodunu çağırır. Örneğin, [10] == 10 ifadesi true döner. Çünkü [10].toString() metodu "10" string'ini döndürür ve karşılaştırma "10" == 10 haline gelir, bu da 10 == 10 olarak sonuçlanır.

Aşağıdaki görsel, bu tuhaf kuralların bazılarını özetleyen klasik bir tablodur. Bu örnekler, == operatörünün ne kadar öngörülemez olabileceğini çarpıcı bir şekilde göstermektedir.

Bu görsel, JavaScript'teki tip zorlamasının (type coercion) şaşırtıcı sonuçlarını sergilemektedir. `[] == ![]` gibi ifadelerin `true` dönmesi, gevşek karşılaştırmanın ne kadar yanıltıcı olabileceğinin bir kanıtıdır. Bu örnekte, `[]` "truthy" olduğu için `![]` ifadesi `false` olur. Karşılaştırma `[] == false` haline gelir. `false` `0`'a, `[]` ise `toString()` üzerinden `""`'e ve o da `0`'a dönüştürülür, sonuçta `0 == 0` `true` olur.

PHP'de Type Juggling ve Klasik İstismar Senaryoları

Type juggling zafiyetleri denince akla ilk gelen dil genellikle PHP'dir. JavaScript'e benzer şekilde, PHP'de de == ve === ayrımı bulunur ve == operatörü, özellikle string ve sayıları karşılaştırırken çok "cömert" davranır.

Aşağıdaki kod parçası, PHP'de gevşek karşılaştırmanın en temel halini gösterir.

Bu PHP kod örneğinde, `$var1` tamsayı değişkeni `1337` değerine sahiptir. `if` koşulu içinde bu değişken, `'1337'` string'i ile gevşek eşitlik operatörü (`==`) kullanılarak karşılaştırılıyor. PHP, string'i sayısal bağlamda yorumlayıp tamsayıya dönüştüreceği için bu koşul `true` olarak değerlendirilir ve ekrana "True" yazar.

Bu örnek zararsız görünse de, aynı mekanizma ciddi güvenlik zafiyetlerine yol açabilir. En bilinen senaryolardan biri, parola hash'lerinin doğrulanmasıdır.

Senaryo: Hash Karşılaştırma Zafiyeti

Diyelim ki bir uygulama, MD5 gibi (artık güvenli olmayan) bir hash algoritması kullanarak kullanıcının parolasını veritabanında saklıyor. Kullanıcı giriş yaptığında, uygulama gönderilen parolanın hash'ini alır ve veritabanındaki hash ile karşılaştırır.

<?php
$user_password_from_db = "0e123456789012345678901234567890"; // Örnek bir hash
$user_input_password = $_POST['password'];

// Zafiyetli kod!
if (md5($user_input_password) == $user_password_from_db) {
    echo "Giriş başarılı!";
    // Oturum başlat...
} else {
    echo "Hatalı parola!";
}
?>

Buradaki sorun, PHP'nin == ile karşılaştırma yaparken sayısal görünen string'leri sayı olarak yorumlamasıdır. Eğer bir string "0e" ile başlıyor ve geri kalanı tamamen rakamlardan oluşuyorsa, PHP bunu bilimsel gösterimde bir sayı olarak ele alır ve değeri 0 olur.

Saldırgan, MD5 hash'i 0e ile başlayan bir parola bulursa (bu tür "sihirli hash'ler" listeleri mevcuttur), bu parolayı gönderdiğinde md5($user_input_password) ifadesi 0e... gibi bir string üretir. Eğer veritabanındaki kullanıcının hash'i de tesadüfen 0'a eşitlenen başka bir "sihirli hash" ise (örneğin "0" veya "0.0"), karşılaştırma 0 == 0 haline gelir ve true döner. Sonuç: Saldırgan, doğru parolayı bilmeden hesaba giriş yapar.

Bu zafiyet sadece hash karşılaştırmalarıyla sınırlı değildir. PHP'deki in_array() fonksiyonu da varsayılan olarak gevşek karşılaştırma yapar.

<?php
$valid_ids = [1, 2, 3];
$user_supplied_id = '1-and-some-sql-injection';

// in_array'in üçüncü parametresi (strict) false veya belirtilmemiş!
if (in_array($user_supplied_id, $valid_ids)) {
    // ...
}
?>

Yukarıdaki kodda, $user_supplied_id string'i sayısal bir bağlamda 1 olarak yorumlanır. 1 değeri $valid_ids dizisinde bulunduğu için, in_array fonksiyonu true döner ve SQL enjeksiyonu içeren payload, normalde geçmemesi gereken bir kontrolden geçmiş olur.


Kod Denetimi Stratejileri

Dinamik tipli dillerde type juggling zafiyetlerini ararken sistematik bir yaklaşım benimsemek kritiktir:

  1. Gevşek Karşılaştırmaları Tespit Et: Kod tabanında == operatörünün kullanıldığı tüm yerleri bulmak için grep veya IDE'nizin arama fonksiyonunu kullanın. != operatörünü de unutmayın.

    • JavaScript: grep -r "==" .
    • PHP: grep -r "==" .
  2. Bağlamı Analiz Et: Bulduğunuz her == kullanımı için kendinize şu soruları sorun:

    • Karşılaştırılan operandlardan en az biri kullanıcı kontrolünde mi? (Örn: req.query, req.body, $_GET, $_POST)
    • Bu iki operandın beklenen tipleri nelerdir? Kod geliştiricisi her zaman iki string veya iki sayı karşılaştırılacağını mı varsaymış?
    • Bir saldırgan, beklenen tipten farklı bir tipte veri gönderebilir mi? (Örn: sayının beklendiği yere true boolean'ı veya bir string göndermek).
  3. PHP'ye Özel Fonksiyonları Kontrol Et: PHP'de, gevşek karşılaştırma yapan yerleşik fonksiyonlara özellikle dikkat et.

    • in_array($needle, $haystack): Üçüncü parametre olan $strict true olarak ayarlanmamışsa, bu fonksiyon zafiyetli olabilir.
    • array_search($needle, $haystack): in_array gibi, bu fonksiyon da varsayılan olarak gevşek arama yapar.
    • switch ($var): PHP'deki switch ifadesi de case'leri kontrol ederken gevşek karşılaştırma (==) kullanır. Bu da beklenmedik davranışlara yol açabilir.
  4. Güvenlik Kritik Kod Yollarına Odaklan: Denetim zamanı kısıtlı olduğunda, çabalarınızı en kritik yerlere odaklayın:

    • Kimlik doğrulama: Parola ve token karşılaştırmaları.
    • Yetkilendirme: Rol ve izin kontrolleri (user.role == 'admin').
    • Veri erişimi: Kayıt sahipliği kontrolleri (record.owner_id == user.id).

Temel kural basittir: Kullanıcı girdisiyle yapılan veya güvenlik açısından hassas olan tüm karşılaştırmalarda daima katı eşitlik (===) kullanılmalıdır.


Sonuç

Bu derste, dinamik tipli dillerin temel bir özelliği olan tip zorlamasının (type coercion), bir saldırgan tarafından nasıl istismar edilebileceğini gördük. Prototype pollution ile nesnelerin yapısını değiştirirken, type juggling ile verilerin tipini değiştirerek güvenlik mekanizmalarını atlatmanın mümkün olduğunu öğrendik.

Anahtar Çıkarımlar:

  • Kök Neden: == gibi gevşek karşılaştırma operatörlerinin, karşılaştırmadan önce otomatik ve sezgisel olmayan tip dönüşümleri yapmasıdır.
  • Güvenli Alternatif: === gibi katı karşılaştırma operatörleri, hem değeri hem de tipi kontrol ederek bu tür belirsizlikleri ortadan kaldırır ve her zaman tercih edilmelidir.
  • İstismar Desenleri: PHP'de "sihirli hash" (0e...) kullanarak kimlik doğrulama atlatma ve JavaScript'te true == 1 veya [] == 0 gibi beklenmedik eşitlikleri kullanarak mantık kontrollerini baypas etme en yaygın örneklerdir.
  • Denetim Odağı: Kod denetiminde öncelik, kullanıcı kontrollü verilerin gevşek karşılaştırma operatörlerine veya fonksiyonlarına ulaştığı tüm kod yollarını bulmak ve bunların === ile değiştirilmesini sağlamaktır.

Bu dersle birlikte "Güvensiz Deserialization ve Tip Manipülasyonu" modülümüzü tamamlamış bulunuyoruz. Nesne yapılarından veri tiplerine kadar, verinin temsil edilme biçimini manipüle etmenin ne kadar güçlü saldırılara yol açabileceğini gördük.

Bir sonraki modülde, uygulama güvenliğinin bir başka temel taşına odaklanacağız: Kriptografi. "Kriptografik İmplementasyon Hataları" başlıklı yeni modülümüzde, zayıf rastgele sayı üretimi, zamanlama saldırılarına açık karşılaştırmalar ve dağıtık sistemlerde anahtar yönetimi gibi konularda yapılan yaygın hataları kod seviyesinde nasıl tespit edeceğimizi inceleyeceğiz.