Flask
flask是一个使用Python编写的轻量级Web应用框架
特点:良好的文档,丰富的插件,包含开发服务器和调试器,集成支持单元测试,RESTful请求调度,支持安全cooies,基于Unicode。
Python可直接使用flask启动一个Web服务页面。
Flask基本架构
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "helloworld"
if __name__=='__main__':
app.run()
成功运行,helloworld,可加上debug功能
(debug=True,host="0.0.0.0",port=??)//在创建时定位错误,改配置会自动重启,也可改端口,但是得手动重启(注意端口冲突问题)并且避免不安全端口存在
Flask变量及方法
Flask变量规则
通过向规则参数添加部分,可动态构建URL
格式化字符串
from flask import Flask
app = Flask(__name__)
@app.route('/hello/<name>')
def hello(name):
return "hello %s"%name
@app.route('/int/<int:postid>')
def id(postid):
return " %d " %postid
if __name__=='__main__':
app.run()
变量传参方法
HTTP方法
注:要放到同级目录下的templates文件夹下,这是flask内置定义好的,不然会找不到文件
from flask import Flask
app = Flask(__name__)
@app.route('/login',methods=['POST','GET'])
def login():
if request.methods == 'POST':
print(1)
user = request.form['ben']
return redirect(url_for('success',name = user))
else:
print(2)
user = request.args.get('ben')
return redirect(url_for('success',name = user))
if __name__ == '__main__':
app.run(debug=True)
//redirect重定向/success/user
<html>
<body>
<from action = "http://localhost:5000/login" method = "post">
<p>Enter Name:</p>
<p><input type = "text" name = "ben" /></p>
<p><input type = "submint" value = "submit" /></p>
</from>
</body>
</html>
Flask模版
视图函数的主要作用是生成请求的响应
1.处理业务逻辑
2.返回响应内容
把业务逻辑和表现内容放在一起,会增加代码的复杂度和维护成本
使用模版:使用静态的页面html展示动态的内容
render_template
加载html文件,默认文件路径在templates
这部分先不一一赘述了,后面会单开一篇文章学习
模版注入漏洞介绍
SSTI漏洞是什么?
服务器端模板注入(Server – Side Template Injection,SSTI)漏洞,是指攻击者能够利用应用程序对模板引擎的使用,将恶意的模板代码注入到服务器端的模板渲染过程中,使得这些恶意代码在服务器端被执行,从而获取敏感信息、执行系统命令或控制服务器等恶意操作的一种安全漏洞。
Flask漏洞
flask代码不严谨,可能造成任意文件读取和RCE远程控制后台系统
漏洞成因:
渲染模版时,没有严格控制对用户的输入;
使用了危险的模版,导致用户可以和flask程序进行交互
(flask是基于python开发的一种web服务器,那么也就意味着如果用户可以和flask进行交互的话,就可以执行python代码,比如eval,system,file等等之类的函数)
实例演示:
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():
str = request.args.get('sun')
html_str = """
<html>
<head></head>
<body>{{str}}</body>
</html>
"""
return render_template_string(html_str, str=str)
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1', 8082)
以GET的方式获取sun的值。并赋值给str
str值通过render_template_string加载到body中间,其中{{str}},会被预先渲染转义,然后才会输出,不会被渲染执行,而是直接当文本输出
漏洞演示
format()函数解释:
python中用于格式化字符串的内置函数,能让字符串更符合特定需求。
基本使用
format()
函数通过花括号 {}
作为占位符,将指定的值填充到字符串中
name = "Tom"
age = 20
print("My name is {} and I'm {} years old.".format(name, age))
进阶用法
- 使用关键字参数:通过关键字参数使代码可读性更强,如:
city = "London"
country = "UK"
print("I live in {city}, {country}.".format(city=city, country=country))
输出 I live in London, UK.
- 嵌套花括号转义:若字符串中本身需包含花括号,可通过双花括号
{{}}
进行转义,例如:
print("The price is {{{}}} dollars.".format(100))
输出 The price is {100} dollars.
演示:
from importlib.resources import contents
import time
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():
str = request.args.get('sun')
html_str = """
<html>
<head></head>
<body>{0}</body>
</html>
""".format(str)
return render_template_string(html_str)
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1', 8082)
通过format()函数先直接向{}中写入字符串,然后才会经rander_template_string解析,所以可以顺序颠倒,输入的字符串可以被rander_template_string当做命令执行
判断模版类型
实际也是一种注入漏洞,判断上什么模版就很重要了
根据图中有无响应,可对常见模版进行判断(其余模版还有很多)
此部分会后续持续更新~
继承关系和魔术方法
漏洞利用的关键(逻辑和利用方法)
继承关系(父类和子类)
子类调用父类下的其他子类
Python flask 脚本没有办法直接执行Python指令
object是父子关系的顶端,所有的数据类型最终的父类都是object
利用逻辑如图:
演示:
class A:pass
class B(A):pass
class C(B):pass
class D(B):pass
c = C()
print(c.__class__.__base__)
//<class '__main__.B'>
print(c.__class__.__base__.__base__.__base__)
//<class 'object'>
print(c.__class__.__mro__)
//(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
//罗列了各个类
print(c.__class__.__mro__[1].__subclasses__())
//[<class '__main__.C'>, <class '__main__.D'>]特定查看B类下的所有子类
魔术方法
__class__查找当前类型所属的对象
__base__沿着父子类的关系往上走一个
__mro__查找当前类对象的所有的继承类
__init__查找类是否重载,重载是指程序在运行时就已经加载好了这个模块到内存中,如果出现wrapper字眼,说明没有重载(类的初始化方法)返回的类型是function
__globals__函数会以字典的形式返回当前对象的全部全局变量
__builtins__提供对Python的所有“内置”标识符的直接访问,即先加载内嵌函数再调用
__import__ : 动态加载类和函数,用于导入模块,经常用于导入os模块(例如__import__('os').popen('ls').read())
url_for :flask的方法,可以用于得到__builtins__
lipsum :flask的一个方法,可以用于得到__builtins__,而且lipsum.__globals__含有os模块:{{lipsum.__globals__['os'].popen('ls').read()}}
config:当前application的所有配置。
popen():执行一个 shell 以运行命令来开启一个进程
request.args.key:获取get传入的key的值
检查漏洞