İçeriğe atla
0 XP 0
12 dkderinleşmeyle 13 dk55 XP7 etkinlik

Şablon ve NoSQL enjeksiyonu

Bu derste
  • Şablon motoru ile NoSQL sürücüsü de birer yorumlayıcıdır; ikisi de girdiyi yanlış yerde değerlendirirse enjeksiyona açılır.
  • Kullanıcı girdisi bir şablonda kod ya da bir sorguda komut olarak yorumlanabilir. Bu derste iki riski tanır, güvenli şablon kullanımı ve tür kontrolüyle önlem alırsın.

Puan bütün etkinlikleri cevaplayınca hesaba yazılır. Bu dersi tamamlayınca +55 XP alacaksın. Giriş yapmadan ilerlersen puan yalnız bu tarayıcıda durur.

Enjeksiyonun kökü hep aynıdır ama yorumlayıcı değişir. Bu derste iki tanıdık olmayan yorumlayıcıyı ele alırsın: şablon motoru ve NoSQL sürücüsü. Şablon motoru kullanıcı verisini kalıbın kendisine karıştırırsa, motor o veriyi bir ifade olarak çalıştırır ve iş sunucuda döner.

NoSQL sürücüsü gövdeden gelen alanı doğrudan sorgu belgesine koyarsa, saldırgan değer yerine bir operatör yollayıp sorgunun anlamını çevirir. İki açığın da savunması aynı ilkeye dayanır: veriyi yapının dışında, ayrı bir kanalda taşımak. En sonda XPath ve LDAP gibi yakın akrabalara da kısaca bakarsın.

Şablon motoru bir yorumlayıcıdır

Şablon motoru, bir metin kalıbının içine değişkeni yerleştirir. Motor, çift süslü parantez arasındaki her şeyi bir ifade olarak değerlendirir, yalnız değişken adı olarak değil. Uygulama, kullanıcının yazdığı bir metni (örneğin profil biyografisini) bu kalıbın kendisine, sabit metin yerine doğrudan koyarsa, kullanıcı kendi süslü parantezini yazabilir hâle gelir. Altay Kargo'nun hoşgeldin e-postası, kullanıcı adını şablon dizesinin bir parçası olarak birleştiriyor olsun.

Kullanıcı adına 7 çarpı 7 işlemini süslü parantezle yazan biri, e-postada 49 gördüyse motor ifadeyi çalıştırmış demektir. Sunucu tarafı şablon enjeksiyonunun (SSTI) klasik ilk testi budur. Bazı motorlarda ifade dili dosya okumaya, hatta işletim sistemi komutu çalıştırmaya kadar uzanır.

Şablon motorunun işini anlamak açığı da anlatır. Motor, sabit kalıptaki yer tutucuları verilerle doldurur: kalıp geliştiricinindir, veri kullanıcınındır ve ikisi ayrı durur.

Sorun, geliştirici kullanıcı verisini kalıbın kendisine katınca çıkar. O an kullanıcı, yalnız bir yer tutucunun değerini değil, kalıbın söz dizimini de yazabilir. Motor, süslü parantez içindeki her şeyi çalıştırılacak bir ifade sayar; bir değişken adı da, bir aritmetik işlem de, bir nesne erişimi de aynı yere gider.

CWE bu sınıfı CWE-1336 olarak kaydeder. Kök neden yine aynıdır: kullanıcı verisi, yorumlanacak ifadenin sınırını belirleyen özel karakterlerle birleşmiştir.

Fark, yorumlayıcının bu kez bir şablon motoru olması ve bazı motorların ifade dilinin tam bir programlama diline yaklaşmasıdır. Bu yüzden SSTI, XSS'ten çok daha ağır sonuç doğurabilir; XSS tarayıcıda çalışır, SSTI sunucuda çalışır. İkisinin ayrımı önemlidir: tarayıcıda çalışan kod kullanıcının oturumunu hedefler, sunucuda çalışan ifade tüm uygulamayı ele geçirebilir.

Etkinlik 1 / 7

Şablon motoru kullanıcı adını doğrudan şablon dizesine ekliyor. Kullanıcı adına süslü parantezli bir 7*7 çarpımı yazılırsa e-postada ne görünür?

Önce kodu oku, sonra çıktıyı seç.

sablon = "Merhaba " + kullaniciAdi + ", siparisin yolda."
motor.render(sablon)

Motora göre ilk test ve log izi

SSTI'ı tespit etmenin en yaygın yolu bir aritmetik ilk testtir: sayı çarpımı içeren bir ifade yazıp sonucun hesaplanıp hesaplanmadığına bakarsın. Sonuç hesaplanıyorsa motor ifadeyi çalıştırıyor demektir. İkinci adım hangi motorun çalıştığını ayırmaktır, çünkü her motorun ifade dili ve tehlike düzeyi farklıdır. Ustaca seçilmiş bir yük iki motoru tek istekte ayırt eder.

Örneğin süslü parantez içinde bir sayıyı bir metinle çarpan ifade, Twig motorunda 49 verirken Jinja2 motorunda 7777777 verir; ikisi de değilse ortada şablon motoru yoktur. Motoru bilmek, savunmacının hangi yamayı ve hangi yapılandırmayı uygulayacağını da belirler. Aşağıdaki çizelge yaygın motorları ve çalıştıkları dili toplar.

İlk test yalnız başlangıçtır. Motor ifadeyi çalıştırıyorsa, saldırganın bir sonraki adımı ifade dilinin ne kadar ileri gittiğini ölçmektir. Bazı motorlarda ifade yalnız veri okur; bazılarında nesne ağacında gezinip sunucunun ortam bilgisine, dosya sistemine, hatta komut çalıştırma yollarına ulaşır.

Tehlike düzeyi bu yüzden motora göre değişir; savunmacının önce hangi motorun kullanıldığını bilmesi bundan ötürü önemlidir. Sunucuda çalışan bir ifade, en kötü durumda uygulamanın tümünü ele geçirebilir.

Motor        Dil       İlk test               Not
Jinja2       Python    {{7*7}} -> 49          {{7*'7'}} -> 7777777
Twig         PHP       {{7*7}} -> 49          {{7*'7'}} -> 49
Freemarker   Java      ${7*7} -> 49           ifade dili geniş
Velocity     Java      #set ile atama         eski, hâlâ yaygın
Pug          Node.js   #{7*7} -> 49           JS ifadesi
Aynı aritmetik ilk test motora göre farklı çıktı verir; bu fark motoru parmaklamaya yarar.

Etkinlik 2 / 7

Her şablon motorunu çalıştığı dille eşle.

Soldan bir tane seç, sağdan eşini seç. Kullanılmış bir seçeneği yeniden seçersen yeni öğeye taşınır.

Üretim ortamında SSTI izini loglardan okursun. Şablon motoru bir hata fırlattığında, hata mesajında motorun adı ve genelde çalıştırılmaya çalışılan ifadenin kendisi geçer. Aynı alana art arda gelen, süslü parantez taşıyan farklı denemeler, saldırganın motoru parmakladığını gösterir.

Sıradan bir kullanıcı adı ya da biyografi bu söz dizimini taşımaz. Bu yüzden şablon motorunun ürettiği ayrıştırma istisnalarını toplamak ve süslü parantezli girdi desenini bir kuralla izlersen, açık henüz kötüye kullanılmadan denemeyi yakalarsın.

Etkinlik 3 / 7

E-posta motorunun hata kaydında SSTI denemesine ait satırları işaretle.

Şüpheli satırlara tıkla. Birden fazla olabilir.

SSTI'ı kapatmak: veriyi değişken olarak geçir

SSTI'ın çözümü bağlama göre kodlamadır: kullanıcı verisi, şablonun kendisine değil, şablonun bir değişkenine yerleştirilir. Motor yalnız değişkenin değerini metne basar, onu yeniden bir ifade olarak değerlendirmez. Sabit şablon dosyasını motor okur, kullanıcı adını da yalnız bir değer olarak geçirirsin.

Motorun bir sanal alan (sandbox) modu varsa onu da açarsın; örneğin Jinja2, güvenilmez şablonlar için SandboxedEnvironment sunar. Ama Jinja belgeleri açıkça uyarır: sanal alan tek başına kusursuz güvenlik değildir; küçük bir şablon büyük bir çıktı üretip kaynağı tüketebilir. Asıl kural değişmez: kullanıcı verisiyle şablon dosyasının kendisini hiç oluşturma.

Bağlama göre kodlamanın yanında iki katman daha vardır. Birçok motor, değişken değerini basarken çıktı kodlamasını (autoescape) açık tutar; bu, şablona sızan bir değerin HTML olarak yorumlanmasını da engeller. Daha katı bir seçim, mantıksız (logic-less) şablon motorları kullanmaktır; bunlar ifade değerlendirmeyi baştan desteklemez, yalnız değer basar, dolayısıyla SSTI için gereken zemin hiç oluşmaz. Motor bir sanal alan sunuyorsa onu açmak riski azaltır, ama Jinja belgelerinin uyardığı gibi sanal alan tek başına kusursuz güvenlik değildir.

// Güvensiz: kullanıcı verisi şablon dizesine karışıyor
sablon = "Merhaba " + kullaniciAdi + ", siparisin yolda."
motor.render(sablon)

// Güvenli: kullanıcı verisi yalnız değişken olarak geçiyor
motor.render("hosgeldin.html", { ad: kullaniciAdi })
Güvenli sürümde motor, sabit şablon dosyasını okur ve ad değerini yalnız metin olarak yerleştirir.

Etkinlik 4 / 7

SSTI'a açık satırı bul.

Zafiyetli satıra tıkla.

NoSQL: aynı kök, JSON yorumlayıcı

NoSQL veritabanları, sorguyu metin yerine bir belge (JSON benzeri nesne) olarak alır. Bu ayrım enjeksiyonu imkansız kılmaz, yalnız biçimini değiştirir. Altay Kargo'nun giriş ucu, gövdeden gelen alanları doğrudan sorgu belgesine geçiriyor olsun. Saldırgan, parola alanına düz bir metin yerine bir karşılaştırma operatörü taşıyan bir nesne yollarsa, sorgunun anlamı değişir: artık belirli bir parolayı değil, alanın boş olmadığını arar.

Yaygın operatörler MongoDB'de eşit değil için ne, büyüktür için gt, dizi içinde için in ve düzenli ifade için regex adını taşır. Parola hiç bilinmeden, sorgunun ilk döndürdüğü kullanıcı olarak giriş açılır. Kök neden yine aynıdır: uygulama, gövdeden gelen veriyi sorgu yapısının bir parçası olarak, doğrulamadan kabul etmiştir.

NoSQL enjeksiyonu iki biçimde görünür. Operatör enjeksiyonunda saldırgan, az önceki gibi bir karşılaştırma operatörünü sorgu belgesine sokar. Söz dizimi enjeksiyonunda ise sorgunun bir dizeye gömülü kısmını bozar. Bazı sürücülerde bir ifade değerlendiren operatör, veritabanının içinde kod çalıştırmaya kadar gider.

Düzenli ifade eşleyen bir operatör ise veriyi karakter karakter sızdıran kör bir kanal açar. İki biçimin de kökü aynıdır: gövdeden gelen veri, sorgunun yapısına doğrulanmadan girmiştir. Bu yüzden savunma hem türü denetler hem de operatör anahtarlarını girdiden ayıklar.

Etkinlik 5 / 7

Giriş ucu, gövdedeki parola alanını doğrudan sorgu belgesine geçiriyor. Saldırgan parola alanına, boş olmama koşulu taşıyan bir operatör nesnesi gönderiyor. Bu neden çalışır?

Çözüm iki katmandır. Önce girdinin türünü doğrularsın: parola alanı yalnız metin (string) kabul eder, nesne ya da dizi reddedilir. Sonra sorgu kurucu bir kütüphane kullanırsın; bu kütüphaneler operatör anahtarlarını kullanıcı girdisinden otomatik ayıklar.

Örneğin Mongoose'un sanitizeFilter süzgeci, adı dolar işaretiyle başlayan alanları bir eşitlik operatörüne sarar; böylece ne operatörü, aranan değerin kendisi hâline gelir ve koşulu bozamaz. Sürücünün resmi sorgu kurucusu bu ayıklamayı destekler, ama açık bırakılmış bir gövdeyi elle süzmeden doğrudan sorguya vermek riski geri getirir.

// Güvensiz: govde alanları doğrudan sorguya geçiyor
db.kullanici.findOne({ ad: govde.ad, parola: govde.parola })

// Saldırganın gönderdiği gövde
{ "ad": "ayse.k", "parola": { "$ne": null } }

// Güvenli: tür doğrulaması önce çalışıyor
if (typeof govde.parola !== "string") return res.status(400).end();
db.kullanici.findOne({ ad: govde.ad, parola: govde.parola })
Operatör nesnesi olarak gelen parola alanı, tür denetiminde string olmadığı için baştan reddedilir.

Yakın akrabalar: XPath ve LDAP

Aynı kök neden başka yorumlayıcılarda da yaşar. XPath enjeksiyonu (CWE-643), kullanıcı girdisinin bir XML belgesini sorgulayan XPath ifadesine karışmasıyla doğar; saldırgan koşulu her zaman doğru yapıp yetki denetimini atlar.

LDAP enjeksiyonu (CWE-90), girdinin bir dizin sorgu filtresine karışmasıyla oluşur; yıldız ve parantez gibi filtre karakterleri koşulu genişletir. İkisinin de çözümü tanıdıktır: girdiyi ifadeye karıştırmak yerine parametreli sorgu ya da güvenli API kullanmak, kabul edilen karakterleri izin listesiyle sınırlamak. Aynı ilke, arama ve filtre gibi kullanıcı girdisinin doğrudan bir sorgu nesnesine yazıldığı her uçta geçerlidir.

Altay Kargo'nun gönderi arama ucu, tarih aralığı parametresini gövdeden alıp doğrudan bir karşılaştırma sorgusuna geçiriyorsa, aynı tür doğrulaması orada da kurulmalıdır. Bu akrabalık, tek bir savunma alışkanlığının birçok açığı birden kapattığını gösterir. Hangi yorumlayıcıyla çalışırsan çalış, veriyi yapının dışında tutmayı, türü doğrulamayı ve izin listesini refleks hâline getirirsen, adını ilk kez duyduğun bir enjeksiyon ailesine karşı bile hazırlıklı olursun. Kök neden değişmediği için savunma da tek bir zihinsel modelde toplanır.

Etkinlik 6 / 7

Her açığı ait olduğu yorumlayıcıya göre ayır.

Önce bir seçeneğe, sonra aşağıdaki uygun kutuya bas. İstersen seçeneği kutuya sürükleyip bırak. Yerini değiştirmek için aynı işlemi tekrarla.

0/4 seçenek yerleşti.

Yorumlayıcı ne olursa olsun savunma aynı yere iner: veriyi yapının dışında tut, türü doğrula, kabul edilen değerleri izin listesiyle sınırla. NoSQL girişindeki tür denetimi bu ilkenin en ucuz örneğidir; tek bir satır, operatör taşıyan bir nesneyi baştan eler. Aynı satır, gövdeden gelen bir dizi ya da nesneyi de reddeder; parola alanına yalnız düz metin geçer. Aşağıdaki boşluğu bu ilkeyle doldur.

Etkinlik 7 / 7

Giriş ucunda parola alanı yalnız metin türünü kabul edip nesne veya diziyi reddederse, sorgu operatörü taşıyan bir nesne artık ____ alanına karışamaz.

Derinleş:SSTI'ı bir araştırma nasıl adlandırdı

Sunucu tarafı şablon enjeksiyonu, uzun süre XSS'in bir alt türü sanıldı. 5 Ağustos 2015'te güvenlik araştırmacısı James Kettle, Black Hat USA konferansında sunduğu çalışmayla bu açığı ayrı bir sınıf olarak tanımladı ve SSTI adını yerleştirdi.

Kettle'ın katkısı yalnız isim değildi: motordan bağımsız, tek istekte birçok şablon motorunu ayırt eden ilk test yüklerini de sistemleştirdi. Aynı aritmetik ifadenin Twig'de 49, Jinja2'de 7777777 vermesi gibi farkların bir tespit yöntemine dönüşmesi bu çalışmayla yaygınlaştı. Bugün OWASP Web Security Testing Guide, SSTI testini ayrı bir başlık altında ele alır. Bir açığın doğru adlandırılması, savunmanın da doğru araçla kurulmasını kolaylaştırır; adsız bir risk çoğu zaman gözden kaçar.

Bu derste bir hata mı gördün? Bildir