# [NISACTF 2022]babyupload # [NISACTF 2022]babyupload 查看页面源码,得到提示:`/source` ![[NISACTF 2022]babyupload-1](https://pic.imgdb.cn/item/655729ecc458853aefecc0f9.jpg) 访问后自动下载源码: ```python 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 """
Select image to upload:
""" @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 # 路由定义:处理文件上传 @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() # 生成唯一的文件ID uid = uuid.uuid4().hex try: # 将文件信息插入数据库 cur.execute("insert into files (id, path) values (?, ?)", (uid, file.filename,)) except sqlite3.IntegrityError: # 处理数据库完整性错误,即文件ID重复 return "Duplicate file" # 提交对数据库的更改 conn.commit() # 将文件保存到服务器的uploads目录 file.save('uploads/' + file.filename) # 重定向到文件查看页面,页面URL包含新上传文件的唯一ID return redirect('/file/' + uid) ``` 上传的文件名不能包含 `.` ,没法利用文件上传来 getshell,看下一段代码: ```python # 路由定义:处理文件查看请求 @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: # 如果文件不存在,返回 "File not found" 并设置状态码为404 return "File not found", 404 # 获取文件内容 with open(os.path.join("uploads/", res[0]), "r") as f: # 读取文件内容并返回给客户端 return f.read() ``` `os.path.join("uploads/", res[0])` 用于拼接文件路径,可以传入多个路径。代码中的意思是把 `uploads/` 与文件名拼接为一个路径,通过 `with open() as f` 打开这个路径,读取内容并显示 `os.path.join()` 有一个特性:当某个路径以 `/` 开头,会丢弃之前的路径 > **相关实验** > > ![[NISACTF 2022]babyupload-2](https://pic.imgdb.cn/item/655730f5c458853aef026bd3.jpg) 可以利用这个这个特性来进行路径遍历 上传一个名为 `/flag` 的文件, `with open(os.path.join("uploads/", res[0]), "r") as f` 执行的结果就变成:`with open("/flag", "r") as f` ![[NISACTF 2022]babyupload-3](https://pic.imgdb.cn/item/6557325bc458853aef06be7c.jpg) 得到 flag :NSSCTF{c1c5c881-6f4b-41e3-b43d-b031717171fe}