Por Vinícius Morais

Esta é uma daquelas histórias interessantes de se contar do início, de forma linear. Há algum tempo, eu e um colega de trabalho estávamos realizando um pentest em uma aplicação web e nos deparamos com uma estranha mensagem de erro ao inserir uma aspa simples em um dos parâmetros de uma requisição: Neo4jError. Inicialmente nos pareceu que era só mais um erro genérico. Porém, algo que ainda não havíamos notado estava escondido naquela névoa entre nós e a aplicação.

Figura1 — Mensagem de erro do banco Neo4j

Ao pesquisarmos um pouco sobre a mensagem de erro, descobrimos o seguinte: existe um banco de dados chamado Neo4j que utiliza grafos para representar seus dados e tem uma linguagem de consulta própria chamada Cypher. Foi então que uma dúvida surgiu em nossas mentes: seria a mensagem de erro o indicativo de um code injection?

Para saná-la, resolvemos fazer um teste simples. Trocamos o valor numérico no parâmetro id da requisição de 42 (Figura 2) para a operação matemática 41+2–1 (Figura 3) e a resposta foi a mesma. Isso indicou que estávamos, de fato, diante de um code injection.

Figura 2 — Requisição original
 
Figura 3 — Requisição com operação matemática

Confirmado que estávamos com uma parte da Cypher query sob o nosso controle, o objetivo agora era entender como poderíamos explorar isso para demonstrar um impacto real. Como, a partir dessa falha, conseguiríamos realizar consultas arbitrárias para o banco de dados?

Em bancos de dados SQL, uma forma muito comum de subverter uma consulta é através do operador UNION, que permite juntar a query original com uma outra arbitrária. Tentamos então criar um payload utilizando esse operador, mas isso acabou não funcionando. E qual foi o motivo? Descobrimos que no Cypher existe uma restrição no UNION que exige que ambas as consultas tenham o mesmo nome de retorno.

Com isso em mente, voltamos ao erro descritivo da Figura 1 e começamos a juntar as peças do quebra-cabeça: a parte final da consulta (RETURN a) estava sendo exposta e sabíamos que se usássemos um id válido, tínhamos como resposta os dados de uma pessoa. Concluímos que a consulta original se parecia com a seguinte:

Sabendo que o valor do id estava sob nosso controle, tentamos modificar a consulta para que ela retornasse todos os labels do banco de dados, respeitando a restrição que determinava que o payload tinha que usar a variável a como nome de retorno. Assim, a seguinte consulta foi construída:

Vale notar que a primeira consulta retorna um Number enquanto a segunda retorna uma String. Essa diferença de tipos não é um problema no Cypher, porém a consulta acima nunca chegou a funcionar por um motivo simples: esse parâmetro vulnerável tinha um limite de caracteres permitidos, impossibilitando a execução do nosso payload. A aplicação possuía outros pontos vulneráveis que não tinham um limite de caracteres, porém não conseguimos gerar um erro que fornecesse o nome da variável de retorno em nenhum desses pontos.

Nós tínhamos um problema chato: estávamos ao mesmo tempo tão perto e tão longe da solução! Para seguirmos em frente, precisávamos descobrir outro payload que possibilitasse a exploração do code injection e que não utilizasse o operador UNION.

Muitas tentativas foram feitas. Após algum tempo, e com a ajuda de grandes mestres do hacking (meus colegas de trabalho), a solução surgiu. No Cypher existe um operador muito bacana (para um atacante) chamado LOAD CSV, que tenta ler um arquivo CSV e retorna o seu conteúdo como uma String. Acontece que ele também realiza requisições HTTP para buscar o arquivo (o que já permite realizar ataques Server-Side Request Forgery) e foi aí que finalmente enxergamos uma luz no fim do túnel em que estávamos.

A ideia foi a seguinte: criaríamos um payload que faria a consulta para trazer os labels do banco de dados. Porém, ao invés de tentar retorná-los na resposta da requisição, seria efetuada uma nova requisição para um servidor externo utilizando o LOAD CSV e concatenando os labels na URL requisitada.

Já que não tínhamos um servidor sob nosso controle (na verdade, isso foi só pra deixar esse blogpost mais legal), utilizamos a ferramenta Collaborator do Burp Suite.

O Burp Suite é uma ferramenta bastante utilizada em trabalhos de pentest que atua como um proxy entre o cliente (geralmente um browser) e o servidor web. Essa solução possui um mecanismo chamado Burp Collaborator Client que fornece URLs temporárias e permite visualizar as requisições feitas para cada uma delas. De posse dessas ferramentas, criamos um payload com o operador LOAD CSV utilizando uma URL fornecida pelo Burp Collaborator:

Finalmente deu certo! Nós conseguimos receber os resultados da consulta no Collaborator:

Figura 4: Requisições recebidas após a execução do payload contendo o LOAD CSV

A aplicação vulnerável realizou uma requisição para cada label do banco Neo4j. Um dos labels recuperados (Person) pode ser observado na figura 4.

Enfim, o período de testes dessa aplicação terminou e achávamos que esse assunto estava acabado. Porém surgiu uma oportunidade de palestrar sobre o tema, o que me fez criar uma aplicação propositadamente vulnerável a Cypher Injection, cujo código está disponível no meu github e continuar com a pesquisa.

Durante essa pesquisa eu percebi a ausência de ferramentas para a exploração de Cypher Injection e isso me fez criar uma extensão para o Burp Suite com o objetivo de facilitar a detecção dessa vulnerabilidade. Foi assim que nasceu a extensão Cypher Injection Scanner que está disponível na BApp Store, a loja de extensões do Burp Suite.

O Cypher Injection Scanner tem duas funcionalidades:

Checagem passiva (Passive Scan): alerta se foi encontrado um erro descritivo do banco de dados Neo4j em alguma das respostas HTTP já recebidas.

Figura 5: Issue de erro descritivo

Checagem Ativa (Active Scan): Envia payloads contendo o operador LOAD CSV com o objetivo de realizar requisições DNS para uma sessão do Collaborator. O scanner aplica uma variedade de payloads, pois alguns detalhes de sintaxe precisam ser tratados de acordo com o local da query em que a injeção acontece. Caso um dos payloads funcione, é lançada uma issue no Burp Scanner informando, entre outros dados, o tipo da vulnerabilidade, a sua severidade e o payload utilizado.

Figura 6: Issue de Cypher Injection

A extensão pode ser instalada pelo próprio Burp Suite. Basta acessar a aba Extender, em seguida BApp Store e buscar por Cypher Injection Scanner.

Espero que a extensão seja útil para quem quiser se aventurar nas injeções de Cypher.