đź§  ORM & NoSQL Injection

1. The Core Theory: Attacking the Abstraction

Traditional SQL injection attacks the syntax of a database query. By injecting characters like single quotes (') or operators (UNION), the attacker breaks out of a data string and alters the query’s execution logic.

Modern ORMs (Prisma, Sequelize, Hibernate) and NoSQL databases (MongoDB, CouchDB) successfully mitigate traditional SQLi by treating all input as parameterized data by default. However, they introduce a new vulnerability: Object-Based Exploitation.

Instead of attacking the query string, you are attacking the data structures (JSON, dictionaries, arrays) that the framework uses to build the query. If a backend blindly accepts a user-controlled object and passes it into a database driver without strict type validation, the attacker can inject framework-specific logic operators.

TL;DR

You are no longer trying to break out of a string; you are trying to change the data type from a primitive (String/Integer) to an Object containing logical instructions.


2. The Attacker’s Mindset (Hunting & Fuzzing)

When hunting for ORM/NoSQL injection, your methodology must shift entirely from traditional SQLi mapping.

  • Stop thinking in strings: Payloads like ' OR 1=1-- or admin' # will almost always fail and be treated as literal strings.

  • Look for JSON/Object endpoints: APIs, GraphQL endpoints, and modern framework features (like Next.js Server Actions) that natively parse JSON are the primary attack surface. application/x-www-form-urlencoded forms are less likely to be vulnerable unless the backend explicitly parses the parameters into objects.

  • Fuzz for Type Juggling: Your initial fuzzing should not be testing for database errors, but testing how the application handles unexpected data types.

    • Change "username": "admin" to "username": {"$ne": null}

    • Change "id": 1 to "id": [1, 2, 3]

    • Change "password": "password123" to "password": {"gt": ""}

  • Analyze the Errors: If injecting an object throws a 500 Internal Server Error, a digest error, or a validation error, the framework might be strictly typing the inputs. If it returns 200 OK but behaves differently (e.g., returning all records or logging you in), you have found an injection sink.


3. Mechanisms of Exploitation

A. Type Juggling & Logic Operators

This occurs when the backend expects a primitive value but accepts an object containing comparison operators.

Vulnerable MongoDB / Node.js Pattern:

// The developer expects req.body.username to be a string like "admin"
app.post('/login', async (req, res) => {
    // SINK: req.body is passed directly into the query object
    const user = await db.collection('users').findOne({
        username: req.body.username, 
        password: req.body.password
    });
    
    if (user) return res.send("Authenticated");
});

The Exploit: The attacker sends {"username": {"$ne": null}, "password": {"$ne": null}}. The driver translates $ne to “Not Equal,” returning the first user in the database.

B. Relational & Nested Filter Injection

Specific to advanced ORMs (like Prisma), this occurs when the developer allows the user’s input object to dictate the relationships or nested queries executed against the database.

Vulnerable Prisma Pattern:

// The developer expects a simple string to find a specific user
app.post('/search', async (req, res) => {
    // SINK: userInput is directly mapped to the 'where' clause
    const data = await prisma.user.findMany({
        where: req.body.userInput 
    });
    return res.json(data);
});

The Exploit: Instead of sending {"username": "target"}, the attacker injects a relational query: {"username": "target", "privateFiles": {"some": {"fileName": {"startsWith": "secret"}}}}. This turns the endpoint into a boolean oracle, allowing the attacker to blindly exfiltrate data from linked tables based on whether the query returns a result or an empty array.

C. Mass Assignment / Auto-binding

This happens when an ORM automatically binds all provided properties from a request payload to the corresponding database model, allowing attackers to overwrite fields the developer never intended to expose.

Vulnerable Code Pattern:

app.post('/updateProfile', async (req, res) => {
    // SINK: Entire req.body object is used to update the record
    await db.users.update({ id: req.user.id }, req.body);
});

The Exploit: The attacker intercepts the standard { "email": "new@email.com" } payload and injects privileged fields: { "email": "new@email.com", "role": "admin", "isVerified": true }.

TABLE creation_date AS "Created" 
FROM "05 - Content" 
WHERE contains(techniques, this.file.link) AND contains(tags, "đźš©") 
SORT file.name ASC