Siber Güvenlik
Server Side Template Injection (SSTI)
Günümüzde modern web uygulamaları kullanıcılar ile etkileşimini giderek arttırması sonucunda sürekli olarak gelişim göstererek dinamik ve kompleks yapılara büründüler. Bu dinamizm içerisinde de her kullanıcı için benzer türdeki verileri ve içerikleri ayrıştırarak sunma ihtiyacı gelişmiştir. Uygulamalar, kullanıcılara yönelik içerikleri sunmak için “şablonlar (templates)” kullanmaya başlamıştır. Bu...
Günümüzde modern web uygulamaları kullanıcılar ile etkileşimini giderek arttırması sonucunda sürekli olarak gelişim göstererek dinamik ve kompleks yapılara büründüler. Bu dinamizm içerisinde de her kullanıcı için benzer türdeki verileri ve içerikleri ayrıştırarak sunma ihtiyacı gelişmiştir. Uygulamalar, kullanıcılara yönelik içerikleri sunmak için “şablonlar (templates)” kullanmaya başlamıştır. Bu yazımızda ise, yine uygulamaların kullanıcıya daha iyi özellikler sunmasını sağlayan bir yapının kötüye kullanımı ile karşılaşabileceğimiz “Server Side Template Injection” zafiyetine değineceğiz.
Server Side Template Injection zafiyetini incelemeye başlamadan önce, uygulamalarda kullanılan MVC yaklaşımını, template yapısını ve bu yapının normal işleyişini incelemekle başlayalım.
MVC Yaklaşımı
Web uygulamalarının çağın gerekliliklerini karşılamak için kompleksleşmesinden bahsetmiştik. Kompleks uygulamalarda, backend ve frontend bileşenlerin iç içe geçmesi ve yazılan kodların okunurluğunun zorlaşması ile birlikte MVC gibi yazılım mimari yaklaşımları ortaya çıktı.
https://www.mshowto.org/mvc-yapisi-ve-ozellikleri.html
MVC yapısı ile temelde aşağıdaki maddelere çözüm sağlanması amaçlanmıştır.
- Yazılan kodların daha okunur olması,
- Backend ve frontend kısımların ayrıştırılması.
MVC yapısında katmanların genel prensipleri aşağıdaki gibi özetlenebilir;
Model: Veri erişimi, doğrulama ve iş mantığı
View: Kullanıcı arayüzü
Controller: İş akışının yönetimi
Template Engine
Kullanıcıların, dinamik web uygulamalarında gerçekleştirdikleri aktiviteler sonuçlarında oluşan verilerin uygun yerlere yerleştirilmesi için şablon motorları kullanılır. Başka bir deyişle, web sayfalarındaki şablonlar ile veri modelini birleştirir. Bu doğrultuda belirli şablonlarda dinamik kullanıcı girdileri işlenerek sayfalar üretilir.
Durumu bir görsel üzerinden inceleyelim. Yukarıda soldaki görselde aslında eski düzende iç içe geçmiş bir kod yapısı ile veri ve frontend kodların tek bir dosya üzerinde bir arada kullanıldığı görülüyor. Fakat sağdaki görselde daha modern teknolojilerin etkisi ile oluşturulan şablon yapısı ile veri modellerinin birleşimi görülüyor.
Peki, şablon motoru yapısını da anladığımıza göre yazımıza konu olan zafiyetimizi tanımaya başlayalım. Şablonlar ve kullanıcıya ait verilerin birleştirilmesi, bu verilerin işlenmesi durumu neye sebep olabilir? Yukarıda incelediğimiz örnekte şablonun yapısını oluşturan statik parça ve şablona yerleşecek verilerin bulunduğu yerleri temsil eden dinamik parçalar dikkatlerimizi çekmiş olmalı. Durumu şablon motorlarının çalışma prensibi üzerinden biraz daha yakından inceleyelim.
Şablon dosyaları veri nesneleri birlikte şablon motoru tarafından işlenerek çıktıyı oluşturuyor. Daha önce de bahsettiğimiz üzere burada statik veriler ve dinamik veriler söz konusu ve dinamik veriler çoğunlukla kullanıcıya ait verileri temsil ediyor. “Kullanıcı tarafından gelen dinamik veriler?”, “Server side?”, “Template Injection?” evet doğru! Bu zafiyetin sömürülmesiyle, kullanıcı tarafından sağlanan verilerin sunucu tarafında şablonlarda işlenmesi esnasında bir injection saldırısının gerçekleşmesi doğrultusunda uzaktan kod çalıştırmaya gidilebilir. Şablon motorları farklı yazılım dillerinde ve farklı syntaxlarda olabilir. Yazılım dillerine göre en çok kullanılan şablon motorları aşağıdaki gibidir:
- PHP – Smarty, Twig
- Java – Velocity, Freemaker
- Python – Jinja2, Mako, Tornado
- JavaScript – Jade, Rage
- Ruby – Liquid
Test Metodolojisi
Diğer tüm zafiyetlere de benzer şekilde SSTI zafiyetini test etmek için öncelikle zafiyet noktasını tespit etmek gereklidir. Burada tespit etme noktası özellikle XSS zafiyeti ile benzer şekilde işler ve hatta çoğu zaman zafiyetlerin birbiriyle karıştırıldığı da görülür. Zafiyete neden olan kısım tespit edildikten sonra, zafiyeti ve altyapıda kullanılan şablon motorunu tanımlamak gerekir. Şablon motorunun tanımlanmasıyla birlikte de hedefini tanıyan saldırgan için artık sömürme aşamasına geçme zamanı gelir.
Tespit Etme
Zafiyetin, kullanıcıya ait dinamik verilerin şablon içerisinde işlenmesi esnasında oluştuğundan bahsetmiştik. Bu durumda tüm girdi noktalarının potansiyel olarak şablonda kullanılma ihtimalini de göz önünde bulundurarak girdiler üzerinde manipülasyonlar yaparak zafiyeti tespit etmeye çalışabiliriz. Şablon motorlarında kullanılan belirli syntax yapıları olduğu için zafiyeti ararken de bu syntax yapılarından faydalanabiliriz. Kullanıcı girdisi olan kısımlarda aşağıda yer alan karakterleri kullanarak bir test gerçekleştirdiğimiz takdirde arka tarafta bu girdi bir şablon motorunda işleniyorsa girdide yer alan karakterler de işlenecektir.
${{<%[%'"}}%\
Örneğin şablon motorunda işlenen bir veri alanında “{{7*’7’}}” olarak sağlanan girdinin şablon içerisinde işlemin matematiksel sonucu olan “49” ya da aynı karakterin verilen sayı kadar tekrarı olan “7777777” gibi yorumlandığı görüldüğünde zafiyetin varlığından bahsedebiliriz.
Tespit Etme
Tabii ki bir pentester için en iyi dost olan hata mesajlarını bu zafiyeti tanımlarken unutmamak lazım. Eğer uygulamada herhangi bir şekilde hata mesajlarına sebep olacak bir girdi sağladığınızda uygulama sunucusundan açık ve detaylı bir şekilde hata mesajı alıyorsanız bu da zafiyeti tanımlama aşaması için güzel tekniklerden birisi.
Hata mesajı alamıyorsak uygulamanın hangi şablon motorunu kullandığını öğrenemez miyiz? Tabii ki öğrenebiliriz, bunun için de spesifik çıktılara odaklanmamız gerekiyor. Şablon motorlarının hepsi temelde aynı prensiple çalışsa da çalışma biçimleri ve veriyi yorumlama biçimlerinde kendine has yaklaşımlar sergileyebiliyor. Bu yaklaşımlardan kaynaklanan farklılıklar da uygulamada kullanılan şablon motorunu tanımlamada ipucu verecektir.
Sömürme
Uygulamada kullanılan şablon motoru tespit edildikten sonra saldırgan olarak nihai hedefimize yani sömürme adımına odaklanabiliriz. Şablon motorları genellikle uygulama sunucusu üzerinde sisteme erişim sağlama yeteneğine sahiptirler. Bu durumda, saldırgan SSTI zafiyetini kullanabildiği altyapılarda uzaktan komut çalıştırma yoluyla hedef sistemi ele geçirebilir. Burada önemli olan noktalardan birisi, şablon motorunu tanımladıktan sonra ilgili altyapıya ait dokümantasyonları okumakla başlar. Örneğin karşımızda ERB şablon motorunu kullanan bir altyapı olduğunda öncelikle ERB’ye ait dokümantasyon okunarak hedef altyapı hakkında bilgi edinilmesi gerekir. Böylece şablon motorunun özelliklerini saldırgan amaçlarla nasıl kullanabileceğimiz hakkında fikir ediniriz. “Şablon motorları hedef sistem üzerinde nasıl komut çalıştırabilir?” sorusunu hep birlikte inceleyelim. Aşağıda örnek olarak bir python2 ve python3 için SSTI RCE payloadları yer alıyor.
Python2
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() }}
Python3
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/etc/passwd').read() }}
Her iki payload da python olmasına rağmen birisinde “__mro__[2]” diğerinde ise “__mro__[1]” olarak farklılıklar görüyoruz. Bunun sebebini ve konunun aslında kök nedenini inceleyelim.
Nesne tabanlı programlamada her şeyin bir nesne olduğunu biliyoruz ve her yeni nesne aslında ana çatı olarak “Object” görürüz. Aslında python paylodalarını da incelediğimizde ''. gibi bir ibare görüyoruz. Tırnaklar içerisindeki veri aslında bir “string” ve “.__class__” ile birlikte biz bu verinin sınıfına erişmeye çalışıyoruz.
Peki MRO nedir? MRO (Method Resolution Order) kalıtımda (inheritance) kullanılan bir kavramdır. Bir metodun bir sınıf hiyerarşisinde aranma sırasıdır. Python'da MRO aşağıdan yukarıya ve soldan sağa doğru gider. Aşağıdaki görselde görüldüğü gibi tüm alt sınıflar aslında nesneden türetilir.
Terminal örneğimize geri dönelim. String bir verinin sınıfı üzerinden MRO hiyerarşisini incelediğimizde Python2 ve Python3 için MRO hiyerarşisinin farklı olduğunu görüyoruz. Yukarıda bahsedildiği gibi aslında payloadın farklılaşmasının nedeni python sürümleri arasındaki mimari farktan kaynaklanmaktadır.
Python3 Python2
Oluşturduğumuz payloadı incelemeye devam ettiğimizde MRO üzerinden nesneye eriştikten sonra nesneye bağlı alt sınıfları listeleyerek saldırgan bakış açısıyla ne yapılacağı hakkında fikir toplayabiliriz.
Bu alt sınıflar içerisinde “file” dikkatimizi çekiyor. Alt sınıflar arasında 40. indiste yer alan file sınıfını kullanarak sunucu üzerindeki dosyalara erişim sağlayabiliriz. O halde devam edelim.
File sınıfına ait “read” metodunu kullanarak hedef uygulamanın çalışmış olduğu sistem üzerinden başarılı bir şekilde dosya okunabildiği görülmektedir. Saldırgan olarak bu yapıda farklı sınıflar üzerindeki metodlar aracılığıyla hedef sistem üzerinde çeşitli aktiviteler gerçekleştirilebilir.
Önlemler
- Kullanıcılara şablon düzenleme ve şablon oluşturma yetkisi verilmemelidir.
- Sandbox ortamları kullanılmalıdır.
- Zararlı modüller ve fonksiyonlar kullanımdan kaldırılmalıdır.
- Kullanılan şablon motorunun güncel sürümü kullanılmalıdır.
- Girdi denetimi ve temizleme işlemi yapılmalıdır.
Referanslar
https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection
https://portswigger.net/web-security/server-side-template-injection
detaylı bilgiler için teşekkür ederim