From query manipulation to password extraction

Recently, I analyzed an AngularJS web application that executed queries on the backend in a way that I’ve never seen before.

Example of the queries used by the application, all of them contains a "filtering" paramether.

I tried to identify the backend used and to perform some classic SQL injections, all without success, but I decided to go on and see if I could manipulate the query system to my advantage.

Starting from one of the API endpoints

I performed a decoding of the URL

Trying to figure out the operators used by the filtering mechanism, I tried to substitute the comparison operator “eq” (equal) with other ones that I saw in similar situations, like “lt” (less than), “gt” (greater than), “lte” (less than or equal to ) and “gte” (greater than on equal to). In every case, I obtained a response coherent with my expectations. For example, using a filter set to lt=1 gave back the same answers of a filter set to eq=0.

A query executed with the filter lt set to 1 resulted in an empty set of results.

Filtering the results for eq set to 0 gave back the same results of filtering them for lt set to 1.

The next step was to try to use a filter straight on the users API endpoint, which resulted in a success.

The filter paramether is applied to the /api/users API endpoint with success.

Here I started to ask myself if it was possible to filter the results by fields not contained in the answers given by the API, like the passwords of the users. While trying to filter the passwords of the users with a comparison operator like “gt” obviously doesn’t make sense, trying to use the same operator on a nonexistent field gave me back an error, showing me that it was possible to filter results on the users’ table by their passwords.

Filterinig the query by an unexistent field gave back an error.

To go further, I needed an operator that allowed me to filter the passwords by the characters contained. After some guessing and further analysis of the queries executed on the backend, I was able to find the “contains” operator, that allowed me to filter results by the characters contained in a given field.

Filtering the passwords that contains "a" with the "contains" operator.

This, however, wasn’t still enough, because I wasn’t able to know where the characters were located inside the passwords. After some googling with the information that I’d got until there, I found that the operators that I discovered until that point were often found with something like a “begins with” operator.

After some more guessing, I was able to find the “startswith” operator. A search using the operator with the character “$” gave me back the entire list of the users of the application, while a search with the same operator and the character “a” showed me an empty list.

Filtering the list of the users by the passwords that starts with "$" gave back the full list of the users.

Filtering the users by the password that starts with "a" gave back an empty list of users.

After guessing that the hash of the passwords stored in the database were calculated using bcrypt with a complexity of 13 (they all started with “$2y$13$”), I quickly put up a Python script to execute a blind injection attack and extract the password of a user given his email address.

And here’s the extracted hash of the password of a user. Success!

The hash of the password of an user is extracted with a blind injection from the database.

However, this works only on some databases, i.e. the ones that are configured to perform case sensitive queries, like a default configuration of PostgreSQL, while it doesn’t work on databases that perform case insensitive queries, like a default configuration of MySQL.