Labs Covered
This write-up focuses on the following PRACTITIONER-level labs from the PortSwigger Web Security Academy:
3 Exploiting NoSQL injection to extract data
This lab demonstrates how attackers can use NoSQL injection to enumerate and extract sensitive data directly from the database by manipulating query parameters.
4 Exploiting NoSQL operator injection to extract unknown fields
This lab shows how attackers can use NoSQL operator injection to extract information from fields that are not displayed to the user by leveraging injection payloads to return hidden fields.
LAB 3 - Exploiting NoSQL injection to extract data
Lab Description :
Solution :
When I intercepted the request using Burp Suite, I observed a GET request to the following endpoint:
/user/lookup?user=wiener
I then sent this request to the Repeater tab for further testing and manipulation.
I opened the request in the original browser session and modified the parameter using a single quote ' in URL-encoded form %27.
This resulted in the following error message:
There was an error getting user details
This indicates that the input was not properly sanitized and caused a syntax error, suggesting a potential injection point.
I used the payload '+', which I URL-encoded as %27%2B%27, before injecting it into the parameter.
If we do not encode it properly, the application does not return the expected result.
Using the URL-encoded version ensures the payload is processed correctly by the server.
Then I used the payload ' && '1'=='2 to test for a false condition.
The application responded with:
Could not find user
This indicates that the condition was evaluated and returned false, confirming that server-side logic is being affected by our injection.
Then I used the payload ' && '1'=='1, which is a condition that always evaluates to true.
The application responded without an error, confirming that the injected condition was successfully interpreted and the logic was executed as expected.
Which is giving us valid result which mean this payload will work
Now, our goal is to retrieve the administrator user’s data.
To do this, I modified the input to target the administrator account while maintaining a condition that always evaluates to true:
administrator' && '1'=='1
This allows the application to process the request without an error and return the administrator’s information.
Then I used the following payload to determine the password length of the administrator account:
administrator' && this.password.length < 30 || 'a'=='b
This payload checks if the password length is less than 30.
If the response returns successfully, it confirms that the condition is true — helping us estimate the length of the password.
Finding the length of password :
To find the length of the password , we can use the following query
?user=administrator' && this.password.length < 30 || 'a'=='b
We can stop at <30 but since the query has a ’ at the end, we provide the OR query ie(** || ‘a’==’a**). Note that we don’t end the equal statement with a
'because there is a trailing'at the end of the query getting added automatically at the end.
The response shows the email of admin. It means the query is executed successfully & confirming that the password length is less than 30.
When we provide this.password.length < 6, the query fails & we get a failed response (“message”: “Could not find user”).
So the length of admin’s password is 8.
After sending the above payload, we successfully received a response containing the administrator details.
This confirms that the condition evaluated to true.
So now, we proceed to find the exact length of the administrator’s password by adjusting the payload to test different lengths using the .length property in our injection.
Finding admin’s password -
Now we ned to bruteforce each position from 0-7 by bruteforcing it with a-z to find the password.
Modify the nosql query payload as follows.### Finding admin’s password -
Now we ned to bruteforce each position from 0-7 by bruteforcing it with a-z to find the password.
Modify the nosql query payload as follows.
Send the request to Intruder tab, Add the this.password[**$0$**] as payload 1 and this.password[$0$]='**$a$** as paylaod 2.
Payload 1 - Numbers from 1-10 Payload 2 - Characters from a-z
Click on start attack.
Now we have the admin’s password -
Login as admin to solve the lab.
LAB 4 - Exploiting NoSQL operator injection to extract unknown fields
Lab Description :
Solution :
The requirement of the lab is to exploit NoSQL injection vulnerabilities to extract invisible fields and log in to the account of the user carlos.
First, I logged in with an arbitrary account to capture the login request using Burp Suite Proxy.
This allows us to analyze the structure of the request and begin crafting injection payloads for further exploitation.
In the http history we can see the login request
We used the payload "password":{"$ne":"invalid"} to log in as the user wiener, and the login was successful.
This confirms that the application is vulnerable to NoSQL operator injection, allowing us to bypass authentication by injecting a condition that always evaluates to true.
However, when we used the same payload for the user carlos, the application responded with:
Account locked: please reset password
I used the following payload to test for NoSQL injection with a conditional time delay:
{
"username": "carlos",
"password": { "$ne": "invalid" },
"$where": "sleep(5000)"
}
This payload checks if we can bypass authentication for the user carlos and simultaneously confirms that the $where clause is being executed by the database.
As expected, the server delayed its response by 5 seconds, confirming successful evaluation of the $where clause and presence of a NoSQL injection vulnerability.
Extracting available field names -
Send the above request to repeater.
- Replace the
$whereparameter with this"$where":"Object.keys(this)[0].match('^.{}.*')". - Add 2 payload positions to bruteforce -
"$where":"Object.keys(this)[0].match('^.{§§}§§.*')"
"$where": This is a key in a JSON object, and it seems to be used to define a condition or filter.“
Object.keys(this)[0]": This part of the expression is JavaScript code, not a regular expression. It’s using the Object.keys(this) function to get an array of keys of the current object (this), and then [0] is used to access the first key in that array. So, it’s essentially accessing the first key in the current object.
Set Attack Type - Cluster Bomb
Payload 1 - Numbers from 0-20
Payload 2 - Simple list - a-z, A-Z, 0-9
Once the attack is over, sort Payload1 & Length to find the 1st key field which is id.
- Repeat the attack by changing hte position from 1 to 2 in the array -
"$where":"Object.keys(this)[1].match('^.{§§}§§.*')"
This time we got the value of 2nd key - username
- Repeat the process for 3nd key field, we got the value of 3rd field as -
password
- Repeat the process for 4th key field, we got the value of 4th field as -
newpwdTkn
Before bruteforcing the 4th field (which most probably is the password reset field), make sure to send a password reset link for carlos before performing the bruteforce for 4th field. Else you will get
500 Internal server errorwhen you bruteforce.
Bruteforcing the password reset token in 4 field which we have identified above -
So where can we use the following fields which we identified? Possibly the forgot-password request.
Send the GET /forgot-password request to repeater. Add the parameter **?newpwdTkn=
Now we need to extract the pwResetTkn (password reset token) field from the carlos account.
To do this, I used the following $where payload to brute-force the token character by character:
"$where": "this.pwResetTkn.match('^.{$$}$$.*')"
- In the first stage, I used numeric values from
0to20to determine the length of the token by observing when the response changes. - In the second stage, I used a character set composed of lowercase letters, uppercase letters, and numbers to extract the token one character at a time, by matching each character position using a regular expression pattern.
This method allows us to exfiltrate the full token through NoSQL injection with regex-based enumeration.
After the attack finish, we found a 16-character token.
Replace the found token into the request:
GET /forgot-password?pwResetTkn=...
and send it. We received a response containing the password change form.
Change password and login to user carlos. Complete lab.