目录

[NISACTF 2022]babyupload

目录

[NISACTF 2022]babyupload

查看页面源码,得到提示:/source

https://pic.imgdb.cn/item/655729ecc458853aefecc0f9.jpg

访问后自动下载源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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 """<!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!", 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/<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)

第一部分代码的注释:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 路由定义:处理文件上传
@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,看下一段代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 路由定义:处理文件查看请求
@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:
        # 如果文件不存在,返回 "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() 有一个特性:当某个路径以 / 开头,会丢弃之前的路径

相关实验

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

https://pic.imgdb.cn/item/6557325bc458853aef06be7c.jpg

得到 flag :NSSCTF{c1c5c881-6f4b-41e3-b43d-b031717171fe}