Exploiting a HQL injection

Heading
Heading
Apr 18, 2017 · 3 min read

While testing a new project I stumbled over something that looked like a SQL injection. When logging into the web application submits a POST request with a JSON body:

POST /login HTTP/1.1
Host:<host>
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Content-Type: application/json
Content-Length: 65
Cookie: JSESSIONID=D6F3D7EAC2FA80E37726928882610255
Connection: close
{"username":"test","password":"test"}

I immediatly tested the input for SQL injection by appending an extra ‘ to the username. The response contains an exception which means that something went wrong. By looking at the server response one can identify that a “org.hibernate.QueryException” occured due to an invalid query:

Image for post
Image for post

Unfortunately, this means that we do not have a SQL injection but a HQL injection. The Hibernate Query Language (HQL) is a query language, similar to SQL, but operating on persistent objects instead of tables and columns. The HQL is translated to SQL by the Hibernate framework.

Honestly, this was the first time I stumbled over such an injection, so I started to do some more generic SQL injection testing. From the above exception I knew that the query looks like the following (“test” is the injection point):

SELECT u FROM Users u WHERE u.active = 1 AND login = 'test'

This is a typical login query which searches for a valid user entity for the provided username. In the next step I submitted “test’ or ‘1’=’1” and got another exception:

Image for post
Image for post

The new exception is a “javax.persistance.NonUniqueResultException”. This means we got more than one expected result. After further testing, I noticed that the server responds with a HTTP error 422 when there is no valid user in the database (speaking in HQL: “u.active = 1 AND login = ‘user’” evaluates to false for all user entities).

Okay, let’s move further and read some data. My first try was to enumerate all available users. The problem is that HQL does not support all SQL features (e.g. the UNION statement, as well as meta information are missing). There is not very much information about HQL injections. I appended something like “test’ OR substring(login,1,1)=’a’” to the query and got a “NonUniqueResultException”. Fine. Unfortunately, I was not able to extract more than two characters.

I finally found out that I the only way to distinguish the queries is the “NonUniqueResultException”, but after more than two characters there simply is just one result hence no “NonUniqueResultException”. So I needed to find a way to cause a “NonUniqueResultException”. After fiddling around I came up with the following query which meets my requirements:

SELECT u FROM Users u WHERE u.active = 1 AND login = 'test' OR (select count(login) from Users u where login LIKE 'a%')>=1 AND '1'='1'

The introduced subquery counts all user names that start with (in this case) “a”. The query is designed to evaluate to true when we have at least on valid user. I quickly tested it with Burp Suite:

Image for post
Image for post

And the negative result when no user with this name is available:

Image for post
Image for post

After I found a successful way to exfiltrate data using an error-based approach it was time to automate it. So I wrote a little python script for this purpose:

With this, I was able to enumerate all available users including their passwords. Yeah :)

The sad part is that I could not read arbitrary data because HQL is missing all SQL comfort features like meta data about available tables and columns. This is due to HQLs restriction to object-mapped entities. I tried several methods suggested by http://blog.h3xstream.com/2014/02/hql-for-pentesters.html but none of them worked in my case.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store