Saat gece yarısını çoktan geçmişti, 03:12 falandı. Terminal başında testCompany bünyesindeki yeni bir mikroservisin trafik loglarını incelerken gözüme bir anormallik çarptı. Bir PDF oluşturma servisi, kullanıcıdan aldığı URL parametresini kullanarak içeriği render ediyordu. Ama loglarda, servisin kendi iç ağındaki 169.254.169.254 adresine, yani bulut sağlayıcısının metadata servisine doğru 'anlamsız' bir yolculuğa çıktığını gördüm. O an uykumun tamamen açıldığını hatırlıyorum. Karşımızdaki basit bir bug değil, içerideki tüm yetkili anahtarları (IAM credentials) dışarı sızdırabilecek bir Server-Side Request Forgery (SSRF) açığıydı.
Bir Red Team Lead olarak sahada en çok karşılaştığım ve geliştiricilerin 'ne olacak canım, sadece linki ziyaret ediyor' diyerek hafife aldığı bu saldırı türü, aslında kalenin surlarını içeriden yıkan truva atıdır. Bugün bu mevzunun derinine, yani o kodun içindeki karanlık dehlizlere ineceğiz.
Masum Bir Fonksiyon Nasıl Silaha Dönüşür?
Geliştiriciler olarak işleri kolaylaştırmayı severiz. Kullanıcının profil fotoğrafını bir URL'den çekmek, bir web sayfasının ekran görüntüsünü almak veya bir faturayı PDF'e dönüştürmek harika özelliklerdir. Ancak bu işlemleri yaparken sunucunun bir 'istek makinesine' dönüştüğünü unutuyoruz.
Vulnerable bir Node.js koduna bakalım (Tabii ki defanged formatta):
// ZAFİYETLİ KOD ÖRNEĞİ
const express = require('express');
const axios = require('axios');
const app = express();
app.get('/generate-pdf', async (req, res) => {
const targetUrl = req.query.url; // Kullanıcıdan kontrolsüz gelen URL
try {
// Sunucu, kullanıcının verdiği URL'e sorgu atıyor
const response = await axios.get(targetUrl);
// ... PDF oluşturma işlemleri ...
res.send("İşlem başarılı");
} catch (error) {
res.status(500).send("Hata oluştu");
}
});
Buradaki sıkıntı şu: Saldırgan url parametresine http://google.com yazmak yerine, sunucunun erişebildiği ama internete kapalı olan iç servisleri yazar. Örneğin: http://127.0.0.1:8080/admin/delete-user?id=1. Sunucu, kendi içindeki admin paneline 'merhaba' der ve komutu saniyeler içinde uygular.
Metadata Servislerine Çökmek
İşin rengi asıl bulut (Cloud) ortamlarında değişiyor. AWS, Google Cloud veya Azure gibi platformlarda, her instance kendi metadata bilgilerine 169[.]254[.]169[.]254 (link local address) üzerinden erişir. Bir saldırgan SSRF bulduğunda ilk deneyeceği payload şudur:
http://169[.]254[.]169[.]254/latest/meta-data/iam/security-credentials/[ROLE_NAME]
Eğer bu istek başarılı olursa, sunucunun sahip olduğu tüm yetkileri temsil eden geçici AccessKeyId, SecretAccessKey ve Token bilgileri saldırganın terminaline düşer. Artık saldırgan senin sunucunmuş gibi davranabilir. testCompany senaryosunda yakaladığım şey tam olarak bu girişimdi. Neyse ki hızlı bir müdahaleyle o yetkileri kısıtlamayı başardık.
Filtreleme mi? Güldürme Beni
Birçok kişi bu durumu engellemek için 'Blacklisting' yöntemine başvurur. 'Eğer URL içinde 127.0.0.1 varsa engelle' derler. Ama bypass yöntemleri o kadar yaratıcı ki şaşırırsınız:
- Decimal IP:
127.0.0.1yerine2130706433yazmak. - Octal IP:
0177.0.0.1. - DNS Rebinding: En tehlikelisi budur. Bir domain önce zararsız bir IP'ye (Örn: 8.8.8.8) çözülür, uygulama güvenlik kontrolünü geçer, tam o sırada saldırgan DNS kaydını
127.0.0.1olarak değiştirir ve uygulama veriyi çeker.
Savunma Hattını Nasıl Kurarız?
Bu işin 'çözdük' denilecek bir sihirli değneği yok ama 'Hardening' dediğimiz o sertleştirme adımları hayat kurtarır.
- Allowlist (Beyaz Liste): Sadece güvenli olduğu bilinen domainlere (
api.example.com,images.static.com) izin ver. Geri kalan her şeyi drop et. - Network Segmentation: Uygulama sunucusunun iç ağdaki hassas servislere (veritabanı, metadata vb.) erişimini firewall seviyesinde kısıtla. Bir web sunucusunun metadata servisiyle işi ne abi? Kapatın gitsin (veya AWS kullanıyorsan IMDSv2'ye geç ve hop sayısını 1 yap).
- Response Validation: Sunucudan dönen cevabı körü körüne kullanıcıya basma. Dönen verinin tipini (MIME type) ve boyutunu kontrol et.
- DNS Pinning: Uygulama seviyesinde DNS çözünürlüğünü sabitleyerek DNS Rebinding saldırılarının önüne geç.
Şu güvenli kod yapısına bir göz atalım:
# GÜVENLİ YAKLAŞIM (PSEUDO-CODE)
import requests
from urllib.parse import urlparse
def fetch_user_content(user_url):
# 1. Protokol kontrolü (Sadece HTTPS)
parsed = urlparse(user_url)
if parsed.scheme != "https":
raise Exception("Güvenli olmayan protokol!")
# 2. Beyaz liste kontrolü
allowed_domains = ["trusted-assets.testCompany.com", "cdn.partner.com"]
if parsed.netloc not in allowed_domains:
raise Exception("Yetkisiz kaynak!")
# 3. İç ağ (Private IP) kontrolü (Gelişmiş kütüphanelerle yapılmalı)
# ... check_if_private_ip(parsed.netloc) ...
response = requests.get(user_url, timeout=5)
return response.content
Sahanın Gerçeği
Siber güvenlik bir ürün değil, bir süreçtir kankalar. Bugün kapattığın SSRF açığı, yarın başka bir kütüphanenin zafiyetiyle tekrar hortlayabilir. Red Team ekiplerinin işi sadece açık bulmak değil, geliştiricilere bu 'güvenlik bakış açısını' aşılamaktır. testCompany loglarında o gece gördüğüm o tek satırlık anomali, belki de milyonlarca satır verinin sızmasını engelledi.
Kod yazarken her zaman şu soruyu sorun: 'Ben bu veriyi dışarıdan alıyorum, peki bu veri benim sistemimin neresine dokunuyor?' Eğer cevabınız 'bilmiyorum' ise, o kod muhtemelen zafiyetlidir.
Siber dünyada kalın, ama güvenli tarafta. Görüşmek üzere.
