进入页面:
尝试文件上传,但是各种后缀名我都试过了,过不去。
在源码中发现提示,存在 ./source 路径:
访问该路径得到源码:
from flask import Flask, request, redirect, g, send_from_directory
import sqlite3
import os
import uuid
app = Flask(__name__)
SCHEMA = """CREATE TABLE files (
id text primary key,
path text
);
"""
def db():
g_db = getattr(g, '_database', None)
if g_db is None:
g_db = g._database = sqlite3.connect("database.db")
return g_db
@app.before_first_request
def setup():
os.remove("database.db")
cur = db().cursor()
cur.executescript(SCHEMA)
@app.route('/')
def hello_world():
return """
"""
@app.route('/source')
def source():
return send_from_directory(directory="/var/www/html/", path="www.zip", as_attachment=True)
@app.route('/upload', methods=['POST'])
def upload():
if 'file' not in request.files:
return redirect('/')
file = request.files['file']
if "." in file.filename:
return "Bad filename!", 403
conn = db()
cur = conn.cursor()
uid = uuid.uuid4().hex
try:
cur.execute("insert into files (id, path) values (?, ?)", (uid, file.filename,))
except sqlite3.IntegrityError:
return "Duplicate file"
conn.commit()
file.save('uploads/' + file.filename)
return redirect('/file/' + uid)
@app.route('/file/')
def file(id):
conn = db()
cur = conn.cursor()
cur.execute("select path from files where id=?", (id,))
res = cur.fetchone()
if res is None:
return "File not found", 404
# print(res[0])
with open(os.path.join("uploads/", res[0]), "r") as f:
return f.read()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
这道题的要求就两个:看懂 python 代码;os.path.join 绝对路径拼接。
为了能更好的看懂本次 python 代码,我推荐以下文章:
python 连接数据库
Python getattr() 函数
关于python中的查询数据库内容中用到的fetchone()函数
request.files
介绍在 Flask 框架中,request.files
是一个代表上传文件的特殊字典对象。它允许您访问用户通过 HTTP 请求上传的文件。具体来说,在 Flask 中,当您的应用程序收到一个带有文件上传的 POST 请求时,可以使用 request.files
来获取上传文件的信息。
request.files
是一个字典,它的键是文件域( input 标签的 name 属性值),而值是一个 FileStorage
对象,该对象提供了访问和操作上传文件的方法和属性。
例如,假设您的 HTML 表单中有一个文件上传域,其 name 属性为 "file"
:
那么您可以通过以下方式访问上传文件:
file = request.files['file']
上面的代码将返回上传文件的 FileStorage
对象,您可以使用该对象的方法和属性来获取文件的详细信息,如文件名、内容类型、保存文件等:
filename = file.filename # 获取上传文件的文件名
file.save('/path/to/save/file') # 将上传文件保存到指定路径
通过 request.files
,您可以轻松地处理和管理用户上传的文件数据。注意,为了使用 request.files
,您的请求必须使用 enctype="multipart/form-data"
编码类型,这是用于文件上传的标准编码类型。
关于 python 的 flask 框架,在学 SSTI 模板注入时有所学习。
# 设置了一个路由,当以 POST 方式访问 ./upload 页面时,就会触发 upload() 函数
@app.route('/upload', methods=['POST'])
def upload():
if 'file' not in request.files:
return redirect('/')
# 从 HTML 表单中获取 file 对象,即用户上传的文件
file = request.files['file']
# 文件名中不能有小数点
if "." in file.filename:
return "Bad filename!", 403
# 调用 db() 函数连接数据库
conn = db()
cur = conn.cursor()
# 调用 uuid 模块以某种方式生成文件的 uid
uid = uuid.uuid4().hex
try:
cur.execute("insert into files (id, path) values (?, ?)", (uid, file.filename,))
except sqlite3.IntegrityError:
return "Duplicate file"
conn.commit()
# 将文件保存到 ./uploads/ 路径下
file.save('uploads/' + file.filename)
# 返回最终保存的文件路径
return redirect('/file/' + uid)
不过在此段代码中 file.save('uploads/' + file.filename)
并没有起到作用,推测原因是 uploads/ 路径不存在。
下面来看这段代码:
# 设定路由,当访问 /file/ 路径时触发 file 函数,其中 为用户输入的任意值,该值会被作为 file 函数的参数值
@app.route('/file/')
def file(id):
# 连接数据库
conn = db()
cur = conn.cursor()
# 执行 SQL 语句,根据 id 查找 path
cur.execute("select path from files where id=?", (id,))
# fetchone 函数的作用在上面的博客中已提到,执行该语句后,res[0] 就是 SQL 语句查询到的 path ,根据源代码,path 就是传入的文件名
res = cur.fetchone()
if res is None:
return "File not found", 404
# print(res[0])
# os.path.join 函数拼接路径,再 open 打开文件
with open(os.path.join("uploads/", res[0]), "r") as f:
return f.read()
os.path.join(path, *paths)
函数用于将多个文件路径连接成一个组合的路径。第一个参数通常包含了基础路径,而之后的每个参数都被当做组件拼接到基础路径后。
但是该函数有个特性:如果拼接的某个路径以 / 开头,那么包括基础路径在内的所有前缀路径都将被删除,该路径将被视为绝对路径。下面的示例揭示了开发者可能遇到的这个陷阱。
比如,上述代码中,若 res[0] 以 / 开头,则前面的 uploads/ 将被抹去,最后拼接的结果只有 res[0] 。
据此,我们可以传入一个名为 “/flag” 的文件,那么最后经过路径拼接抹去前面的 uploads/ 路径,就会直接打开根目录下的 flag 文件。
当然,这种方法存在猜测性,前提是根目录下确实有一个 flag 文件。
文件名为 /flag ,文件内容任意:
返回结果中给出了路径:/file/de087626346e4fc390a272957aa5b88e ,那么直接访问:
拿到 flag 。