
Exploit de prova de conceito para CVE-2023-25813, uma vulnerabilidade de injeção SQL nas versões do Sequelize ORM anteriores a 6.19.1, com análise detalhada e etapas de reprodução.
replacements não são escapados adequadamente, o que pode resultar em injeção de SQL dependendo da estrutura da consulta.Sequelize é uma das bibliotecas ORM (Object-Relational Mapping) disponíveis para Node.js. ORM é uma tecnologia que automatiza a conversão entre objetos e bancos de dados relacionais, permitindo consultar, criar, atualizar e excluir registros do banco de dados por meio de objetos sem escrever consultas SQL diretamente.
// SELECT * FROM users WHERE id = 1;
const user = await User.findByPk(1);
replacements é um mecanismo de vinculação no Sequelize para injetar com segurança os valores de entrada do usuário no SQL ao usar consultas SQL brutas ou métodos ORM. É usado como principal meio de prevenção de injeção de SQL.
// 위치 기반 바인딩
await sequelize.query('SELECT * FROM projects WHERE status = ?', {
replacements: ['active'],
});
// 키 기반 바인딩
await sequelize.query(
'SELECT * FROM users WHERE name = :name AND age = :age',
{
replacements: {
name: 'Alice',
age: 25
}
}
);
Sequelize fornece a opção replacements para vincular com segurança os valores de entrada do usuário ao SQL.
No entanto, nas versões do Sequelize anteriores à 6.19.1, ao usar métodos ORM, apesar de vincular por meio de replacements, ainda pode ocorrer injeção de SQL devido a um problema na ordem interna de geração e substituição do SQL.
Abaixo está um exemplo de código Sequelize que demonstra a vulnerabilidade. A injeção de SQL pode ocorrer ao combinar o uso de literal e replacements.
User.findAll({
where: or(
literal('soundex("firstName") = soundex(:firstName)'),
{ lastName: lastName },
),
replacements: { firstName },
})
O atacante pode injetar os seguintes valores no replacements, quebrando a estrutura da consulta.
{
"firstName": "OR true; DROP TABLE users;",
"lastName": ":firstName"
}
Essa estrutura insere novamente a chave de vinculação (:firstName) no valor, adicionando a chave de vinculação mais uma vez.
SELECT * FROM users
WHERE soundex("firstName") = soundex(:firstName)
OR "lastName" = ':firstName'
A consulta final completa é mostrada abaixo. No entanto, essa consulta assume uma forma que torna difícil determinar claramente se será executada, e o resultado pode variar dependendo do interpretador SQL e da configuração do banco de dados. Em alguns ambientes, é tratada como um simples erro de sintaxe, mas dependendo da configuração, é uma estrutura perigosa que pode executar comandos SQL não intencionais.
SELECT * FROM users
WHERE soundex("firstName") = soundex('OR true; DROP TABLE users;')
OR "lastName" = ''OR true; DROP TABLE users;''
Se o interpretador SQL permitir consultas múltiplas, uma única vinculação pode deletar a tabela.

O Sequelize considera a string passada para a função literal como um trecho SQL confiável e não a escapa. Portanto, essa string é inserida diretamente na consulta SQL, e mesmo que contenha :param, ele permanece sem tratamento.
Como mostrado abaixo, o replacements funciona encontrando e substituindo os tokens :param restantes após a montagem completa da string SQL, imediatamente antes da execução. Assim, a entrada do usuário é inserida dentro do SQL já estruturado, quebrando toda a estrutura da consulta e causando a vulnerabilidade.
// sequelize-6.19.0/src/sequelize.js
if (options.replacements) {
if (Array.isArray(options.replacements)) {
sql = Utils.format([sql].concat(options.replacements), this.options.dialect);
} else {
sql = Utils.formatNamedParameters(sql, options.replacements, this.options.dialect);
}
}
Foi definido injectReplacements para permitir a vinculação apenas em locais sintaticamente seguros.
// sequelize-6.19.1/src/sequelize.js
if (options.replacements) {
if (Array.isArray(options.replacements)) {
sql = Utils.format([sql].concat(options.replacements), this.options.dialect);
} else {
sql = Utils.formatNamedParameters(sql, options.replacements, this.options.dialect);
}
sql = injectReplacements(sql, this.dialect, options.replacements);
}