SSTI——flask模板注入漏洞入门

766 词

最近一直在和python打交道,就想顺便学点Flask漏洞,也算打打python后端框架基础吧。

Flask搭建后端服务

众所周知python也可用用来写后端,最简单的框架就是Flask,例如:

1
2
3
4
5
6
7
8
9
10
from flask import Flask
app = Flask(__name__)

@app.route('/')
# route装饰器将url与下面的函数链接起来
def hello_world():
return 'helloworld!'
if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000,debug=True)
# host为0.0.0.0则为暴露在公网,可从任意IP访问

上面的python文件运行时会在5000端口跑web服务,访问https://IP:5000/ 返回hello world!的字符串。

上面的例子为返回简单的字符串,也可用模板渲染html源码在浏览器显示:

1
2
3
4
5
6
7
8
9
from flask import Flask,render_template,render_template_string
app = Flask(__name__)
@app.route('/')

def hello_world():
html = '<h1>hello world!</h1>'
return render_template_string(html)
if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000,debug=True)

在浏览器中访问显示如下:

img

即html字符串中的标签被模板渲染并显示在浏览器中。

Flask模板注入漏洞原理

而当字符串中的具体参数可控时,就可能存在模板注入漏洞:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask,request,url_for,redirect,render_template,render_template_string
app = Flask(__name__)
@app.route('/',methods=['GET'])

def code():
code = request.args.get('id')
# 获取get传参的参数
html = '''
<h2>%s</h2>
'''%(code)
# 三引号作用与引号没区别,常用于大段字符串
return render_template_string(html)
if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000,debug=True)

而构造js代码就可能造成XSS攻击

img

此时传参的code直接插入字符串中,并被render_template_string渲染而触发SSTI。

只要换种写法就能避免:

1
2
3
4
5
6
7
8
9
10
from flask import Flask,request,url_for,redirect,render_template,render_template_string
app = Flask(__name__)
@app.route('/',methods=['GET'])

def code():
code = request.args.get('id')
return render_template_string('<h2>{{code}}</h2>',code=code)
# {{}}为Jinja2模板引擎中的变量包裹标识符
if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000,debug=True)

img
这是因为模板引擎一般都默认对渲染的变量值进行编码转义,因此用户控制的不是模板而是变量,并不会得到渲染产生SSTI。

基于SSTI的攻击

jinja2模板引擎会对{{}}内的内容进行渲染,也可执行一些表达式:
img
或查看全局常量:
img
可见表达式和命令都被执行了,由此可构造文件包含漏洞

与反序列化类似,同样需要利用魔术方法构造漏洞链,常用魔术方法:
- class 返回类型所属的对象
- mro 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
- base 返回该对象所继承的基类
- __base__和__mro__都是用来寻找基类的
- subclasses 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
- init 类的初始化方法
- globals 对包含函数全局变量的字典的引用

思路一般为先用{{''.__**class__**}}(注:此处不是双引号而是两个单引号)找类型所属对象,然后{{''.__**class__**.__**mro__**}}查找所有基类
img
然后即可查找所有可用的类

留言