
Vulnerabilidade de Execução Remota de Código por Injeção de AST no Handlebars.js
Este post é um artigo de pesquisa publicado pela EQSTLab.
PoC referenciado: dinhvaren/cve-2026-33937
★ CVE-2026-33937 PoC de Injeção de AST e Execução Remota de Código no Handlebars.js ★
https://github.com/user-attachments/assets/99e383e7-e71f-4e10-8c62-f50abff8b4f5
CVE-2026-33937: Vulnerabilidade de Injeção de AST e Execução Remota de Código no Handlebars.js
| Versões Afetadas | 4.0.0 – 4.7.8 |
| Versão Corrigida | 4.7.9 |
| Pontuação CVSS | 9.8 (Crítica) |
Uma vulnerabilidade de confusão de tipos no Handlebars.js surge do fato de que Handlebars.compile() aceita não apenas uma string de template, mas também um objeto AST pré-analisado (JSON) como entrada. Um atacante que controla o valor passado para compile() pode fornecer um objeto AST manipulado no qual o campo value de um nó NumberLiteral é substituído por uma string JavaScript arbitrária. Como o gerador de código interno insere esse campo diretamente no corpo da função JavaScript emitida, sem sanitização, a função resultante executa código controlado pelo atacante quando renderizada — alcançando Execução Remota de Código no servidor.
Compile e execute o ambiente vulnerável usando Docker:
Construir Imagem
docker build -t cve-2026-33937-server .
Executar Container
docker run --name cve-2026-33937 -p 12701:12701 -p 9229:9229 cve-2026-33937-server
Ou use os scripts npm fornecidos:
npm run docker:up # build + run in one step
A aplicação é servida em http://localhost:12701.
Interface Web
Abra http://localhost:12701 em um navegador. A página apresenta uma interface de criação de e-mails B2B. Os valores dos campos fornecidos pelo usuário são interpolados em uma string de template do Handlebars no lado do cliente e, em seguida, enviados ao servidor para renderização. Injete um payload malicioso do Handlebars por meio dos campos de entrada ou diretamente pela API.
API Direta
# POST crafted template to the vulnerable endpoint
curl -s -X POST http://localhost:12701/api/email/preview \
-H "Content-Type: application/json" \
-d '{"subject":"test","editorTemplateData":"<PAYLOAD>"}'
Substitua <PAYLOAD> por um payload de injeção de AST direcionado ao Handlebars.compile().
POST /api/email/preview
O servidor aceita um corpo JSON contendo editorTemplateData e o passa diretamente para Handlebars.compile() sem sanitização ou aplicação de lista de permissões (allowlist):
// app.js
const renderEmail = Handlebars.compile(editorTemplateData);
Handlebars.compile() é documentado como aceitando uma string de template, mas sua verificação interna de tipo também permite que um objeto AST pré-analisado seja passado diretamente. Dentro de javascript-compiler.js, o visitor NumberLiteral() emite o campo value do nó literalmente no código-fonte JavaScript gerado, sem qualquer validação de tipo ou sanitização:
// javascript-compiler.js (simplified)
NumberLiteral(number) {
this.pushStackLiteral(number.value); // value inserted as-is into emitted JS
}
Se um atacante fornecer um objeto AST manipulado em que number.value é uma string contendo JavaScript arbitrário (por exemplo, "1; require('child_process').execSync(...)") em vez de um literal numérico, o corpo da função emitida contém e executa esse código no momento da renderização.
O padrão perigoso na aplicação vulnerável é:
const render = Handlebars.compile(userInput); // userInput may be a crafted AST object
render(safeContextData); // attacker code runs here
Nota sobre a técnica de exploração: Os PoCs públicos existentes (incluindo o referenciado) alcançam a injeção por meio de um nó
NumberLiteralcombinado com um helperlookup. Este PoC confirma a mesma primitiva de RCE usando um nóBooleanLiteralcombinado com o helper embutidolog, demonstrando que a confusão de tipos não se limita a um único tipo de nó ou função auxiliar.
/api/email/preview com um valor editorTemplateData manipulado.Handlebars.compile() no objeto AST controlado pelo atacante.render() aciona a execução de JavaScript arbitrário no processo do servidor.Quando o Handlebars é implementado em um ambiente de backend Node.js, essa vulnerabilidade leva a um caminho de execução de código no lado do servidor, ao contrário do XSS padrão no lado do cliente. O ataque não requer que nenhuma saída seja refletida para um usuário; o payload é executado dentro do processo Node.js com os mesmos privilégios da aplicação. Dependendo da implantação:
/etc/passwd) podem ser lidos e exfiltrados.+-------------------------------------------+
| Attacker |
+-------------------------------------------+
|
| POST /api/email/preview
| {"editorTemplateData": "<malicious payload>"}
v
+-------------------------------------------+
| Handlebars.compile(editorTemplateData) |
| (No sanitization — app.js:17) |
+-------------------------------------------+
|
| AST node injection
| breaks template sandbox
v
+-------------------------------------------+
| Arbitrary JS Execution (Server Process) |
+-------------------------------------------+
|
| Read sensitive files, spawn shell,
| exfiltrate secrets, etc.
v
+-------------------------------------------+
| Remote Code Execution (RCE) |
+-------------------------------------------+
Atualize o Handlebars.js para 4.7.9 ou posterior.
A versão 4.7.9 introduz validação rigorosa do tipo de entrada em compile(), rejeitando argumentos que não sejam strings antes do início da geração de código.
Aplique uma verificação de tipo antes de chamar compile().
Se uma atualização imediata não for possível, valide no local da chamada se o argumento é sempre uma string:
if (typeof templateInput !== 'string') throw new TypeError('Template must be a string');
const render = Handlebars.compile(templateInput);
Use handlebars/runtime para pré-compilação em tempo de build.
Pré-compile os templates no momento do build com a CLI do Handlebars e distribua apenas o pacote de runtime. O build de runtime não inclui compile(), eliminando completamente a superfície de ataque em implantações de produção.
Este repositório destina-se exclusivamente a pesquisa em segurança, educação e demonstração controlada de vulnerabilidades. Ele não deve ser usado para testar ou explorar sistemas sem autorização explícita por escrito do proprietário do sistema. O objetivo deste projeto é ajudar pesquisadores de segurança, defensores e desenvolvedores a entender a vulnerabilidade, validar a exposição em ambientes de laboratório controlados e aplicar mitigações eficazes.