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/

原文发布于微信公众号 - FreeBuf(freebuf)

原文发表时间:2017-06-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

Go语言Goroutine与Channel内存模型

Go语言内存模型规定了在一个goroutine中一个变量的读取的情况下,确保能够观察到在其他另外goroutine中写入同样变量的值。也就是说,如果在多个gor...

362100
来自专栏LEo的网络日志

python技巧分享(五)

292100
来自专栏Java工程师日常干货

【随笔】JVM核心:JVM运行和类加载

本篇博客将写一点关于JVM的东西,涉及JVM运行时数据区、类加载的过程、类加载器、ClassLoader、双亲委派机制、自定义类加载器等,这些都是博主自己的一点...

8030
来自专栏九彩拼盘的叨叨叨

Node.js 版本管理器: nvm 介绍

有时候,我们需要测试写的 Nodejs 的程序在不同 Nodejs 版本下是否能正常运行;或是我们想要尝试下最新版 Nodejs 的新特性,但常用的代码需要旧版...

9910
来自专栏从零开始学自动化测试

pytest文档19-doctest测试框架

doctest从字面意思上看,那就是文档测试。doctest是python里面自带的一个模块,它实际上是单元测试的一种。 官方解释:doctest 模块会搜索那...

16220
来自专栏C/C++基础

分离编译模式简介

示例代码编译运行环境:Windows 64bits+VS2017+Debug+Win32。

10040
来自专栏未闻Code

Tenacity——Exception Retry 从此无比简单

Python 装饰器装饰类中的方法这篇文章,使用了装饰器来捕获代码异常。这种方式可以让代码变得更加简洁和Pythonic。

18110
来自专栏Java帮帮-微信公众号-技术文章全总结

day27.MongoDB【Python教程】

集合:类似于关系数据库中的表,储存多个文档,结构不固定,如可以存储如下文档在一个集合中

12530
来自专栏Python

Flask路由报错:raise FormDataRoutingRedirect(request)

  raise FormDataRoutingRedirect(request) FormDataRoutingRedirect: A request was ...

45640
来自专栏java学习

Java入门基础题目练习

《Java基础入门》课后习题 第1章 Java开发入门 一、填空题 1、Java的三大体系分别是______、_____、______。 2、Java...

44940

扫码关注云+社区

领取腾讯云代金券