Skip to content
Sedat Özdemir
Writing

cloud-security

Görünmez Duvarları Aşmak: PDF Generator'dan Cloud Metadata'ya Uzanan O Tehlikeli Yol

Bir PDF oluşturma servisinin nasıl bir iç ağ casusuna dönüştüğünü ve bulut ortamındaki en değerli anahtarların nasıl tehlikeye girdiğini teknik bir kriz anıyla inceliyoruz.

Sedat Özdemir
· 3 dk read

Saat 03:14. Terminal ekranındaki o soğuk beyaz imleç yanıp sönüyor, ben ise kahve makinesinin çıkardığı o rahatsız edici sesi dinliyorum. testCompany'nin ana dashboard'unda bir gariplik vardı. Normalde saniyede 5-10 arası istek alan iç ağdaki bir mikroservis, aniden bir trafik patlaması yaşıyordu. İşin garibi, gelen istekler dışarıdan değil, bizim kendi 'PDF Generator' servisimizden geliyordu. Birileri bizim içerideki kaleyi, içerideki bir askerle vurmaya başlamıştı.

O gece anladım ki, SSRF (Server-Side Request Forgery) dediğimiz şey sadece akademik bir makale konusu değil; düzgün yapılandırılmamış bir 'URL' parametresinin, tüm bulut altyapınızı teslim edebileceği bir anahtar.

Olayın Perde Arkası: Güvenli Görünen Fonksiyon

Senaryo çok tanıdık: Kullanıcı bir link verir, sistem o linke gider, içeriği alır ve PDF'e dönüştürür. 'User Experience' için harika, değil mi? Ama bir saldırgan için bu, 'Gel beni sömür' demektir. Geliştirici arkadaşımız (ismini vermeyelim, o kendini biliyor) URL'i kontrol eden basit bir regex yazmış. Sadece http ve https ile başlamasını zorunlu tutmuş. Ama unuttuğu bir şey vardı: Sunucunun kendi iç dünyası.

Saldırgan önce basit bir tarama yapmıştı:

POST /api/v1/generate-pdf
Payload: {"url": "http://127.0.0.1:8080/admin"}

Bu istekten dönen '403 Forbidden' cevabı, saldırgana içeride 8080 portunda bir şeylerin çalıştığını fısıldadı. Dışarıya kapalı olan o 'admin' paneli, içeriden erişilebilir durumdaydı.

Metadata Servisi: Hazine Odasının Kapısı

Asıl kriz, saldırganın hedefi bulut sağlayıcısının metadata servisine çevirmesiyle başladı. AWS, Azure veya GCP fark etmez; her birinin sunuculara kendi kimlik bilgilerini sunduğu bir 'Magic IP'si vardır: 169.254.169.254.

Saldırgan şu payload'u gönderdiğinde işler ciddileşti:

POST /api/v1/generate-pdf
Payload: {"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/app-role"}

Eğer bu PDF servisi bir bulut instance'ı üzerinde çalışıyorsa ve servis rolü (IAM Role) gereğinden fazla yetkiye sahipse, dönen PDF dosyasının içinde AccessKeyId, SecretAccessKey ve Token kabak gibi görünecektir. Artık saldırganın sizin sunucunuzda kod çalıştırmasına gerek yok; o anahtarları kendi makinesine kopyalayıp sizin adınıza 'bulut' üzerinde at koşturabilir.

Neden Patlıyoruz? (The Root Cause)

Genelde yapılan en büyük hata 'Blacklist' (Kara Liste) yaklaşımıdır. 127.0.0.1'i engellersin, saldırgan 0.0.0.0 ile gelir. localhost dersin, 127.0.0.1.nip.io gibi DNS servisleriyle bypass eder. Hatta IPv6 kullanıp [::] üzerinden sızmaya çalışır.

İşin teknik derinliğinde 'TOCTOU' (Time-of-Check Time-of-Use) dediğimiz bir bela da var. Sen URL'i kontrol ettiğin anda IP güvenlidir, ama tam o isteği yapacağın saniyede DNS değişir ve sunucu aslında gitmemesi gereken bir yere gider.

Defans Hattını Nasıl Kurarız?

O geceyi sunucuları kapatıp DNS seviyesinde filtreleme yaparak kurtardık ama kalıcı çözüm bu değil. Eğer bir gün bu tarz bir özellik geliştireceksen, şu adımları 'mandatory' (zorunlu) olarak ajandana ekle:

  1. Whitelist (Beyaz Liste) Şart: Sadece izin verdiğin domain'lere veya IP bloklarına izin ver. Eğer kullanıcının her yere erişmesi gerekiyorsa, bunu izole edilmiş bir network üzerinden yap.
  2. Network Segmentation: PDF üreten sunucun neden iç ağdaki database'e veya metadata servisine erişebiliyor? Onu bir sandbox'a koy, dış dünyayla ve iç dünyayla bağını sadece ihtiyacı kadar kur.
  3. IMDSv2 Kullanımı: Eğer AWS kullanıyorsan, IMDSv2'ye geçiş yap. Bu versiyon, metadata erişimi için bir 'Session Token' (PUT isteği ile alınan) zorunluluğu getirir. SSRF saldırılarının %90'ı sadece GET isteklerine dayandığı için bu hayat kurtarır.
  4. Zararsızlaştırılmış (Defanged) Örnek Kod:

Aşağıdaki gibi basit bir kontrol asla yeterli değildir:

# KÖTÜ ÖRNEK - Sakın yapma!
import requests

def generate_pdf(user_url):
    if "169.254" in user_url:
        return "Yasaklı!"
    return requests.get(user_url).content

Bunun yerine, isteği yapmadan önce IP adresini çözüp (resolve), özel/yerel IP bloklarını (Private IP Ranges) kontrol eden bir yapı kurmalısın:

# DAHA İYİ BİR YAKLAŞIM (Mockup)
import socket
from ipaddress import ip_address

def is_safe_url(url):
    hostname = url.split('/')[2]
    ip = socket.gethostbyname(hostname)
    if ip_address(ip).is_private:
        return False # İç ağa erişim yasak!
    return True

İşin Özü

Red Team tarafında biz bu tarz açıkları 'zincirleme' (chaining) bayılırız. Basit bir PDF oluşturma aracı, bir bakmışsınız S3 bucket'larınızdaki müşteri verilerine giden yolun ilk basamağı olmuş.

O gece saat 05:30'da sistemleri stabilize ettiğimizde şunu düşündüm: Güvenlik, kodun arasına serpiştirilmiş 'if' blokları değil, mimari bir duruştur. Eğer sistemini 'herkes bana saldırabilir' varsayımıyla kurmazsan, bir gece yarısı o kahve makinesinin gürültüsünde terminal başında uyanık kalmak zorunda kalırsın.

Siz siz olun, dışarıdan gelen her veriyi (URL dahil) birer el bombası gibi düşünün. Pimi çekilmiş mi yoksa çekilmemiş mi diye bakmak yerine, o bombayı güvenli bir odada (sandbox) patlatmayı tercih edin.

Safe coding, bir sonraki kriz anında görüşmek üzere. O zamana kadar loglarınıza iyi bakın.

Related posts