By Vinícius Morais
This is one of those interesting stories to tell from the beginning, in a linear way. Some time ago, a coworker and I were performing a pentest in a web application and we came across a strange error message when inserting a single quote in one of the parameters of a request: Neo4jError. Initially it seemed to us that it was just another generic error. However, something we had not noticed yet was hidden in that mist between us and the application.
When we searched a little bit about the error message, we came across the following message: there is a database called Neo4j that uses graphs to represent its data and has its own query language called Cypher. It was then that a doubt arose in our minds: would the error message be the indicator of a code injection?
To solve it, we decided to do a simple test. We changed the numerical value in the id parameter from 42 (Figure 2) to the mathematical operation 41+2–1 (Figure 3) and the answer was the same. This indicated that we were, in fact, facing a code injection.
It was confirmed that we had a part of the Cypher query under our control, the goal now was to understand how we could exploit this to demonstrate a real impact. How, from this failure, would we be able to perform arbitrary queries to the database?
In SQL databases, a very common way to subvert a query is through the UNION operator, which allows an attacker to join the original query with an arbitrary one. We then tried to create a payload using this operator, but it did not work. And what was the reason? We found out that in Cypher there is a restriction in UNION that requires that both queries have the same return name.
With this in mind, we went back to the descriptive error of Figure 1 and started to put the pieces of the puzzle together: the final part of the query (RETURN a) was being exposed and we knew that if we used a valid id, we would have a person’s data as the answer. We concluded that the original query looked like the following:
Knowing that the id value was under our control, we tried to modify the query so that it returned all the labels in the database, respecting the restriction that determined that the payload had to use the variable a as a return name. Thus, the following query was built:
It is worth noting that the first query returns a Number while the second one returns a String. This type difference is not a problem in Cypher, but the above query never worked for a simple reason: this vulnerable parameter had a limit of allowed characters, making it impossible to execute our payload. The application had other vulnerable points that did not have a character limit, but we couldn’t generate an error that provided the name of the return variable in any of those points.
We had a picky problem: we were both so close and so far from the solution! To move forward, we needed to find another payload that would allow for the exploitation of the code injection and that would not use the UNION operator.
Many attempts were then made. After some time, and with the help of great hacking masters (my co-workers), the solution came up. In Cypher there is a very cool operator (for an attacker) called LOAD CSV, which tries to read a CSV file and returns its content as a String. It happens that it also performs HTTP requests to search the file (which already allows for Server-Side Request Forgery attacks) and that was when we finally saw a light at the end of the tunnel we were in.
The idea was the following: we would create a payload that would do the query to bring the labels from the database. However, instead of trying to return them in the request response, a new request would be made to an external server using the LOAD CSV and concatenating the labels to the requested URL.
Once we did not have a server under our control (actually, this was just to make this blogpost cooler), we used the Collaborator tool from Burp Suite.
Burp Suite is a tool widely used in pentest work which acts as a proxy between the client (usually a browser) and the web server. This solution has a mechanism called Burp Collaborator Client which provides temporary URLs and allows to view the requests made for each of them. With these tools, we created a payload with the operator LOAD CSV using a URL provided by Burp Collaborator:
It finally worked out! We managed to get the results of the requests at Collaborator:
The vulnerable application made a request for each Neo4j bank label. One of the recovered labels (Person) can be seen in Image 4.
Anyway, the testing period of this application is over and we thought this matter was also over. However, there was an opportunity to talk about it, which made me create an application purposely vulnerable to Cypher Injection (its code is available in my github) and I was able to continue with the research.
During this research I realized the absence of tools for Cypher Injection exploration and this made me create an extension to Burp Suite in order to facilitate the detection of this vulnerability. This was the way Cypher Injection Scanner extension was born which is available at BApp Store, the Burp Suite extension store.
The Cypher Injection Scanner has two features:
Passive Scan: alerts if a descriptive error from the Neo4j database is found in any of the HTTP responses already received.
Active Scan: It sends payloads containing the LOAD CSV operator to perform DNS requests for a Collaborator session. The scanner applies a variety of payloads, as some syntax details need to be handled according to the location of the query where the injection takes place. If one of the payloads works, an issue is released in Burp Scanner informing, among other data, the type of vulnerability, its severity and the payload used.
The extension can be installed by Burp Suite itself. Just access the Extender tab, then BApp Store and search for Cypher Injection Scanner.
I hope the extension is useful for those who want to venture into Cypher injections.