03-10-2018 / Od 0 do pentestera

SSTI - Server-Side Template Injections

W tym odcinku #od0dopentestera o silnikach szablonów na przykładzie języka Python i frameworka Flask1 w którym to użyjemy Jinja22.

Dawno temu kawałki kodu odpowiedzialnego za logikę aplikacji oraz treść wyświetlaną użytkownikowi trzymano w jednym pliku.

Jak można się domyślić taki "misz masz"nie dość że jest trudny to opanowania to jeszcze powodował wiele błędów w aplikacjach.

Dlatego też stworzono silniki szablonów.

Dzięki nim logika strony tworzona jest w jednym pliku a wszystko to co ma zostać wyświetlone w drugim.

Początkowo silniki te były proste i w większości przypadku działały jako zwykłe "znajdź/zamień" znane z edytorów tekstu.

Z czasem jednak ich możliwości drastycznie się zwiększyły. Co widać na podstawie dokumentacji Jinja2, który to jest popularnym silnikiem używanym w aplikacjach stworzonych w języku Python.

Obecnie można więc powiedzieć że silniki te same w sobie są językami programowania.

I zazwyczaj można w nich wykonać sporo potencjalnie niebezpiecznych czynności takich jak oczytanie dowolnego pliku lub też wykonanie dowolnego kodu.

Co się zatem stanie kiedy programista zapomni z jak potężnymi narzędziami ma do czynienia i zacznie używać ich w zły sposób?

Popatrzymy na przykładowy kod aplikacji we Flasku.

imie = request.values.get('imie')
return Jinja2.from_string('Hej ' + imie).render()

Pobieramy tutaj parametr imie od użytkownika a następnie wyświetlamy go używając funkcji from_string().

Gdzie zatem tkwi dzisiejszy błąd?

Jeśli przyjrzymy się bliżej dokumentacji funkcji from_string możemy wyczytaj, że parametr source, który ona przyjmuje to treść szablonu, który jest następnie parsowany i zwracany jako obiekt Template.

Jeśli zatem jest to treść szablonu - ciąg tych znaków może zawierać dowolne funkcję, które ten silnik szablonów obsługuje.

A ponieważ ciąg ten jest kontrolowany przez użytkownika - przy pomocy parametru imie - użytkownik może wykonać dowolny kod, na jaki pozwala silnik szablonów.

W standardowej sytuacji, nie wiedzielibyśmy że aplikacja ta korzysta z Jinja2.

Najpierw należało by przystąpić do zidentyfikowania silnika z którym mamy do czynienia.

Możemy użyć do tego prostej infografiki stworzonej przez Twórców narzędzia Burp3, które w paru krokach pozwoli nam na sprawdzenie z jakim narzędziem mamy do czynienia.

https://portswigger.net/blog/server-side-template-injection

My wiemy już jaki to silnik dlatego użyjemy prawidłowego payloadu:

{{7*'7'}}

Zwrócony został wynik mnożenia. Wiemy zatem że użyta przez nas komenda zadziałała.

Co zatem możemy teraz zrobić?

Oczywiście moglibyśmy przejrzeć dokumentację w poszukiwaniu interesujących funkcjonalności.

Warto jednak sprawdzić, czy ktoś nie zrobił tego przed nami.

Na Githubie możemy znaleźć listę przykładowych zapytań.

Ja spróbuje tutaj odczytać plik sekret. Musze zatem zmienić ścieżkę do pliku.

{{ ''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() }}

Można też użyć narzędzi, które za nas przetestują z jakim silnikiem mamy do czynienia4.

Po jego użyciu widzimy że prawidłowo rozpoznał silnik szablonów, system a także wypisał co można przy jego pomocy wykonać.

Jak zatem ochronić się przed tym błędem?

Używać silników zgodnie z przeznaczeniem. Zatem pamiętać że użytkownik nigdy nie może podawać treści szablonu.

Może jedynie podawać treść parametru, który zostanie przez ten szablon wyświetlona.