Flask Jinja2开发中遇到的的服务端注入问题研究
Posted FreeBuf
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flask Jinja2开发中遇到的的服务端注入问题研究相关的知识,希望对你有一定的参考价值。
0×00. 前言
作为一个安全工程师,我们有义务去了解漏洞产生的影响,这样才能更好地帮助我们去评估风险值。本篇文章我们将继续研究Flask/Jinja2 开发中遇到的SSTI (服务端模板注入)问题, 如果你从未听过SSTI 或者没有弄清楚它到底是个什么东东,建议您最好先阅读一下这篇文章<点击阅读原文查看链接>。
0×01. 测试代码
为了更好地演示Flask/Jinja2 开发中的SSTI问题,我们搭建一个小的POC程序(基于Flask 框架),主要由两个python脚本组成:
Flask-test.py
一些开发者可能认为为一个简单的404错误页面去单独创建一个模板文件是多余的,他们更喜欢在404 视图函数中用模板字符串(正如上述测试代码中的page_not_found函数中的template字符串)代替单独的404模板文件; 一些开发者还会在返回的错误页面中提示用户是哪一个URL导致了404错误,但他们不把错误的URL传递给render_template_string模板上下文,而是喜欢用%s动态地将问题URL传递给模板字符串,这些看起来都很OK。但实际上真的是这样的吗,让我们接着往下看
0×02. render_template_string 函数中默认上下文对象引起的SSTI问题
我们开始测试,404函数功能没得问题,确实显示了错误信息,并指出哪一个URL导致了此问题
到这里,很多人可能都想到了这个404函数存在的问题,对,就是XSS,是的,的确存在XSS漏洞,这也属于SSTI,但这篇文章不想讨论这一点, 如果你再深入思考一下,可能会发现这里存在代码注入,比如当我们的URL是下面这样,URL中包含了Jinjia2语法表达式:
http://10.1.100.3:5000/{{8+8}}
我们发现模板引擎执行了8+8,并返回了结果,这是一个简单的SSTI问题,我们再来看看其他有趣的SSTI 问题, 我们来看看
render_template_string 函数的定义:
def render_template_string(source, **context):"""Renders a template from the given template source string
with the given context.
:param source: the sourcecode of the template to be
rendered
:param context: the variables that should be available in the
context of the template.
"""ctx = _app_ctx_stack.top
ctx.app.update_template_context(context)return _render(ctx.app.jinja_env.from_string(source),
context, ctx.app)
这里有两个参数,一个是source,很简单就是需要渲染的模板字符串, 另一个是个包裹关键字传递参数(字典), 表示的是模板上下文,这里我们
就简单说一下这个Flask/Jinjia2开发中的模板上下文。主要有三个上下文,
一个是Jinjia globals<点击阅读原文查看链接> (http://jinja.pocoo.org/docs/dev/templates/#builtin-globals)
一个是Flask Template globals<点击阅读原文查看链接> (http://flask.pocoo.org/docs/0.10/templating/#standard-context)
最后一个是开发者自己显示添加的(这里暂且不表),前两个都是默认内置的(也就是上述代码中context参数默认值),这两个是我们要讨论的
render\_template\_string 默认上下文对象之request对象
经过一番调查,我们发现Flask Template globals 中有一个request对象,它表示当前请求对象(flask.request),它和你在视图代码中用到的
request对象(就是flask.request)一样,包含了所有的请求上下文信息,request 对象中包含一个environ对象,environ是一个字典,包含了所有的服务端环境变量信息,这个environ中有一个key:werkzeug.server.shutdown,这个key指向一个方法,这个方法是shutdown_server,就是用来关闭服务的,很显然这可以用来进行DOS攻击,
比如执行下面的URL,就可以关闭应用程序服务:<点击阅读原文查看链接>
http://10.1.100.3:5000/{{request.environ['werkzeug.server.shutdown']()}}
幸亏生产环境用的是gunicorn这样的WSGI 服务器而不是werkzeug,这样才不会造成更大影响
render\_template\_string 默认上下文对象之config对象
这config对象也是一个Flask template global,它代表中当前配置文件对象,也就是flask.config 中Config对象实例(源代码flask/config.py)
它是一个类似字典的对象,包含了应用程序所有的配置文件信息(你所有的用app.config.xxx | app.config['xxx'] 配置信息 都在config这个上下文对象中),在很多的例子中,这个condfig对象包含了很多敏感的信息,比如数据库连接信息,连接第三方服务的SECRET_KEY等
执行下面的URL,就可以获取当前应用程序所有的配置信息
<点击阅读原文查看链接>
http://10.1.100.3:5000/{{config.items()}}
可以看到我们确实获取到了很多敏感的信息,包括数据库连接的密码, 所以不要认为把敏感信息保存在环境变量中就是安全的。
0×03. 反思render_template_string 默认上下文对象之config对象
0×02节已经提到过,render_template_string 默认上下文对象之config对象是flask.config中config对象的实例,它是一个类似字典的对象,config上下文对象有一些非常有意思的成员方法,比如:from_envvar , from_object ,from_pyfile , 当然还有一个成员属性root_path
这里我们先研究其成员方法from_object, 源代码如下:
这段代码意思就是,如果传入from_object方法的参数obj是str|unicode类型,那么就载入obj所代表的的模块,然后将参数obj所代表的模块中所有的大写属性加入到当前config对象实例中
我们来测试一下<点击阅读原文查看链接>
http://10.1.100.3:5000/{{config.from_object(‘os’)}}
这个url就是调用Flash Template Globals 中 config上下文对象中的from_object方法载入os模块,并将os模块中的大写属性加入config对象实例中
我们先来看一下os模块中有哪些大写属性值, 下面是一段测试代码,用来输出OS模块中所有的大写属性及其值
import os
for key in dir(os):
if key.isupper():
print key, ' ', getattr(os, key)
我们执行一下下面这个URL,看看是否真的导入了指定模块(这里指的是os一些大写属性,当然也包括一些os模块内置方法)中大写属性到当前上下文对象config中<点击阅读原文查看链接>http://10.1.100.3:5000/{{config.items()}}
恩,确实导入了OS模块中所有大写属性,其实这里给我们提供了一个思路,我们可以通过SSTI漏洞执行config上下文对象中from_object这类的方法导入一些模块,然后寻找可以callabled属性,以此来绕过Jinjia2模板沙盒。
0×04. 总结
本篇文章是我对Flask/Jinjia2 开发中遇到的一些问题的初步研究,config这个上下文对象其实还有一些其他的利用方式,危害也更大些, 我会在下篇文章中涉及
Origin Refer: https://nvisium.com/blog/2016/03/09/exploring-ssti-in-flask-jinja2/ <点击阅读原文查看链接>
*本文作者ForrestX386,转载请注明来自FreeBuf.COM
以上是关于Flask Jinja2开发中遇到的的服务端注入问题研究的主要内容,如果未能解决你的问题,请参考以下文章
漏洞分析Flask框架(jinja2)&服务端模板注入漏洞(SSTI)
XCTF-攻防世界CTF平台-Web类——12Web_python_template_injection(SSTI服务器模板注入Flask框架之Jinja2模板渲染引擎)