Acme Corp (isim değiştirildi). Kurucu çiftin işi, 2026'nın üçüncü çeyreği. Rakamların tamamı ölçüldü.

Sunucular temizdi. Kod değildi.

Başka yerlerden çalınmış parolalar, bir kaydın kime ait olduğuna hiç bakmayan bir endpoint, kodda ve web sunucusunda beş kök neden. Önce yalnızca okuduk, sonra müdahale ettik. Nasıl çalıştığımız ve neyi ölçtüğümüz aşağıda.

Ne oldu

Acme Corp, multi-tenant bir web platformu işletiyor: nginx ve load balancer'ların arkasında bir PHP uygulaması. Ticari VPN ve hosting adreslerinden gelen otomatik taramalar, biz çalışmaya başladığımızda üç gündür sürüyordu.

Bu taramaların ardından bir saldırgan, başka yerlerden çalınmış parolaları platformun login sayfasında denedi ve en az 10 gerçek kullanıcı hesabına girdi: 908 denemede 374 başarılı giriş. Kendine ait bir hesap açamadı; kayıt denemelerini bir CAPTCHA durdurdu. Ele geçirdiği oturumları kullanarak, kaydın kime ait olduğuna hiç bakmayan bir endpoint'ten diğer tenant'ların kayıtlarını toplu hâlde çekti.

Nasıl çalıştık

Tek bir Claude Code oturumunda, bir subagent ile çalıştık; önce yalnızca okuduk. Log'ları, kodu ve sunucuları Claude Code okudu; soruları biz sorduk, her cevabı biz değerlendirdik. Her değişikliğin bir yedeği ve bir geri dönüş yolu vardı.

İlk 36 dakika

Log'lara sorduk: veriler hangi adreslerden çekildi? İlk istekten itibaren saldırının imzası yalnızca ticari VPN ve hosting aralıklarından geliyordu. İşin 36. dakikasında saldırganın adreslerini uygulamada ve load balancer'larda engelledik, veri sızdıran endpoint'leri geçici olarak kapattık. Engellemeden sonra saldırgan 94.000'den fazla istek daha gönderdi; hiçbiri geçmedi.

Log'lardaki her adresi dört gruba ayırdık: müşteri, bot, VPN/veri merkezi ve ev bağlantısı. Ev bağlantılarını tek tek kontrol ettik; her biri platformun gerçek bir müşterisine aitti.

Sunucular temizdi

Acme Corp'un 16 VM'inin hepsini yalnızca okuyarak taradık: yeni kullanıcı, web shell, beklenmedik dosya değişikliği ya da process yoktu. Kaynak kod, veritabanı parolaları ve dosya sistemi dışarı sızmamıştı. İhlal uygulama katmanındaydı.

Beş kök neden

Koda sorduk: saldırganı içeri ne aldı? Kodun cevabı beş kök nedendi.

  1. Sahiplik kontrolü yok. Kayıtların sızdığı endpoint ve onlarca başka endpoint, bir kaydı hangi tenant'a ait olduğuna bakmadan döndürüyordu.
  2. Göstermelik koruma. 50 endpoint tenant ID'yi request body'den alıyordu; her kullanıcı başka bir tenant'ın ID'sini gönderebiliyordu.
  3. Router bypass. Internal modül ve core path'leri, doğrudan gelen çağrılara login istemeden cevap veriyordu.
  4. Rate limit yok. Login ve doğrulama kodu denemelerinin sıklığını hiçbir şey sınırlamıyordu.
  5. Açıkta kalan dosyalar. Bir phpinfo sayfası, directory listing'ler ve bağımlılıkların lock dosyaları herkese açıktı.

Neleri değiştirdik

İlk dördünü, ilk mesajdan sonraki üç saat dolmadan kodda ve nginx'te kapattık.

  • Tek bir ownership guard. Bir oturum başka bir tenant'ın kaydına dokunduğunda 403 dönen tek bir PHP fonksiyonu. Oturum ya da kayıt yoksa isteği geçirir; böylece cron job'ları ve webhook'lar çalışmaya devam eder. Bütün kod tabanını taradıktan sonra guard'ı 229 dosyaya ekledik.
  • Internal path'lere 404. nginx, internal path'lere gelen doğrudan çağrılara 404 döner.
  • Rate limit. nginx, login ve doğrulama kodu POST'larını IP adresi başına sınırlar; GET istekleri etkilenmez.
  • Canlı nginx geçişi. Rate limit için bir modül gerekiyordu, ama çalışan nginx bu modül olmadan derlenmişti. Aynı sürümün resmî paket binary'sine USR2 ile, servisi durdurmadan geçtik ve eski master'ın elindeki istekleri bitirmesini bekledik. Bu geçişte tek bir istek bile düşmedi. Otomatik bir güncelleme bu değişikliği geri almasın diye paketleri hold'a aldık.

Değişen her PHP dosyası php -l kontrolünden, her nginx değişikliği de reload'dan önce nginx -t kontrolünden geçti. Beşinci kök nedeni, açıkta kalan dosyaları, yapılacaklar listesine yazıp Acme Corp'un kendi ekibine devrettik.

Nasıl kontrol ettik

  • Guard'ın unit testi. 5 senaryonun 5'i de geçiyor: aynı tenant geçer, başka bir tenant 403 alır; oturum yoksa, kayıt yoksa ya da ID boşsa istek geçer.
  • Patch sonrası production. 219 meşru istek 200 aldı: 0 false positive. Guard'ın log etiketi gerçek kullanıcılar için boş kaldı.
  • Rate limit. 10 hızlı login POST'u: 7'si 200 aldı, ardından 429 döndü.
  • Engelleme. Engellemeden sonra gelen 94.000'den fazla saldırgan isteğinin hiçbiri geçmedi.

Acme Corp'a teslim ettiklerimiz

Acme Corp'un ekibine eksiksiz bir rapor teslim ettik: gerçek diff'ler, zaman çizelgesi, her adresin nereden geldiği ve bir yapılacaklar listesi. Rapor, izlenecek bir sonraki ipucunu da gösterdi: bir TLS parmak izi.

  • 36 dkçalışma oturumumuzun başından saldırgan engellenene kadar
  • 0 / 94.000+engellemeden sonra geçebilen saldırgan isteği
  • 16 / 16yalnızca okuyarak taradığımız VM temiz çıktı: yeni kullanıcı, web shell ya da beklenmedik dosya değişikliği yok
  • 4 / 5kök nedeni üç saat dolmadan, kodda ve nginx'te kapattık; beşincisini Acme Corp'un kendi ekibine devrettik
  • 374 / 908giriş denemesi çalınmış parolalarla başarılı oldu; saldırgan en az 10 gerçek kullanıcı hesabına girdi
  • 50endpoint tenant ID'yi request body'den alıyordu
  • 229dosyayı tek bir ownership guard koruyor
  • 0 / 219meşru istek patch sonrası guard'a takıldı
  • 5 / 5guard'ın geçtiği unit test senaryosu
  • 0canlı nginx geçişinde düşen istek

Sizin ortamınızda da aynı şekilde, yalnızca okuyarak başlarız; her değişiklik sizin onayınızı bekler. Sağlık kontrolü altyapınızı okur; kod ya da bir olay için görüşme ayarlayın.

Ana sayfaya dön