Server Side Template Injection Zafiyeti

Omer Citak - 27 Şubat 2017 -

Web uygulamarı geliştirmek artık eskisi kadar kolay değil. Artık projelerimizi modern mimariler ile tasarlamamız gerekiyor. Bu modern mimarinin birçoğunda olan View katmanında bize yardımcı olması için template engineler kullanırız. Template engineler birçok kolaylığın yanında bir takım güvenlik zafiyetlerini de yanında getiriyor. Bu yazıda bu zafiyetlerden bahsettik.

Server Side Template Injection Zafiyeti

MVC Mimarisi

MVC mimarisinden önce, spagetti kod yazıldığı vakit hem backend hem frontend kodları tek bir dosya içerisinde karma olarak yazılırdı. O zamanlar web sadece “web sitesi” olarak kullanıldığından kodlama stili normaldi. Ancak günümüzde web kavramı sadece “web siteleri” için değil, “web uygulamaları” için de kullanılıyor.

Web uygulamaları ile birlikte hayatımıza MVC gibi yazılım mimarileri de girdi. Bu tarz mimarilerin ortak özelliklerinden bir tanesi de gerek kod okunulurluğu, gerek web servisi ve diğer web uygulamaları tarafından rahat kullanılabilmesi için backend ve frontend kodlarının ayrı katmanlarda yazılmasıdır.

MVC mimarisinin şemasına bakalım;

MVC Nedir?

  • Kullanıcı web uygulamasına tanımlı route’lar sayesinde Controller katmanına ne istediğini söylüyor.
  • Controller katmanı; eğer kullanıcının istekleri arasında herhangi bir veri işlemi var ise gerekli işlemleri Model katmanına yaptırıp cevabını alıyor.
  • Aldığı cevabı View katmanına iletiyor.
  • View katmanı ise kullanıcının görmesi gereken cevabı hazırlayıp kullanıcıya iletiyor.

Somut bir örnek verecek olursak;

  • Kullanıcı http://testsparker.com/blog route’una bir istekte bulunuyor.
  • Bu isteği Controller katmanı karşılıyor.
  • İstenilen şey sistemdeki blog yazıları olduğundan ve bu yazılar örneğin bir veri tabanında tutulduğundan Controller katmanı Model katmanına yazıları toparlayıp kendisine vermesini istiyor.
  • Controller katmanı gelen yazı yığınını (model object) düzenleyip, View katmanının zorlamadan işleyebileceği hale (data array) sokuyor ve kullanıcıya iletmesi için View katmanına veriyor.
  • View katmanı gelen yazı dizisini bir döngü içerisinde HTML template’indeki uygun yerlere basıyor.

Template Engine

Template engine’ler, modern web ile MVC mimarisinin “View” katmanında yer alıyor. Controller katmanından gelen veriyi işleyip HTML formatında kullanıcıya iletiyor.

PHP dili ile yazılmış bazı template engine’ler aşağıdaki gibi;

  • Twig
  • Smarty
  • Blade
  • Volt
  • Mustache

Twig Engine

Twig, PHP projelerinde kullanılabilecek popüler bir template engine'dir. Aşağıdaki Composer dosyası ile projenize dahil edebilirsiniz.

{
    "require": {
        "twig/twig": "1.*"
    }
}

Twig’i projeye dahil ettikten sonra aşağıdaki gibi kullanabilirsiniz.

<?php
include 'vendor/twig/twig/lib/Twig/Autoloader.php';

$name = $_GET['name'];

Twig_Autoloader::register();
$loader = new Twig_Loader_String();
$twig = new Twig_Environment($loader);
$result = $twig->render($name);

echo $result;

Yukarıdaki gibi bir kod ile sayfaya GET metotunun name parametresine gönderilen değer Twig tarafından render edilip sayfaya basılacaktır.

Server Side Template Injection Zafiyeti-2

Name parametresinden gönderdiğimiz test değeri olduğu gibi sayfaya basıldı.

Twig’de değişken render etmek için süslü parantez kullanılır. Süslü parantezler arasında değişken adı kullanıldığında o değişkenin render edilmesi beklenir. Bu yüzden name parametresine süslü parantezler arasında bir değer gönderirsek onun render edilmesi beklenir. Süslü parantezler arasında gönderilecek değer illa değişken olmak zorunda değil. Twig engine içerisindeki bir metotun da tetiklenmesini sağlayabiliriz.

Örneğin aşağıdaki görselde süslü parantezler arasında 5 * 2 işlemi yapıldığında işlem gerçekleştirilip sonrasında sayfaya basılıyor.

Server Side Template Injection Zafiyeti-3

Burada bir parantez açıp Twig’in kendi içerisindeki metotlarına göz atmamız gerekiyor. Twig’deki bilmemiz gereken başlıca metolar şunlardır;

  • getFilter(“filter”); Bu metot kendisine parametre olarak verilen Twig filtresini getirir.
  • registerUndefinedFilterCallback(“function_name”); Bu metot eğer getFilter metotu çağrılmış ise getFilter’in callback’i olarak kendisine parametre olarak verilen değer ismindeki global bir fonksiyonu çağırır. Bu metotu PHP’deki call_user_func metotu gibi düşünebiliriz.
  • setCache(“ftp://testsparker.com:21”); Bu metot kendisine parametre olarak verilen değerdeki şablonu önbelleğe alır.
  • loadTemplate(“backdoor”); Bu metot kendisine parametre olarak verilen değer ismindeki şablonu yükler.

Twig içerisinde kullanabileceğimiz başlıca metotlar yukarıdaki gibidir. Bu metotlar Twig’in kendi metotları olduğundan _self.env prefix’i ile çağırmamız gerekiyor.

Örnek olarak çağrılması gereken filter’ın callback’ine shell_exec, filter’a ise dir değerini gönderirsek arka planda sistem shell_exec fonksiyonunu çağırıp dir parametresini gönderecektir.

Yani aşağıdaki gibi bir payload’ı name parametresine gönderirsek sonuç aşağıdaki görseldeki gibi olacaktır.

GET /ssti/ssti.php?name={{_self.env.registerUndefinedFilterCallback("shell_exec")}}{{_self.env.getFilter("dir")}} HTTP/1.1
Host: localhost

Server Side Template Injection Zafiyeti-4

Bu şekilde Twig Engine üzerinden sistemde istediğimiz komutu çalıştırabiliyoruz.

Saldırılardan Korunmak

Zafiyet barındıran kod aşağıdaki gibidir.

<?php
include 'vendor/twig/twig/lib/Twig/Autoloader.php';

$name = $_GET['name'];

Twig_Autoloader::register();
$loader = new Twig_Loader_String();
$twig = new Twig_Environment($loader);
$result = $twig->render($name);

echo $result;

Bu kodda SSTI zafiyetine sebep olan şey, kullanıcıdan alınan değerin doğrudan render edilmesidir. SSTI zafiyetine maruz kalmamak için kullanıcıdan alınan değer değil, önceden hazırlanmış HTML şablonu render edilmelidir. Yukarıdaki zafiyetli kodunu düzeltecek olursak yapmamız gereken şey şöyle olacaktır;

  1. test.twig adında bir HTML şablon dosyası oluşturup
  2. ssti.php dosyasında doğrudan test.twig şablonunu render etmeliyiz.

Test.twig dosyasının içeriği;

{{ name }}

ssti.php dosyasının içeriği;

<?php
include 'vendor/twig/twig/lib/Twig/Autoloader.php';

$name = $_GET['name'];

Twig_Autoloader::register();
$loader = new Twig_Loader_String();
$twig = new Twig_Environment($loader);
$result = $twig->render(‘test.twig’, [
‘name’ => $name,
]);

echo $result;

Zafiyet barındıran kodumuzu yukarıdaki gibi düzenledikten sonra saldırganlara karşı önlemimizi almış oluyoruz.