NoSQL injection

Iam_Wander
8 min readJan 12, 2024

--

NoSQL injection is a vulnerability where an attacker is able to interfere with the queries that an application makes to a NoSQL database. NoSQL injection may enable an attacker to:

  • Bypass authentication or protection mechanisms.
  • Extract or edit data.
  • Cause a denial of service.
  • Execute code on the server.

Lab 1: Detecting NoSQL injection

The product category filter for this lab is powered by a MongoDB NoSQL database. It is vulnerable to NoSQL injection. To solve the lab, we need to list all unlisted products in the product category.

Determining which characters are processed

To determine which characters are interpreted as syntax by the application, you can inject individual characters. For example, you could submit ', which results in the following MongoDB query:
this.category == '''
If this causes change to the original response, it may indicate that the ' character has broken the query. We can verify this by adding an escape string this.category == '\''
If the response remains a the original response, this may indicate that the application is vulnerable to an injection attack.

After detecting a vulnerability, the next step is to determine whether you can influence boolean conditions using NoSQL syntax. To achieve this, we can send two boolean queries.

' && 0 && 'x — False Statement

' && 1 && 'x — True statement.

You can url encode if necessary.

False Statement

false statement
no products are returned

True Statement

Notice the false statement returns no products and the content length is lesser. The true statement on the other hand returns the original content. This is proof we can influence boolean conditions with NoSql syntax.

Overriding existing conditions

Now that we can influence boolean condition, we can inject a JavaScript condition that always evaluates to true.

'||1||'

Which results in the following Querry

this.category == 'product'||'1'=='1'

And since the injected condition is true, all the products should be returned.

And we can now reveal unreleased products.

Lab 2: Exploiting NoSQL operator injection to bypass authentication

In this lab, the login functionality for this lab is powered by a MongoDB No-SQL database. It is vulnerable to No-SQL injection using MongoDB operator.

We will be using operator injections to in the login functionality to solve this lab.

Examples of MongoDB query operators include:

  • $where - Matches documents that satisfy a JavaScript expression.
  • $ne - Matches all values that are not equal to a specified value.
  • $in - Matches all of the values specified in an array.
  • $regex - Selects documents where values match a specified regular expression.

Usage example;

Consider an application that uses the users input form in its POST Body section in json format, for example.

{“username”:”Wiener”,”password”:”peter”}

We can test the inputs with a set of operators to see whether its vulnerable to an injection. i.e

{"username":{"$ne":"invalid"},"password":{"peter"}}

{"username":{"$ne":"invalid"},"password":{"$ne":"invalid"}}

username":{"$in":["admin","administrator","superadmin"]},"password":{"$ne":""}}

Lab Approach

Start by login in to the lab and head to the POST /login

Notice the user input is returned in the body of the request, and with this, we can test with an operator injection whether the lab is vulnerable.

{"username":{"$ne":"invalid"},"password":"peter"}

Observe that we still get logged in, but why?

When we call the operator ‘ne’:’invalid’, this means put any value whose username is not equal to invalid and has the password peter.

{"username":{"$ne":"invalid"},"password":""}

For the same reason when we put a blank password we get logged out as the password cannot be blank.

Since to solve this lab we need to be logged in as admin, we will craft an operator query for the same.

{"username":{"$in":["admin","administrator"]},"password":{"$ne":"invalid"}}

Notice we do not get logged in, but when we add user “wiener” or “carlos” we get logged in as either of them. This means we might not have a user matching admin or administrator exactly as it is.

Therefore we need to use the operator $regex to help match any user with close values to admin and whose password is not equal to an empty value.

{"username":{"$regex":"admin"},"password":{"$ne":""}}

You get logged in as admin. You can use his cookie value to login via a browser.

Lab 3: Exploiting NoSQL injection to extract dataLab: Exploiting NoSQL injection to extract data

Consider a vulnerable application that allows users to look up other registered usernames and displays their role. This triggers a request to the URL:

https://insecure-website.com/user/lookup?username=admin

This triggers the following No-Sql Query

{"$where":"this.username == 'admin'"}

In this Lab, the user lookup functionality is powered by a MongoDB No-SQL database. It is vulnerable to No-SQL injection.

To solve the lab, extract the password for the administrator user, then log in to their account.

You can log in to your own account using the following credentials: wiener:peter.

Once logged in, select the GET /user/lookup?user=wiener from the proxy history, and send it to the repeater, there, we will test whether the database is vulnerable to an injection attack by adding a ‘ character after the =wiener

We recieve an error

When we add a null byte %00 we get the response as it should be. This is because adding a null byte after the ‘%00 ignores everything after.

Now that we know there is a database vulnerability, we will add apply boolean conditions to confirm if the username administrator exists.

GET /user/lookup?user=wiener' && 1 && 'x

GET /user/lookup?user=wiener' && 0 && 'x

If the we get the user details, this means that the user wiener exists. We can do the same for Carlos and administrator. Remember to url encode the boolean condition.

Now that the administrator user exists lets check whether a password field exists.

GET /user/lookup?user=administrator'&&this.password%00

We can now start to brute force for the administrators password length using burp intruder.

GET /user/lookup?user=administrator’&& this.password.length <9 || ‘a’==’b

the password has 8 characters. The next step is to bruteforce the password values using the intruder.

GET /user/lookup?user=administrator' && this.password[0] =='a' || 'a'=='b%00


You can also use the match clause to sove this lab


&& this.password.match('^.{0}a.*')%00

Lab 4: Exploiting NoSQL operator injection to extract unknown fields

The user lookup functionality for this lab is powered by a MongoDB NoSQL database. It is vulnerable to NoSQL injection.

To solve the lab, log in as carlos

We will begin by testing with boolean conditions to confirm whether the application executes any javascript that we can inject.

Start the lab by login in to an arbitrary account, eg wiener:peter

To test whether the application is vulnerable to a NoSql injection, we will introduce the $ne operator and see how the application behaves.

Notice that we get logged in as wiener.

To test whether the application is vulnerable to a javascript injection, we will test for a boolean condition by adding the $where clause in the json data as shown below.

Adding 1, the application results to true, whereas adding 0, we recieve an invalid password response.

Notice when we try to logged in a carlos when the boolean statement=true, his account gets locked and we are required to reset his password via a token that gets sent to his email, which we have no access.

All in all this is good news as we can proceed to enumerate for the field/column values using the $where clause.

For instance we could check what is the first column value as follows.

{"username":"carlos","password":{"$ne":"invalid"},"$where":"Object.keys(this)[0].match('^.{0}a.*')"}

After bruteforcing, we get the first field as ‘id’, second as ‘username’. But since we need to retrieve a login token, we will look for a field that allows us to reset the password. ‘Make sure to sent a forgot password request for carlos, this is to ensure a token recovery field is created’

Now bruteforce for the tocken.

We have discovered a field called unlockToken.

Lets visit

GET /forgot-password?unlocktoken= 

Notice we still need the token value.

We can create a query for this as shown.

{"username":"carlos","password":{"$ne":"invalid"},"$where":"this.unlockToken.match('^.{0}a.*')"}



or

{"username":"carlos","password":{"$ne":"invalid"},"$where":"this.unlockToken[§0§]=='§a§'"}

Once you get the value, forward the request through the repeater and change your password.

Log in with the new password to solve the lab.

Preventing NoSQL injection

The appropriate way to prevent NoSQL injection attacks depends on the specific NoSQL technology in use. As such, we recommend reading the security documentation for your NoSQL database of choice. That said, the following broad guidelines will also help:

  • Sanitize and validate user input, using an allowlist of accepted characters.
  • Insert user input using parameterized queries instead of concatenating user input directly into the query.
  • To prevent operator injection, apply an allowlist of accepted keys.

--

--

No responses yet