NoSQL Injection
Last updated
Last updated
Room Link: https://tryhackme.com/r/room/nosqlinjectiontutorial
Bypassing the Login Screen
First of all, let's open the website on http://10.10.191.67/ and send an incorrect user/pass to capture the request on Burp:
The original captured login request looks like this:
We now proceed to intercept another login request and modify the user and pass variables to send the desired arrays:
This forces the database to return all user documents and as a result we are finally logged into the application:
Logging in as Other Users
We have managed to bypass the application's login screen, but with the former technique, we can only login as the first user returned by the database. By making use of the $nin operator, we are going to modify our payload so that we can control which user we want to obtain.
First, the $nin operator allows us to create a filter by specifying criteria where the desired documents have some field, not in a list of values. So if we want to log in as any user except for the user admin, we could modify our payload to look like this:
This would translate to a filter that has the following structure:
['username'=>['$nin'=>['admin'] ], 'password'=>['$ne'=>'aweasdf']]
Which tells the database to return any user for whom the username isn't admin and the password isn't aweasdf. As a result, we are now granted access to another user's account.
Notice that the $nin operator receives a list of values to ignore. We can continue to expand the list by adjusting our payload as follows:
This would result in a filter like this:
['username'=>['$nin'=>['admin', 'jude'] ], 'password'=>['$ne'=>'aweasdf']]
This can be repeated as many times as needed until we gain access to all of the available accounts.
Note: The jude
user above is not an actual user, but an example of how an additional username can be added.
To find other users
Extracting Users' Passwords
At this point, we have access to all of the accounts in the application. However, it is important to try to extract the actual passwords in use as they might be reused in other services. To accomplish this, we will be abusing the $regex operator to ask a series of questions to the server that allow us to recover the passwords via a process that resembles playing the game hangman.
First, let's take one of the users discovered before and try to guess the length of his password. We will be using the following payload to do that:
Notice that we are asking the database if there is a user with a username of admin and a password that matches the regex: ^.{7}$
. This basically represents a wildcard word of length 7. Since the server responds with a login error, we know the password length for the user admin isn't 7. After some trial and error, we finally arrived at the correct answer:
We now know the password for user admin has length 5. Now to figure out the actual content, we modify our payload as follows:
We are now working with a regex of length 5 (a single letter c plus 4 dots), matching the discovered password length, and asking if the admin's password matches the regex ^c....$
, which means it starts with a lowercase c, followed by any 4 characters. Since the server response is an invalid login, we now know the first letter of the password can't be "c". We continue iterating over all available characters until we get a successful response from the server:
This confirms that the first letter of admin's password is 'a'. The same process can be repeated for the other letters until the full password is recovered. This can be repeated for other users as well if needed.
Following the method mentioned above I was able to get the password for John.
Did it again for pedro
Now that we have covered Operator Injection, let's take a look at a Syntax Injection example. A Python application is running to allow you to receive the email address of any username that is provided. To use the application, authenticate via SSH using ssh syntax@10.10.70.250
along with the credentials below:
Password
syntax
Once authenticated, you can provide a username as input. Let's start by simply providing admin
:
Terminal
We can start to test for Syntax Injection by simply injecting a '
character, which will result in the error seen in the response below:
Terminal
The following line in the error message shows us that there is Syntax Injection:
for x in mycol.find({"$where": "this.username == '" + username + "'"}):
We can see that the username variable is directly concatenated to the query string and that a JavaScript function is being executed in the find command, allowing us to inject into the syntax. In this case, we have verbose error messages to give us an indication that injection is possible. However, even without verbose error messages, we could test for Syntax Injection by providing both a false and true condition and seeing that the output differs, as shown in the example below:
Terminal
Now that we have confirmed Syntax Injection, we can leverage this injection point to dump all email addresses. To do this, we want to ensure that the testing statement of the condition always evaluates to true. As we are injecting into the JavaScript, we can use the payload of '||1||'
. Let's use this to disclose sensitive information:
Terminal
The Exception to the Rule
It is worth noting that for Syntax Injection to occur, the developer has to create custom JavaScript queries. The same function could be performed using the built-in filter functions where ['username' : username]
would return the same result but not be vulnerable to injection. As such, Syntax Injection is rare to find, as it means that the developers are not using the built-in functions and filters. While some complex queries might require direct JavaScript, it is always recommended to avoid this to prevent Syntax Injection. The example shown above is for MongoDB; for other NoSQL solutions, similar Syntax Injection cases may exist, but the actual syntax will be different.