Type Confusion em aplicações web
Neste artigo, vamos abordar Type Confusion: um vetor de ataque em linguagens web, onde os tipos de variáveis são determinados e atualizados em tempo de execução.
Antes de prosseguirmos, é fundamental compreender os conceitos de tipagem dinâmica e tipagem estática, pois eles formam a base para entender como ocorre o Type Confusion em aplicações web.
Tipagem dinâmica x Tipagem estática
Na tipagem dinâmica, os tipos das variáveis são determinados e atualizados em tempo de execução, ao contrário da tipagem estática, onde os tipos são definidos em tempo de compilação.
Tipagem Dinâmica
JavaScript usa tipagem dinâmica, o que significa que a variável 'x' não possui um tipo estático e, portanto, pode assumir qualquer tipo. Pode ser undefined, object, boolean, string, array, etc.
Tipagem Estática
No Java, você declara e inicializa a variável “x” com um valor inteiro. Como Java é uma linguagem de tipagem estática, uma vez que o tipo de uma variável é declarado, ele não pode ser alterado. Portanto, uma tentativa de atribuir uma string à variável “x” resultaria em um erro de compilação, pois o tipo da variável “x” é declarado como inteiro.
O Que é o Type Confusion?
Agora que compreendemos as diferenças entre tipagem dinâmica e estática, podemos explorar o conceito de Type Confusion.
Type Confusion é uma vulnerabilidade que ocorre quando tipos de dados são manipulados de forma inadequada, representando um risco à segurança do sistema. Essa vulnerabilidade é mais comum em linguagens de tipagem dinâmica, onde os tipos são determinados em tempo de execução e podem ser alterados durante a execução do programa. Esse tipo de vulnerabilidade pode resultar em diversos problemas de segurança, pois o sistema pode interpretar ou processar dados de maneira inesperada com impactos que podem variar dependendo do contexto da aplicação.
Exemplo de Código Vulnerável
Vamos analisar um exemplo de código que ilustra uma vulnerabilidade de Path Traversal.
O código a seguir é uma aplicação Express simples que serve arquivos a partir do diretório /files
, com base no parâmetro de consulta file
. No entanto, é importante observar que a exploração dessa vulnerabilidade de Path Traversal só é possível devido à existência de uma vulnerabilidade de Type Confusion. Sem explorar o Type Confusion, não conseguiríamos explorar efetivamente o Path Traversal.
Entendendo o Método indexOf
Antes de prosseguirmos com a exploração, é essencial compreender o que é o método indexOf
.
No código a seguir, o método indexOf
é utilizado para verificar se o parâmetro file
contém a sequência ..
, que pode indicar uma tentativa de navegação para diretórios superiores.
O método indexOf
retorna o índice da primeira ocorrência da substring especificada (no caso, ".."
) dentro da string. Se a substring não for encontrada, o método retorna -1
. Portanto, o código usa file.indexOf("..") !== -1
para detectar possíveis tentativas de Path Traversal e bloquear o acesso a caminhos suspeitos.
Agora que entendemos como o método indexOf
é utilizado para detectar a presença de sequências específicas, como ..
, vamos observar a aplicação em funcionamento.
Primeiro, faremos uma solicitação para acessar um arquivo válido dentro do diretório /files
. Em seguida, vamos testar a aplicação com uma tentativa de Path Traversal para verificar como ela lida com essa entrada e se a proteção contra essa vulnerabilidade está funcionando corretamente.
Vamos tentar acessar um arquivo existente, por exemplo, document.txt
, que está localizado dentro do diretório /files
. A URL de solicitação seria:
http://localhost:3000/?file=document.txt
Agora, vamos testar a tentativa de Path Traversal usando o parâmetro file
para acessar um diretório superior. Por exemplo:
http://localhost:3000/?file=../../etc/passwd
O Impacto do Type Confusion
Enquanto o método indexOf
é usado para detectar tentativas básicas de Path Traversal, é importante notar uma limitação significativa relacionada ao Type Confusion. No código apresentado, não há uma verificação explícita do tipo do parâmetro file
, o que pode levar a problemas se o tipo da entrada não for o esperado.
Comportamento do Método indexOf
em Strings e Arrays
Existem alguns métodos integrados que são definidos tanto em String
quanto em Array
e que possuem o mesmo nome, como includes
, indexOf
, lastIndexOf
, etc.
O método indexOf
tem comportamentos distintos dependendo do tipo da variável com a qual é chamado. Em JavaScript:
- String.prototype.indexOf(): Quando o parâmetro
file
é uma string, indexOf("..") verifica a presença da substring ".." e retorna o índice da sua primeira ocorrência. Se não encontrado, retorna -1. - Array.prototype.indexOf(): Se o parâmetro
file
fosse um array, o mesmo métodoindexOf
verificaria se o elemento ".." está presente no array. Nesse caso, se o array contiver apenas um elemento, como ["../"], a função retornaria -1 se a substring não for encontrada como um item do array.
A ausência de uma verificação rigorosa do tipo do parâmetro file
permite a manipulação da entrada de maneiras inesperadas. Por exemplo, se um array for passado em vez de uma string, o método indexOf
não funcionará como esperado para detectar Path Traversal, o que pode permitir que o ataque seja bem-sucedido.
Conversão de Strings para Arrays
Uma maneira de passar arrays em parâmetros de consulta é através da conversão de strings para arrays. O pacote qs
é amplamente utilizado para essa finalidade e é comum em frameworks como Express.js. Ele transforma uma string de consulta em um objeto JavaScript para facilitar a manipulação dos dados.
O qs
analisa a string de consulta e interpreta o tipo de dados com base na estrutura dos parâmetros. Por exemplo:
- Parâmetros Múltiplos com o Mesmo Nome: Se uma string de consulta contiver múltiplas ocorrências do mesmo nome de parâmetro, o
qs
as tratará como um array. Por exemplo, a consulta?file=value1&file=value2
será convertida em um array["value1", "value2"]
. - Parâmetros Declarados como Arrays: O
qs
também pode identificar parâmetros explicitamente declarados como arrays na string de consulta. Por exemplo, a consulta?file[]=value1&file[]=value2
será interpretada como um array["value1", "value2"]
.
Isso significa que podemos enviar um parâmetro como um array para explorar falhas na verificação de tipo e potencialmente contornar as medidas de segurança que dependem da manipulação de strings.
Agora que compreendemos como a manipulação de arrays em parâmetros de consulta pode contornar a verificação de tipo, vamos retornar à aplicação e testar essa vulnerabilidade na prática.
Utilizaremos um parâmetro de consulta formatado como um array para explorar a falha de Path Traversal e acessar o arquivo passwd
:
http://127.0.0.1:3000/?file[]=../../../../etc/passwd
Do Type Confusion ao Cross-site Scripting (XSS): Uma Perspectiva Ampliada
Após explorar o impacto do Type Confusion na vulnerabilidade de Path Traversal, é crucial expandir nossa compreensão para outras formas de falhas de segurança na web.
O Type Confusion pode ter implicações mais amplas do que inicialmente aparenta. Por exemplo, quando o tipo de dado não é devidamente verificado, outras vulnerabilidades, como Cross-site Scripting (XSS), podem ser facilitadas ou exploradas de maneiras inesperadas.
O Caso do CVE-2021-23443
Um exemplo concreto é o CVE-2021-23443, uma vulnerabilidade significativa identificada na biblioteca edge.js
. Essa falha envolve XSS, permitindo que um atacante injete e execute scripts maliciosos no navegador dos usuários.
Vamos analisar como essa vulnerabilidade foi explorada em versões anteriores à 5.3.2 e as implicações que ela teve para a segurança das aplicações que utilizavam essa biblioteca. Embora essa falha tenha sido corrigida nas versões posteriores, ela serve como um exemplo importante da necessidade de verificação robusta de tipos e outras práticas de segurança para proteger aplicações web contra uma variedade de ameaças.
No exemplo de código abaixo, apresentamos uma aplicação básica em Node.js que simula uma loja de produtos. A aplicação utiliza o framework Express para gerenciar rotas e servir conteúdo estático, enquanto o edge.js é empregado para renderizar páginas dinâmicas. O código inclui um campo de pesquisa onde os usuários podem pesquisar por produtos.
Vamos simular uma busca por produtos utilizando a aplicação. Podemos fazer uma requisição para procurar por um produto específico, como "pants", utilizando o seguinte formato de URL:
http://localhost:3000/?search=pants
Agora, vamos observar como a aplicação se comporta ao inserirmos uma payload de XSS. Suponha que um usuário mal-intencionado tente explorar a aplicação ao passar a seguinte payload no campo de pesquisa:
http://localhost:3000/?search=<script>alert('XSS')</script>
Ao inserir a payload de XSS, notamos que nenhum alerta foi exibido na tela. Em vez disso, o código JavaScript injetado foi exibido como texto literal. Ao analisar o código HTML da página, vemos que a aplicação fez a codificação dos caracteres especiais, transformando a payload em:
<script>alert('XSS')</script>
Essa transformação para entidades HTML (como <, > e '
) impede que o conteúdo seja interpretado como código executável pelo navegador. O HTML encoding garante que a entrada do usuário seja tratada como texto comum, protegendo a aplicação contra ataques de XSS ao evitar a execução de scripts maliciosos.
A Função escape()
: A Cúmplice Inesperada na Vulnerabilidade de Type Confusion
Agora, vamos dar uma olhada na função escape()
da biblioteca edge.js, uma peça fundamental para escapar caracteres especiais em “strings” e evitar injeções de código. É quase irônico como algo projetado para proteger a aplicação pode se tornar um problema de segurança quando não se faz uma verificação adequada do tipo de entrada.
A função escape()
mostrada na imagem acima é projetada para escapar caracteres especiais em strings, ajudando a prevenir injeções de código . Ela faz isso apenas se a entrada for uma string, usando helpers_1.string.escapeHTML(input)
. Se a entrada não for uma string, a função verifica se é uma instância de SafeValue
e retorna seu valor. Caso contrário, retorna a entrada original sem modificações.
O problema surge quando um tipo de dado inesperado, como um array, é passado para a função. Nesse caso, a função escape()
não realiza o escape de caracteres especiais, retornando a entrada original sem a devida sanitização.
Sabendo disso, podemos explorar a vulnerabilidade enviando um array como entrada:
http://localhost:3000/?search[]=<script>alert('XSS')</script>
Prevenindo ataques de NoSQL injection?
Enquanto navegava pela internet, encontrei um código que parecia prometer proteção automática contra injeções. A imagem que acompanhei dizia: “Ao utilizar um ODM ou ORM, você se beneficia automaticamente de proteções integradas contra injeção NoSQL, reduzindo o risco de vulnerabilidades em seu código.”
O código que ilustra essa “proteção” é o seguinte:
// Mongoose example with validation and sanitization
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
age: { type: Number, min: 0, max: 150 },
});
const UserModel = mongoose.model('User', userSchema);
const name = req.body.name; // User input
const query = UserModel.find({ name: name });
query.exec((err, result) => {
// Handle the result
});
Embora o código acima utilize um ODM como o Mongoose, ele ainda pode ser vulnerável a ataques se não houver uma verificação rigorosa dos tipos de dados. Vamos explorar como essas proteções podem ser contornadas e como os ataques de NoSQL injection podem ser facilitados por falhas de Type Confusion.
O código abaixo foi adaptado a partir do exemplo acima que prometia proteção automática contra injeções NoSQL:
Após iniciar a aplicação, podemos realizar uma requisição POST ao endpoint /users
, fornecendo o valor "Frank" para o parâmetro name
a fim de buscar um usuário específico.
Até aqui, tudo parece estar funcionando conforme o esperado. No entanto, é exatamente aqui que surge a vulnerabilidade de Type Confusion. Ao analisarmos o código, notamos que não há nenhuma verificação do tipo da variável name, como mostrado neste trecho:
Sem a devida verificação do tipo de entrada, a aplicação pode ser vulnerável a ataques, como injeções NoSQL, aproveitando-se da falta de controle sobre o tipo de dado recebido.
Se olharmos a documentação do MongoDB, veremos que ela fornece diversos operadores que podem ser utilizados em consultas. Um desses operadores é o $ne
, que é utilizado para buscar documentos onde o valor de um campo não é igual a um valor especificado.
Após revisar a documentação do operador $ne
, podemos ver como ele pode ser usado para explorar a vulnerabilidade de NoSQL injection associada à falta de validação rigorosa de tipos, especificamente o Type Confusion.
Neste contexto, podemos empregar a seguinte payload:
{"name": {"$ne": null}}
Quando essa payload é enviada, o operador $ne
instrui o MongoDB a retornar todos os documentos onde o campo name
é diferente de null
. Devido à ausência de validação do tipo da variável name
no código, a aplicação não está configurada para tratar adequadamente esse tipo de consulta. Isso leva à recuperação de todos os usuários, conforme ilustrado na imagem abaixo:
Mitigando o Type Confusion
Para mitigar eficazmente vulnerabilidades de Type Confusion, é essencial adotar uma abordagem rigorosa de validação de tipos no código. Isso envolve a verificação explícita do tipo de uma variável antes de realizar operações que dependam de tipos específicos. O uso do operador typeof
em JavaScript é uma prática útil para garantir que as variáveis sejam do tipo esperado.
Além disso, a utilização de linguagens com tipagem estática, como TypeScript, pode ajudar significativamente a prevenir problemas relacionados a type confusion. TypeScript oferece uma verificação de tipos em tempo de compilação, o que reduz a probabilidade de erros de tipo em tempo de execução e fortalece a robustez do código.
Outra recomendação é o uso de bibliotecas de validação de dados, como o Zod. O Zod permite definir tipos de dados de forma clara e concisa, garantindo que apenas dados válidos sejam aceitos. Isso contribui para a segurança do código, minimizando o risco de processamento inadequado de dados e fortalecendo a proteção contra vulnerabilidades.
Exemplo de Mitigação no Código da Biblioteca edge.js
Para ilustrar a aplicação das técnicas de mitigação discutidas, vamos analisar uma correção implementada na função escape()
da biblioteca edge.js
.
A mitigação foi implementada com a seguinte alteração no código:
export function escape(input: any): string {
return input instanceof SafeValue ? input.value : string.escapeHTML(String(input));
}
Nesta versão corrigida, a função escape foi ajustada para sempre converter input para uma string antes de aplicar escapeHTML
. A verificação instanceof SafeValue
assegura que, se input for uma instância de SafeValue
, seu valor será utilizado diretamente. Caso contrário, qualquer entrada é convertida para string e sanitizada. Isso garante que a função lida corretamente com entradas de tipos variados e evita a vulnerabilidade de Type Confusion.
Em resumo, a exploração de vulnerabilidades como Type Confusion ressalta a importância crucial de validar e sanitizar dados em qualquer aplicação. Garantir que o código seja robusto e seguro exige uma combinação de boas práticas, como verificação de tipos, uso de linguagens com tipagem estática e implementação de bibliotecas de validação confiáveis.
Os exemplos discutidos ao longo deste artigo demonstram como falhas simples podem levar a brechas significativas, e como abordagens de mitigação eficazes podem fortalecer a segurança do sistema. Como citado no início do artigo, o impacto dessa vulnerabilidade pode variar dependendo do contexto da aplicação.
E é importante lembrar: a segurança não é apenas uma etapa no desenvolvimento, mas um processo contínuo que deve ser integrado a todas as fases do ciclo de vida do software.
Onde Praticar Type Confusion?
Atualmente, o Hacking Club oferece diversos CTFs que exploram a vulnerabilidade de Type Confusion em vários contextos diferentes. A plataforma é ideal para quem deseja aprender hacking de forma prática e aprofundada.
Temos mais de 200 ambientes com vulnerabilidades reais e write-ups detalhados para treinamento. Além disso, disponibilizamos laboratórios específicos e várias aulas adicionais sobre Type Confusion para um aprendizado mais completo.
Semanalmente, lançamos máquinas gratuitas para você praticar e desafiar suas habilidades em hacking!