前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【NISACTF 2022】babyupload

【NISACTF 2022】babyupload

作者头像
ph0ebus
发布2023-05-16 10:56:43
3070
发布2023-05-16 10:56:43
举报
文章被收录于专栏:我的网安魔法之旅
本题考点

  1. python代码审计
  2. os.path.join() 处理路径拼接的问题
做题过程

经典的文件上传页面,F12查看源码

代码语言:javascript
复制
<!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>

发现/source路径,访问后得到源码,然后进行代码审计

代码语言:javascript
复制
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)

综上,后端代码的逻辑如下:上传的文件不能有后缀名,上传后生成一个uuid,并将uuid和文件名存入数据库中,并返回文件的uuid。再通过/file/uuid访问文件,通过查询数据库得到对应文件名,在文件名前拼接uploads/后读取该路径下上传的文件。

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

绝对路径拼接漏洞 os.path.join(path,*paths)函数用于将多个文件路径连接成一个组合的路径。第一个函数通常包含了基础路径,而之后的每个参数被当作组件拼接到基础路径之后。 然而,这个函数有一个少有人知的特性,如果拼接的某个路径以 / 开头,那么包括基础路径在内的所有前缀路径都将被删除,该路径将视为绝对路径

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

使用BurpSuite抓包后,修改文件名为 /flag 后发包,利用/file/uuid即可读取 flag。

本文采用CC-BY-SA-3.0协议,转载请注明出处 Author: ph0ebus
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-02-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 本题考点
  • 做题过程
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档