SSTI (Server Side Template Injection) je ranjivost do koje dolazi kada napadač manipulira procesom generiranja odgovora poslužitelja iz predloška web aplikacije. Time napadač u određenim slučajevima može izvršavati proizvoljan kôd na poslužitelju (remote code execution, RCE), kao i mijenjati generirani odgovor koji poslužitelj vraća.
Generatori predložaka (engl. template engines) su dizajnirani za generiranje web stranica kombiniranjem fiksnih predložaka s promjenjivim podacima. Do SSTI-a dolazi kada korisnički unos nije proslijeđen kao podatak, već je konkateniran direktno u predložak u nepročišćenom obliku, pri čemu se zadržava sintaksa koda za generiranje predloška. Ovo napadačima omogućuje izvršavanje proizvoljnog koda unutar procesa generiranja predloška, u istom programskom jeziku koji koristi generator predložaka. Napad može eskalirati do izvršavanja naredbi u ljusci operacijskog sustava poslužitelja.
Da bi se detektirala SSTI ranjivost na poslužitelju, treba uočiti razlike između odgovora na očekivane podatke i odgovora na unos koji ukazuje na mogućnost izvršavanja koda. Ako poslužitelj u nekom trenutku vrati grešku, može se otkriti da je ranjiv te ponekad i koji je točno generator predložaka korišten. Još jedan od pokazatelja ranjivosti je nereflektirani ili nepotpuno reflektirani podatak za koji se očekuje da će ga poslužitelj reflektirati.
Jedan od osnovnih pristupa otkrivanju ovakve ranjivosti je enumeracija i ispitivanje generatora predložaka (engl. template fuzzing) umetanjem nizova posebnih znakova često korištenih u predlošku, npr. ${{<%[%'"}}%\
Kako bi se ovakva ranjivost lakše razlikovala od obične XSS ranjivosti, preporučuje se umetanje jednostavnih matematičkih operacija. Potrebno je identificirati koji je generator predložaka korišten. To se ostvari isprobavanjem unosa sa specifičnom sintaksom za različite predloške dok neki ne proizvede rezultat koji ukazuje na uspješno izvršavanje koda. Iako je broj potencijalnih generatora predloška velik, mnogi koriste sličnu sintaksu dizajniranu da ne interferira s HTML sintaksom, primjerice:
{{7*7}}
${7*7}
<%= 7*7 %>
${{7*7}}
#{7*7}
*{7*7}
Jednom kad je generator predložaka poznat, potrebno je pročitati dokumentaciju generatora predložaka kako bi se otkrio najefikasniji pristup napadu. Ključna poglavlja uključuju osnovnu sintaksu, sigurnost, listu ugrađenih metoda, funkcija, filtera i varijabli te ekstenzija i plugina. Također je potrebno istražiti okolinu kako bi se otkrilo koji su sve dostupni resursi i objekti te koji su izloženi podaci i strukture.
Nakon što su prikupljene sve potrebne informacije o aplikaciji, moguće je napasti web aplikaciju ubacivanjem predloška za izradu objekata ili čitanje/pisanje u datotekama kako bi se otkrila ranjivost svake pojedine funkcije. Dan je jednostavni primjer ranjivog koda:
$output = $twig->render("Dear " . $_GET['name']);
U primjeru se dio predloška generira dinamički putem GET parametra name. S obzirom na to da se sintaksa evaluira na poslužitelju, napadač može postaviti maliciozan SSTI unos u taj parametar:
http://vulnerable-website.com/?name={{bad-stuff-here}}
PRIMJER: Labos pronađen na internetu
Otvorimo stranicu na adresi https://ssti.secure-cookie.io/
Cilj nam je s pomoću SSTI napada izmijeniti svoje ime, a možda i doći do nekih zgodnih podataka.
Prvo unosimo matematičke jednadžbe u polje za unos teksta kako bismo otkrili predložak, odnosno HTML dokument koji sadrži varijable. U polje unosimo
{{7*7}} i zatim gledamo kako će se interpretirati u predlošku.
Aplikacija izračunava brojeve dane jednadžbom i vraća ih kao naše ime. Budući da se naredba 7*7 izvršila, znamo da se koristi predložak s dvostrukim vitičastim zagradama pa možemo stranici poslati kompleksnije naredbe. Pokušajmo doći do informacija o sustavu na kojem se nalazi aplikacija.
Krećemo s nečim jednostavnim te zasad samo pokušajmo {{"foo"}} .
Vidimo da se string prepoznao jer smo dobili odgovor “Hello foo”. Sad pokušajmo saznati u kojem se jeziku mogu prihvatiti naredbe tako što ćemo zvati neke funkcije nad tim stringom.
Ako unesemo {{"foo".toUpperCase()}}, naredba se neće prepoznati i nećemo dobiti odgovor. Zaključujemo da aplikacija ne prihvaća naredbe pisane u Javi. No, ako pokušamo koristiti Python sintaksu i upišemo {{"foo".upper()}}, dobijemo rezultat FOO, što znači da možemo slati naredbe u Pythonu.
Ono što sad želimo je doći do Pythonovog 'os' modula jer nam on omogućuje naredbe vezane uz operacijski sustav. Šaljemo naredbu:
{{"foo".__class__.__base__.__subclasses__()[182]
.__init__.__globals__['sys'].modules['os'].popen("ls").read()}}
Raščlanimo ju na dijelove. .__class__ vraća klasu objekta nad kojim je funkcija pozvana (u ovom slučaju 'str'). .__base__ traži ime bazne klase (object). .__subclasses__ vraća listu svih potklasi od object. Trebamo klasu na indeksu 182 jer ona uključuje 'sys' modul, a od njega možemo doći do 'os' modula. Da bismo došli do modula, prvo moramo inicijalizirati objekt te klase (naredbe .__init__) i dohvatiti globalne varijable te funkcije (naredba .__globals__). Želimo samo 'sys' pa to navedemo u zagradi pored. 'sys' modul sadrži metodu modules pa ju pozovemo i navedemo da želimo 'os'. Sad možemo izvesti bilo koju funkciju iz tog modula te biramo ls koji pozovemo preko popen. Na kraju pozovemo read() da bismo mogli pročitati output.
Na ovaj smo način izveli naredbu operacijskog sustava na udaljenom poslužitelju. U ovom slučaju to je bila ls naredba, no mogla se iskoristiti i bilo koja druga.