打开题目
最开始以为是文件上传的漏洞
结果发现无论我们上传什么文件都会显示bad filename
去网上看了大佬的wp知道
我们直接去看源代码得到提示 /source
那我们去访问一下这个路径看看
得到一个下载文件
用记事本打开得到
源代码如下
from flask import Flask, request, redirect, g, send_from_directory
import sqlite3
import os
import uuidapp = 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 """<!DOCTYPE html>
<html>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">Select image to upload:<input type="file" name="file"><input type="submit" value="Upload File" name="submit">
</form>
<!-- /source -->
</body>
</html>"""@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!", 403conn = db()cur = conn.cursor()uid = uuid.uuid4().hextry: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/<id>')
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)
代码审计一下后端就行了
@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().hextry:
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)
-
文件上传(upload 函数):
- 代码首先检查是否有名为 'file' 的文件被 POST 请求发送。
- 如果没有找到 'file',则重定向到根目录。
- 然后检查文件名中是否包含句点('.'),如果包含,则返回 "Bad filename!" 并返回状态码 403。
- 生成一个唯一的文件 ID(使用 uuid.uuid4().hex)。
- 尝试将文件信息插入数据库中(使用 SQLite),并在可能存在的情况下捕获重复文件名的异常。
- 将文件保存到 "uploads/" 目录下,并通过重定向返回文件的 ID。
@app.route('/file/<id>')
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
-
文件下载(file 函数):
- 根据文件的 ID 查询数据库中文件的路径。
- 如果找不到文件,则返回 "File not found" 并返回状态码 404。
- 否则,使用
open
函数打开文件,并读取文件内容,然后返回给用户。
看大佬的解释是说:
上传的文件不能有后缀名,上传后生成一个uuid,并将uuid和文件名存入数据库中,并返回文件的uuid。再通过/file/uuid访问文件,通过查询数据库得到对应文件名,在文件名前拼接uploads/后读取该路径下上传的文件。
但肯定要想如何读取 flag 文件,在文件名前被uploads/拼接意味着只能读取上传后的文件,而且上传的文件没有后缀名,不能直接利用,但os.path.join()函数存在绝对路径拼接漏洞
绝对路径拼接漏洞
os.path.join(path,*paths)函数用于将多个文件路径连接成一个组合的路径。第一个函数通常包含了基础路径,而之后的每个参数被当作组件拼接到基础路径之后。
然而,这个函数有一个少有人知的特性,如果拼接的某个路径以 / 开头,那么包括基础路径在内的所有前缀路径都将被删除,该路径将视为绝对路径
解题:
由此,当上传的文件名为 /flag ,上传后通过uuid访问文件后,查询到的文件名是 /flag ,那么进行路径拼接时,uploads/ 将被删除,读取到的就是根目录下的 flag 文件。
上传文件,将filenam改为/flag
得到提示路径
我们直接访问就得到flag
知识点:
uuid
通用唯一标识符 (UUID) 是一种特定形式的标识符,在大多数实际用途中可以安全地认为是唯一的
os.path.join
os.path.join()函数用于路径拼接文件路径,可以传入多个路径
截图来源见:os.path.join()函数用法详解-CSDN博客
参考文章:
[NISACTF 2022]babyupload wp-CSDN博客