# [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 """
"""
@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}