freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

从0到1:Flask SSTI(服务器模板注入)
2021-07-06 13:40:00

前言

今天碰到一道需要SSTI注入的题,百度了一下,发现大多数关于SSTI的基础文章上下文的连贯性并不是很好,于是便有了此文。

在阅读此文前,我假设您已具有Python和Html基础.

什么是SSTI(服务器模板注入)

SSTI 即服务端模板注入攻击,服务端接受用户输入,将其作为 Web 应用模板的一部分渲染编译后执行了恶意内容,导致敏感信息泄露,代码执行等.(与Xss原理大致相似)

什么是Flask

Flask是一个用Python编写的Web应用程序框架.它由Armin Ronacher 开发,他领导一个名为Pocco的国际Python爱好者团队.Flask基于Werkzeug WSGI工具包和Jinja2模板引擎.两者都是Pocco项目.

一个Flask应用是如何跑起来的

运行虚拟环境:

Flask是运行在虚拟环境(VirtualEnv)中的,VirtualEnv是一个虚拟的Python环境构建器。它可以帮助用户并行创建多个Python环境.因此,它可以避免不同版本的库之间的兼容性问题.(最好不要使用python3.8运行,以减少不兼容的可能性)

pip install virtualenv
virtualenv venv
cd ./venv/Scripts
activate.bat

如何跑起来:

先来看一个简单的小demo

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
   return 'Hello World'

if __name__ == '__main__':
   app.run()

首先是导入flask框架,这是必须的.

Flask构造函数使用当前模块(__name __)的名称作为参数.

Flask类的route()函数是一个装饰器,它告诉应用程序哪个URL应该调用相关的函数.

app.route(rule, options)

rule参数代表该路径与哪个函数进行绑定.如上图中的小demo,@app.route('/')是函数hello_world的装饰器,即代表网站的根路径与函数hello_world绑定,在访问http://127.0.0.1/的时候,就会触发函数hello_world.

最后,Flask类的run()方法在本地开发服务器的虚拟环境上运行应用程序

Flask模板

Flask模板的基本使用

在项目下创建 templates 文件夹,用于存放所有模板文件,并在目录下创建一个模板文件 html 文件 hello.html

在env这个虚拟环境中,render_template()这个函数会自动寻找在同级目录下templates文件夹下的文件.

也就是说render_template('hello.html')等价于render_template('./templates/hello.html')

模板变量(重点)

传入字符串到html模板中

在html模板中使用{{变量名}}的方法输出变量内容

在html模板中,{{}}之间的内容会被当做表达式进行执行,并将结果输出到页面上

一个存在SSTI漏洞的demo

在ctf中基于python开发的web,存在SSTI漏洞的一般都使用的是Jianja2模板引擎

什么是Jianja2引擎

Jinja2是Python下一个被广泛应用的模版引擎,他的设计思想来源于Django的模板引擎,并扩展了其语法和一系列强大的功能。.中最显著的一个是增加了沙箱执行功能和可选的自动转义功能.

注意这一句话,可选的自动转义功能,即默认不进行转义.这为我们进行SSTI注入创造了条件.

test.py

from flask import Flask, request
from jinja2 import Template
app = Flask(__name__)

@app.route('/')
def index():
    id = request.args.get('id')
    t = Template('''
    <html>
      <head>
        <title></title>
      </head>
     <body>
          <h1>%s</h1>
      </body>
    </html>
        ''' % (id))
    return t.render()
app.run()

表达式果然被执行了.

一个不存在SSTI漏洞的demo

test.py

from flask import Flask, request,render_template
app = Flask(__name__)

@app.route('/')
def index():
    id = request.args.get('id')
    return render_template('hello.html',id=id)
app.run()

使用request对象,处理来自客户端url中id的数据.

request.args.get() 解析查询字符串的内容,它是问号(?)之后的URL的一部分。

html模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{{id}}
</body>
</html>

flask中的render_plate函数会对变量内容进行转义,即输出原始字符串.所以不存在SSTI漏洞.

如何对存有SSTI漏洞的flask进行命令执行

这里一道简单的ctf练习题为例.你可能会疑惑,没有导入OS模块,也没有system()类似的函数怎么能进行命令执行呢?

命令执行的基本流程:获取基本类->获取基本类的子类->在子类中找到关于命令执行和文件读写的模块

获取基本类

我们都知道在python中一切皆为对象.

使用__bases__获取基本类

Python 为所有类都提供了一个 bases属性,通过该属性可以查看该类的所有直接父类,该属性返回所有直接父类组成的元组,注意是直接父类.

''.__class__.__bases__

可以看到str的父类是basestring

使用__mro__同样也可以查找父类,不同的是__mro__获取的是类的调用顺序

调用顺序依次是object->basestring->str

获取基本类的子类

''.__class__.__mro__[2].__subclasses__()

在子类中找到关于命令执行和文件读写的模块

在上图中我们要找到'os' 所在的'site._Printer'类,它的索引为[71]

使用__subclasses__()[71].__init__.__globals__['os'].popen('cmd').read(),运行命令并得到回显结果

总结

SSTI模板注入的漏洞好像并不太常见,以后遇到再更新吧。

# web安全
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录