[NISACTF 2022]babyupload

[NISACTF 2022]babyupload

本题考点
  1. python代码审计
  2. os.path.join() 处理路径拼接的问题
做题过程
  1. 经典的文件上传页面,F12查看源码

    <!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>
    
  2. 发现/source路径,访问后得到源码,然后进行代码审计

    from flask import Flask, request, redirect, g, send_from_directory
    import sqlite3
    import os
    import uuid  # 通用唯一识别码(Universally Unique Identifier),标准格式为xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx
    
    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  # 生成文件的uuid
        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)  # 保存文件在uploads/下
        return redirect('/file/' + uid)  # 返回文件的uuid
    
    
    @app.route('/file/<id>')
    def file(id):  # 访问文件
        conn = db()
        cur = conn.cursor()
        cur.execute("select path from files where id=?", (id,))  # 根据文件uuid,在数据库中查询文件名
        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:  # 将查询到的文件名与uploads/拼接
            return f.read()
    
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0', port=80)
    
    
  3. 综上,后端代码的逻辑如下:上传的文件不能有后缀名,上传后生成一个uuid,并将uuid和文件名存入数据库中,并返回文件的uuid。再通过/file/uuid访问文件,通过查询数据库得到对应文件名,在文件名前拼接uploads/后读取该路径下上传的文件。

  4. 但肯定要想如何读取 flag 文件,在文件名前被uploads/拼接意味着只能读取上传后的文件,而且上传的文件没有后缀名,不能直接利用,但os.path.join()函数存在绝对路径拼接漏洞

    绝对路径拼接漏洞

    os.path.join(path,*paths)函数用于将多个文件路径连接成一个组合的路径。第一个函数通常包含了基础路径,而之后的每个参数被当作组件拼接到基础路径之后。

    然而,这个函数有一个少有人知的特性,如果拼接的某个路径以 / 开头,那么包括基础路径在内的所有前缀路径都将被删除,该路径将视为绝对路径

  5. 由此,当上传的文件名为 /flag ,上传后通过uuid访问文件后,查询到的文件名是 /flag ,那么进行路径拼接时,uploads/ 将被删除,读取到的就是根目录下的 flag 文件。

  6. 使用BurpSuite抓包后,修改文件名为 /flag 后发包,利用/file/uuid即可读取 flag。
    在这里插入图片描述
    在这里插入图片描述

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值