Skip to main content

Welcome to a Grasp lesson

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

Lesson 22

Yarış Koşulları Nasıl Tespit Edilir?

Recommended resources

Merhaba, önceki dersimizde iş mantığı zafiyetlerine odaklanarak, bir uygulamanın iş akışını bir durum makinesi (state machine) gibi modelleyip yasadışı durum geçişlerini nasıl tespit edeceğimizi öğrendik. O derste amacımız, olayların mantıksal olarak doğru sırayla gerçekleşmesini sağlamaktı.

Bu dersimizde ise merceğimizi olayların zamanlamasına çeviriyoruz. Sıralı olarak analiz edildiğinde kusursuz görünen bir kod parçasının, birden fazla istek veya iş parçacığı tarafından neredeyse eş zamanlı olarak çalıştırıldığında nasıl beklenmedik ve güvensiz durumlara yol açabileceğini inceleyeceğiz. Öğrenme hedefimiz, paylaşılan kaynakları veya kritik bölümleri işleyen çok iş parçacıklı ve asenkron kodda yarış koşullarını (race condition) belirlemektir. Bu zafiyet sınıfı, otomasyonun kolayca gözden kaçırdığı, ancak bir uzmanın bağlamsal analiz yeteneğiyle ortaya çıkarabileceği en ince ve etkili hatalardan biridir.

Yarış Koşullarının Anatomisi: TOCTOU

Matematik ve bilgisayar bilimleri altyapınızdan da hatırlayacağınız gibi, eşzamanlılık (concurrency) yönetimi karmaşık bir konudur. Güvenlik bağlamında, bu karmaşıklığın en sık istismar edildiği desenlerden biri Time-of-Check to Time-of-Use (TOCTOU) olarak bilinir. Bir yarış koşulu, özünde bir TOCTOU zafiyetidir.

Bu zafiyet, bir programın bir kaynağın durumunu kontrol etmesi (Time-of-Check) ile bu kontrolün sonucuna göre o kaynak üzerinde bir eylem gerçekleştirmesi (Time-of-Use) arasında geçen kritik bir zaman aralığından kaynaklanır. Eğer bu kısa zaman aralığında, "yarış penceresi" (race window) olarak adlandırılan bu boşlukta, başka bir iş parçacığı veya işlem kaynağın durumunu değiştirirse, ilk işlemin yaptığı kontrol geçersiz hale gelir ve eylemi beklenmedik sonuçlar doğurur.

Klasik bir e-ticaret senaryosu üzerinden gidelim:

  1. İş Parçacığı A (Kullanıcı A): Veritabanından ürünün stok miktarını kontrol eder. stok = 1.
  2. İş Parçacığı B (Kullanıcı B): Aynı anda, ürünün stok miktarını kontrol eder. stok = 1.
  3. İş Parçacığı A: Stok yeterli olduğu için (1 >= 1), ürünü sepete ekler ve stok miktarını 0 olarak günceller.
  4. İş Parçacığı B: Kendi yaptığı kontrol de geçerli olduğu için (1 >= 1), o da ürünü sepete ekler ve stok miktarını 0 olarak günceller (veya -1'e düşürür).

Sonuç: Stokta olmayan bir ürün satılmış, veri tutarlılığı bozulmuş ve iş mantığı ihlal edilmiştir.


Koddaki Tehlikeli Desenler ve Korunma Yöntemleri

Güvenli kod analizi yaparken hedefimiz, bu "kontrol et, sonra hareket et" (check-then-act) mantığını içeren ve paylaşılan bir kaynağı (veritabanı satırı, dosya, oturum verisi vb.) değiştiren kod bloklarını, yani kritik bölümleri (critical sections) tespit etmektir.

Bu desenleri bulduğumuzda sormamız gereken soru şudur: "Bu kritik bölgeye aynı anda birden fazla iş parçacığının girmesini engelleyen bir kilitleme (locking) mekanizması var mı?"

Java gibi dillerde bu mekanizmaları nasıl arayacağımızı anlamak için aşağıdaki kaynağa göz atalım.

Java'da Thread Safety ve Concurrency: Performans Tuzaklarını ...

Bu makale, yarış koşullarının temel mantığını ve Java'daki kod seviyesinde çözümlerini basit örneklerle açıklıyor. Bir kod denetçisi olarak hangi anahtar kelimeleri ve desenleri aramanız gerektiğini anlamak için harika bir başlangıç noktasıdır.

Lütfen makaledeki "Banka Hesabı Problemi" bölümünü okuyarak yarış koşullarının klasik tanımını gözden geçirin. Ardından, "Thread Safety Nedir?" başlığı altında, bu sorunu çözmek için kullanılan synchronized ve Lock gibi temel kilitleme mekanizmalarının nasıl çalıştığını inceleyin. Son olarak, e-ticaret sitesi örneğini okuyarak, az önce tartıştığımız stok kontrolü senaryosunun kodda nasıl göründüğünü ve synchronized ile nasıl korunduğunu görün.

Okuduğunuz gibi, bir kod denetçisi olarak göreviniz, paylaşılan bir durumu değiştiren withdraw() veya checkStockAndBuy() gibi metodları bulmak ve bunların synchronized anahtar kelimesi, bir Lock bloğu veya veritabanı seviyesinde bir kilitleme (örneğin, SQL'de SELECT ... FOR UPDATE) ile korunup korunmadığını doğrulamaktır. Bu korumanın yokluğu, potansiyel bir yarış koşulu zafiyeti için güçlü bir işarettir.

Şimdi bu zafiyetin pratik olarak nasıl sömürüldüğünü ve bir saldırganın bakış açısından nasıl göründüğünü inceleyelim.


Saldırganın Gözünden: Limitleri Aşmak

Yarış koşullarının en yaygın istismar türlerinden biri "Limit Overrun" (Limit Aşımı) olarak bilinir. Bu teknikte saldırgan, bir eyleme konulmuş bir kullanım limitini (örneğin, "bu kupon sadece bir kez kullanılabilir" veya "dakikada en fazla 10 istek gönderebilirsiniz") aşmak için yarış penceresini kullanır.

Aşağıdaki blog yazısı, bu konsepti ve Burp Suite gibi standart araçlarla nasıl test edileceğini uygulamalı olarak anlatıyor.

Race Conditon Zafiyetlerinin Tespiti ve Exploit Edilmesi

Bu kaynak, teoriyi pratiğe dökerek yarış koşullarının nasıl tespit edilip istismar edileceğini adım adım gösteriyor. Sizin gibi pratik deneyimi yüksek bir uzman için bu yaklaşım oldukça tanıdık gelecektir.

Makalenin başındaki "RACE CONDITIONS NEDİR?" bölümünü ve ardından "Limit Overrun Race Condition" bölümünü dikkatlice okuyun. Özellikle kuponların birden fazla kullanılması veya hediye kartlarının defalarca harcanması gibi somut örnekler, bu zafiyetin iş mantığına etkisini net bir şekilde ortaya koymaktadır.

Yazıda da belirtildiği gibi, buradaki ana fikir, limiti kontrol eden ve güncelleyen kod bloğuna, limitin güncellenmesine fırsat vermeden çok sayıda isteği aynı anda göndermektir.

Aşağıdaki görsel, tek kullanımlık bir kupon kodunun (PROMO20) bir POST isteği ile başarıyla uygulandığı anı göstermektedir. Saldırganın amacı, bu isteğin onlarcasını sunucuya aynı anda göndererek, "kupon kullanıldı" bilgisinin veritabanına yazılmasından önce hepsinin kabul edilmesini sağlamaktır.

Solda, bir kuponu uygulamak için gönderilen HTTP POST isteği. Sağda, sunucudan gelen ve kuponun başarıyla uygulandığını belirten yanıt görülmektedir. Yarış koşulu saldırısı, bu isteği çoklu paralel gönderimle defalarca başarılı kılmayı hedefler.

Pratik Uygulama: Yarışın Kaosu ve Zaferi

Blog yazıları ve teorik açıklamalar, yarış koşullarını tespit etmeyi basit gibi gösterebilir. Ancak gerçek dünya, ağ gecikmeleri (network latency), sunucu yükü ve hatta barındırma sağlayıcılarının uyguladığı hız limitleri gibi kontrol dışı faktörlerle doludur. Bu zafiyeti avlamak, genellikle sabır ve tekrar gerektiren bir süreçtir.

Aşağıdaki video, bu "avlanma" sürecini tüm çıplaklığıyla gözler önüne seriyor. Bir geliştirici, yazdığı koddaki bir yarış koşulu zafiyetini canlı olarak istismar etmeye çalışıyor ve bu süreçte karşılaştığı gerçek dünya zorluklarını ve çözüm yollarını gösteriyor.

CodeBreakers: From Dev to Hacker 0x08 | Gerçek Hayatta Race Condition Zafiyetleri Nasıl Hacklenir ?

Bu video, bir blog yazısında okuyamayacağınız pratik bilgileri içeriyor: bir yarış koşulu zafiyetini istismar etmeye çalışırken işler ters gittiğinde bir uzmanın nasıl düşündüğünü ve sorunları nasıl aştığını gösteren bir vaka çalışmasıdır.

Lütfen videodaki şu kritik anları izleyin: Kurulum ve İlk Denemeler: Sunucu, iki kullanıcı hesabı arasında kredi transferi yapılan bir senaryo kurar. Burp Suite kullanarak basit bir paralel istek saldırısı dener, ancak beklendiği gibi çalışmaz. Bu, teorinin pratiğe her zaman uymadığının harika bir göstergesidir. Hata Ayıklama ve Engelleri Aşma: Bu bölüm, dersin en değerli kısımlarından biridir. Sunucu, saldırının neden başarısız olduğunu anlamak için hata ayıklama sürecine girer. İstek sayısı, zamanlama ve en önemlisi, barındırma sağlayıcısının hız limitlemesinin saldırıyı nasıl engellediğini keşfeder. Israrlı denemeler sonucunda nihayet zafiyeti tetiklemeyi başarır. Zafiyetin Açıklaması: Başarılı istismarın ardından, kodun hangi bölümünün zafiyetli olduğunu ("check-then-act" mantığı) ve yarış penceresinin tam olarak nerede oluştuğunu teknik olarak açıklar. Bu, saldırıyı tekrar kaynak koda bağlar.

Bu video, yarış koşullarının ne kadar hassas ve öngörülemez olabileceğini, ancak doğru metodoloji ve araçlarla tespit edilip kanıtlanabileceğini mükemmel bir şekilde göstermektedir.


Kod Analizi Stratejisi

Videodan ve makalelerden öğrendiklerimizi birleştirerek, bir kod denetçisi olarak yarış koşullarını bulmak için izlemeniz gereken stratejiyi özetleyebiliriz:

  1. Paylaşılan Durumu Belirleyin: Kod tabanında, birden fazla işlemin aynı anda erişip değiştirebileceği paylaşılan kaynakları arayın. Bunlar genellikle veritabanı kayıtları (balance, stock, usage_count), oturum (session) verileri, önbellek (cache) girişleri veya dosya sistemindeki dosyalardır.
  2. Kritik Bölümleri Tespit Edin: Bu paylaşılan kaynaklar üzerinde "kontrol et, sonra hareket et" (check-then-act) mantığını uygulayan kod bloklarını bulun. Örneğin: "kullanıcının bakiyesi yeterli mi? -> evet -> bakiyeyi düşür".
  3. Kilitleme Mekanizmalarını Doğrulayın: Bu kritik bölümlerin, iş parçacığı güvenliği sağlayan mekanizmalarla korunup korunmadığını kontrol edin. Kodda synchronized, lock, mutex gibi anahtar kelimeler veya veritabanı sorgularında SELECT ... FOR UPDATE gibi ifadeler arayın. Bu mekanizmaların yokluğu büyük bir tehlike işaretidir.
  4. Çok Adımlı İşlemleri Gözden Geçirin: Zafiyet her zaman tek bir fonksiyonda olmaz. Bazen bir iş akışı, aynı paylaşılan durumu değiştiren birden fazla adımdan (veya birden fazla API endpoint çağrısından) oluşur. Örneğin, bir sepete ürün ekleme (POST /cart) ve ödeme yapma (POST /checkout) işlemleri. Bu iki adım arasında bir yarış koşulu olabilir mi? Saldırgan, ödeme süreci başladıktan sonra ama onaylanmadan önce sepete pahalı bir ürün ekleyebilir mi? Bu tür karmaşık senaryolar, uzman bakışı gerektirir.

Sonuç

Bu dersimizde, iş mantığı zafiyetlerinin zamanlama boyutuna odaklandık ve yarış koşullarının nasıl ortaya çıktığını, kodda nasıl tespit edileceğini ve pratikte nasıl istismar edilebileceğini öğrendik.

Temel Çıkarımlar:

  • Yarış koşulları, bir kaynağın durumunun kontrol edildiği an ile kullanıldığı an arasındaki zaman boşluğundan (TOCTOU) kaynaklanır.
  • Limitler, bakiyeler ve kullanım sayaçları gibi paylaşılan durumları yöneten operasyonlarda sıkça görülürler.
  • Kod analizi, paylaşılan kaynakları, kritik bölümleri ve bu bölgedeki eksik kilitleme mekanizmalarını bulmaya odaklanmalıdır.
  • İstismar, genellikle yarış penceresini hedef alan çok sayıda paralel isteğin gönderilmesini içerir ve ağ koşulları gibi dış faktörlere oldukça duyarlıdır.
  • Zafiyet, tek bir endpoint içinde olabileceği gibi, birden fazla endpoint'i kapsayan karmaşık bir iş akışına da yayılabilir.

Önceki dersimizde olayların doğru sırasını, bu dersimizde ise doğru zamanlamasını ele aldık. Bir sonraki dersimizde, bu derste temelini attığımız TOCTOU konseptini genelleştireceğiz. Sadece eşzamanlılık bağlamında değil, aynı zamanda bir dosyanın izinlerini kontrol edip sonra o dosyayı okumak gibi diğer senaryolarda TOCTOU zafiyetlerinin nasıl ortaya çıkabileceğini ve tespit edileceğini inceleyeceğiz. Bu, bir güvenlik zafiyetinin farklı bağlamlarda nasıl tezahür edebileceğini anlamak için önemli bir adım olacaktır.