Cyolo Receives Investment from IBM Ventures for Zero Trust Secure Access Platform

How to Test and Prevent NoSQL Injections

Cyolo Team

Cyolo Team

Injections are one of most common web application vulnerabilities. Listed in the OWASP Top 10,  injections are transmissions of data that change the way commands are interpreted in the system. There are many types of injections, including LDAP, XML, HTML, OS commands and NoSQL. In this blog post, we will discuss how to identify, test and prevent NoSQL injections in web applications.

Although many examples of this blog are focused on MongoDB (one of the most popular NoSQL implementations), the test procedures and the prevention section apply to all NoSQL databases.

 

What are NoSQL Databases?

Traditionally developers used structured models for process data and data was stored in Relational Databases. However, in recent years, the need to store and process enormous amounts of data (in Big Data, IoT and real-time web applications) gave way to the need for non-structured databases. These databases are called NoSQL, and they are very common.

A NoSQL (known also like “non SQL”, “non relational” or “not only SQL”) database provides a mechanism for storage and retrieval of data, which is modeled in means other than the tabular relations, which are used in relational databases. Generally, NoSQL databases execute fast queries, are easy to use, and they are scalable and very flexible.

The most used and popular NoSQL Databases include:

 

NoSQL queries are usually performed in JSON (JavaScript Object Notation) format. JSON is an open standard file format for data interchange. Alternatively, MongoDB uses BSON (binary JSON) for storing data.

 

What is a NoSQL Injection?

A NoSQL injection is a vulnerability that allows the attacker to gain control over the NoSQL database. A NoSQL injection happens by sending queries via untrusted and unfiltered web application input, which leads to leaked unauthorized information. NoSQL Injection techniques do not differ much from SQL injections.

Attackers that can exploit a NoSQL injection can bypass authentication, extract and modify data, perform denial of service attacks or even can gain complete control over the application.

Let’s review some examples.

 

Example 1: Typical User Login

In a typical login we have two main types of input data: the username and the password.

A web application request can look like this:

 POST /login HTTP/1.1

…..

{ “username”: “peter”, “password”: “mypass123” }

 

Now let’s assume the web application processes this request using the next query:

User.find({ username: req.body.username, password: req.body.password })

 

If the web application parses this query replacing each input data, the query that finally executes is:

 User.find({ username: “peter”, password: “mypass123” })

 

Until now, everything seems fine. But if we change the HTTP body request like this:

{ “username”: “admin”, “password”: {"$gt":""} }

 

Then the resulting query is:

User.find({ username: “admin”, password: {“$gt”:”” }, ... 

 

$gt is a MongoDB operator that means “greater than”. So, MongoDB queries the User collection that complies with the username “admin” and has a password ‘greater than “” ‘ (empty string). It does not matter what the real password is, since the condition {“$gt”:””} will always be true. Then, if “admin” is a valid administrator user, the find method will return valid data and the attacker can logon to the web application.

 

Example 2: Searching for Information

It is common to have search modules in a web application, so it is not odd to query searches. Suppose there is a customer search functionality and you want to search Benjamin’s profile.

For this, you would need to execute a query in the database to find all information about Mary. The query should be:

> db.customer.find( {name: "Mary"} )

 

With the following result:

{

"_id"  : ObjectId("711d1917170058fe821b34da"),

"name" : "Mary",

  "gender" : "female",

  "age"  : 47,

  "city" : "New York"



}

 

You design a form in the web application with an input value: the name of the customer (customername).

If you process the request with the previous query and concatenate the input data, then the resulting query will be like this:

db.customer.find( {name: req.body.constumername } )

 

This is ok if you input “”, but what happens if you input “{“$ne”: “nobody”}”.

db.customer.find( {name: {"$ne": "nobody"} } )

 

The “$ne” operator means “not equal” in MongoDB. So, it will retrieve all customer records whose names are different from “nobody”. All that information can be sent to the frontend if the application does not have any control, which means an attacker might be able to access unauthorized data.

These are basic simple examples for illustrating the risk. However, there are other more complex cases we will refer to later.

 

Real Life NoSQL Vulnerabilities

NoSQL injections were reported in some CVEs (Common Vulnerabilities and Exposures). One of the more recent ones is CVE-2021-22911: “An improper input sanitization vulnerability exists in Rocket.Chat server 3.11, 3.12 & 3.13 that could lead to unauthenticated NoSQL injection, resulting potentially in RCE”. This CVE has a CVSS (Common Vulnerability Scoring System, a standard for rating severity of vulnerabilities) Score of 7.5 (10 is the highest severity). Rocket is a web framework for developing applications.

According to the vulnerability report, one method of the software was vulnerable to NoSQL injections because it didn’t sanitize data, resulting in accounts takeovers due to leaks of password reset tokens. Here is the related code:

findOne() is a MongoDB method that returns one document that matches the criteria. In this case, an attacker can also take over an admin account and execute a subsequent Remote Code Execution. This is a real example that can severely impact an application.

 

How to Test an Application for NoSQL Vulnerabilities

Knowing the impacts that an attacker can produce if our application is vulnerable to NoSQL Injection, we should test any request to the server that interacts with the database.

Here is our approach for the testing:

 

Identifying the Database

If we are performing black box testing (meaning we don’t have information about the application), then identifying and understanding the database in use can help perform specific tests.

For example, MongoDB usually generates primary keys with the field name “_id”. It uses an algorithm defined in MongoDB documentation:

  • a 4-byte timestamp value, representing the ObjectId’s creation
  • a 5-byte random value generated once per process
  • a 3-byte incrementing counter, initialized to a random value

So, if you review the data from HTTP requests and responses you can find objects like this:

{

  _id: '416e2e66bcf86cd887528100',

  product: 'chair',

  code: 'AZ100',

  color: 'brown',

}

 

In this case, the code is enough to assume that MongoDB is the type of database used.

Another test you can try for complementing your last finding is forcing error messages and comparing them with typical messages of the databases. 

On the other hand, developers should also apply protection methods to prevent attackers from identifying the database in use. Otherwise, it will be harder to track their attacks.

 

Testing with Payloads

As is the case with other types of injections, manipulating input data by trying different payloads is the main way to identify and exploit a NoSQL injection. To do that, we here is a list of payloads we can use in our tests

 To have a complete understanding of any payload, it is important to understand or search for information about some database operators like $eq, $ne, $gt, $where, $exists, $regex.

Some interesting payload examples we can find in the list are:

$where: '1 == 1'

1, $where: '1 == 1'

{ $ne: 1 }

{$gt: ''}

[$ne]=1

{"username": {"$gt":""}, "password": {"$gt":""}}

 

We can test directly in the web application, using a web application proxy (like Burp Suite or OWASP ZAP), or automate the testing with a Fuzz Tool or by writing a custom script to accomplish that task.

By observing the results, you can conclude if your web application is vulnerable to a NoSQL Injection, and which payloads could breach you.

 

Testing Using $where Operator

MongoDB supports a $where operator. This operator passes either a string containing a JavaScript expression or a full JavaScript function to the query system:

{ $where: <string|JavaScript Code> }

 

If the web application accepts this type of operator you can try some specific payloads. For example, assume the application executes this query inserting the input value $customername:

db.costumer.find( { $where: function() { return (this.name == $ customername) } } );

 

This query will retrieve the customer data whose name equals to $customername.

If the input data is not sanitized, an attacker can input “’nobody’; sleep(10000);” and the final query the database executes is:

db.costumer.find( { $where: function() { return (this.name == 'nobody'; sleep(10000)) } } );

 

The MongoDB sleep() function suspends a JavaScript execution context for a specified number of milliseconds. In the example, the server stops execution for 10 seconds.

You can review more details and examples in OWASP Testing for NoSQL Injection.

 

Automated Tool Testing

Tools help us to perform automated testing to detect NoSQL injections.

There is a NoSQL tool called NoSQLMap which is open source and supports MongoDB and CouchDB. Unfortunately, in the last two years it has not been upgraded or maintained.

Other interesting python tools are:

  • Nosql-MongoDB-injection-username-password-enumeration, a tool for enumerating usernames and passwords of Nosql(mongodb) injection vulnerable web applications.
  • NoSQL-Attack-Suite, a tool for bypass authentication and enumerating users.

 

How to Prevent NoSQL Injections

Most injections preventions apply to NoSQL injections:

  • Use a safe API or library  instead of using the interpreter (code that directly executes instructions written in a programming or scripting language). There are database-specific APIs or libraries to use. For example, for MongoDB you can use mongo-sanitize or mongoose.
  • Use server-side input validation preventing inaccurate and malicious data. Always validate expressions and characters that must not be included in the input data. However, some applications require storing and showing special characters like $, { or }. Take in consideration that client-side validation helps but can be bypassed.
  • Don’t use special characters because they can be used to create NoSQL injection payloads; or sanitize the data.
  • If possible, use database controls that limit the amount of data a query can retrieve.
  • Run applications at the lowest privilege possible. For instance, run the web server with a least privileged user and connect to the database with a user that doesn’t have access to administration data. This security control limits application attacks.
  • In MongoDB: don’t use where, mapReduce or group operators (they are used in NoSQL injection payloads); or disable JavaScript in server-side execution. You can disable JavaScript by:
    • Setting the –noscripting command line argument.
    • Setting security.javascriptEnabled to false in the configuration file.

 

Other approaches, like zero trust, can also help to prevent injection attacks. Because the zero trust framework only allows authenticated and authorized users and devices to access applications and data, it is almost impossible for an attacker to bypass these controls. Also, any attempt to attack an application can be detected as suspicious behavior and be closed.

To learn more about zero trust, schedule a demo with the Cyolo experts.

 

 

Subscribe to our Blog

Get the latest posts in your email
OT/ICS Security: People and Challenges

The State of ICS/OT Cybersecurity in 2022 and Beyond, Part 1: People and Challenges

More Articles

Subscribe to our Blog

Subscribe to our Blog

Get the latest posts in your email