Lab: Exploiting NoSQL operator injection to extract unknown fields

Intro

The difficulty level of this exercise is PRACTITIONER and the goal is to log into the application as an administrator. The database backend is MongoDB.

To solve this exercise, we need Burp Suite, either Community or Professional Edition.

Walkthrough

After starting the exercise, the shop should look like the screenshot. However, the products in the shop may be different, as the Web Security Academy exercises are regenerated again and again. It is important to note that this has no influence on the solution.

At the top left you will find the link "My account".

Now let's try to log in to the application as user "carlos" with the password "invalid".

After submitting the form, we receive the message "Invalid username or password". Since we know that the username is "carlos", the password "invalid" will not be correct.

To find out the correct password, we now switch to the Burp Proxy and there to "HTTP history". To do this, we click on "Proxy" and on "HTTP history".

In the HTTP history we can see all requests and responses that have passed through the Burp Proxy so far. We are now looking for a POST request to the endpoint /login.

g

We send this request to the Burp Repeater by moving the mouse over the request and pressing the right mouse button. A context menu opens in which we select the "Send to Repeater" option.

g

In Burp Repeater, we now change the value of the password parameter from "invalid" to {"$ne": "invalid"}. The request should now look like this:

POST /login HTTP/2
Host: 0aed0051033812b68337ebec001500cd.web-security-academy.net
Cookie: session=OS4ZQHZdBpDmCG0FK5jLnyHvg07imrzw
Content-Length: 42
Sec-Ch-Ua-Platform: "Linux"
Accept-Language: en-US,en;q=0.9
Sec-Ch-Ua: "Chromium";v="131", "Not_A Brand";v="24"
Content-Type: application/json
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.86 Safari/537.36
Accept: */*
Origin: https://0aed0051033812b68337ebec001500cd.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0aed0051033812b68337ebec001500cd.web-security-academy.net/login
Accept-Encoding: gzip, deflate, br
Priority: u=1, i
{"username":"carlos","password":{"$ne":"invalid"}}

We now send this request to the application. In response, we receive the error message "Account locked: please reset your password". To read this message, we have to scroll down a little in the response.

Although we cannot access Carlos' account, this response indicates that the $ne operator has been accepted and the application is vulnerable. We switch to the browser and try to reset Carlos' password. To do this, we click on the "Forgot password?" link.

In the form we enter the user name "carlos" and click on "Submit". In response, we receive "Please check your email for a reset password link". As we don't have access to Carlos' mailbox, we can't click on the link. We switch back to the Burp Repeater and test whether the application is susceptible to JavaScript injection. To do this, we add the entry "$where": "0" to the JSON in the body of the request. Our request should now look like this:

POST /login HTTP/2
Host: 0aed0051033812b68337ebec001500cd.web-security-academy.net
Cookie: session=OS4ZQHZdBpDmCG0FK5jLnyHvg07imrzw
Content-Length: 66
Sec-Ch-Ua-Platform: "Linux"
Accept-Language: en-US,en;q=0.9
Sec-Ch-Ua: "Chromium";v="131", "Not_A Brand";v="24"
Content-Type: application/json
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.86 Safari/537.36
Accept: */*
Origin: https://0aed0051033812b68337ebec001500cd.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0aed0051033812b68337ebec001500cd.web-security-academy.net/login
Accept-Encoding: gzip, deflate, br
Priority: u=1, i
{"username":"carlos","password":{"$ne":"invalid" }, "$where": "0"}

After we have sent the request, we receive the message "Invalid username or password". This message appears in the response if we scroll down a little.

Now we change the parameter from "0" to "1". So that our complete JSON looks like this:

{"username":"carlos","password":{"$ne":"invalid" }, "$where": "1"}

After we have sent the request, we receive the message "Account locked: please reset your password". This message appears in the response when we scroll down a little. This means that the JavaScript in the $where clause is being evaluated.

We send this request to the Burp Intruder by moving the mouse over the request and pressing the right mouse button. A context menu opens in which we select the "Send to Intruder" option.

In Burp Intruder, we now construct an attack with which we can identify all fields of the user object. To do this, we adjust the value of the "$where" parameter as follows:

"$where":"Object.keys(this)[1].match('^.{}.*')"

The static method Object.keys() returns an array with the property names of the object this, which can be enumerated with a string key. The method .match('^.{}.*')" searches for the specified RegEx in the string. More information about RegEx can be found here https://en.wikipedia.org/wiki/Regular_expression.

In Burp Repeater, we now adapt our request as follows. First, we add two payload positions. To do this, we click with the mouse between {} and click on the "Add §" button

The JSON should now look like this:

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

Now we click with the mouse behind {} and click on the "Add §" button

The JSON should now look like this:

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

If necessary, the button must be clicked twice to set both §s.

We select the "Cluster bomb attack" in the drop-down field for attack selection.

In the Payloads section, we now select Payload position 1 and "Numbers" as the Payload type.

In the Number range section, we enter 0 for From: and 20 for To:.

Ein Bild, das Text, Screenshot, Reihe, Schrift enthält. Automatisch generierte Beschreibung

In the Payloads section, we now select the Payload position "2" and the Payload type "Simple list". In the Payload configuration section, we now add all letters from a-z and A-Z, as well as all numbers from 0-9. When using Burp Suite Professional, the predefined lists from Burp can be used. To start the attack, click on the "Start attack" button.

With the Community Edition of Burp Suite, this attack takes a little longer than with the Professional Edition. Time to get a coffee.

After completing the attack, we need to sort the output. To do this, we click once on Payload 1 and then twice on Length. This sorts the output according to the message "Account locked". Your output should look like the screenshot:

Ein Bild, das Text, Zahl, Reihe, Schrift enthält. Automatisch generierte Beschreibung

For Payload 2, you can now see the name of the parameter: username. We can now identify further names by increasing [1] to [2] and so on.

"Object.keys(this)[2].match('^.{}.*')"

Now we start a new attack by clicking on "Start attack". The result should now look like this:

We have now identified the name of the second parameter password. If we insert [4], we get a very interesting parameter: pwReset.

Now we need to test whether the pwResetTkn parameter can be used with the password reset function.

We switch to the Burp Proxy and open the HTTP history there. Here we search for the GET request to the endpoint /forgot-password. If you remember, we tested the password reset function earlier. Send this request to Burp Repeater. To do this, move the mouse over the request and press the right mouse button. Select "Send to Repeater" in the context menu.

Ein Bild, das Text, Screenshot, Zahl, Schrift enthält. Automatisch generierte Beschreibung

In the Burp Repeater, we append some simple data to the first line of the request. For example:

GET /forgot-password?foo=invalid HTTP/2

When you send the request, you receive a response that is the same as the original response. This means that the response is not influenced by our parameter foo. Let's now try out our parameter pwResetTkn.

GET /forgot-password?pwResetTkn=invalid HTTP/2

As you can see, we now receive the message "Invalid token". This confirms that we have the correct token name and endpoint.

We now switch to Burp Intruder and open our request to the /login endpoint. Here we make the following adjustment:

From

"$where":"Object.keys(this)[4].match('^.{}.*')"

becomes

"$where":"this.pwResetTkn.match('^.{§§}§§.*')"

After these adjustments, we start the attack again by clicking on "Start attack". Once the attack is complete, we need to sort the output. To do this, we click once on Payload 1 and then twice on Length. In the Payload 2column, we now see the token: 9857d80036ae72ab.

In Burp Repeater we now insert the token 9857d80036ae72ab as the value for our parameter pwResetTkn. The first line of the request should look like this:

GET /forgot-password?pwResetTkn=9857d80036ae72ab HTTP/2

We now send this request to the application. After we have received the response, we move the mouse over the response and press the right mouse button, in the context menu we select the option "Show response in browser".

Then click on "Copy".

A new tab must be opened in the browser and the response just copied must be pasted there. A page will appear on which the password for carlos can be changed.

After the password has been changed, the user "carlos" can be used to log in.

Video solution

Last updated