NoSQL Injection: The Complete Practical Guide to MongoDB Injection

Data Extraction with NoSQL Injection

How to read other users' documents through a filter you control, using $regex, $in, and operator smuggling to widen result sets and reach hidden fields.

Published

Operators do more than bypass a login. Dropped into a filter you control, they turn a query that was supposed to return your data into one that returns everyone's. This page covers using NoSQL operators as a read primitive: widening result sets, confirming field values, and pulling documents the interface never meant to show you. Practice on a free live target.

The idea: a filter you can widen

Many endpoints run a query shaped by your input. A search, a lookup by email, a listing meant to be scoped to your account. If your input is merged into the filter object, you can often replace a narrow condition with a broad one and the query returns far more than intended.

Consider a product or message search that runs:

const results = await items.find({ owner: req.user.id, title: req.query.title });

If title reaches the query as an object, you can make it match everything:

/search?title[$ne]=__nope__

which the parser turns into:

{ "owner": "...", "title": { "$ne": "__nope__" } }

Every item with a title not equal to that nonsense string now matches. Where the input can influence the scoping field itself, the same move widens past your own account.

Step 1: find a shapeable filter

Look for endpoints that take a value and return records: search, filter, "find by email," listing pages, autocomplete. Confirm the parameter is reflected in which records come back by changing it and watching the result set grow or shrink. That responsiveness tells you your input is part of the filter.

Step 2: smuggle an operator in

Swap a scalar for an operator that matches more than the app intended. Useful widening operators:

{ "field": { "$ne": "value-that-never-matches" } }
{ "field": { "$gt": "" } }
{ "field": { "$regex": ".*" } }
{ "field": { "$exists": true } }

$ne and $regex of dot-star match essentially everything, $gt with an empty string matches any non-empty string, and $exists true matches any document that has the field. Each one broadens the result set, and the right one on a scoping field can spill other users' records into your response.

Step 3: confirm specific values with regex

Extraction is not only about volume, it is about targeting. An anchored or embedded $regex lets you ask yes-or-no questions about a value you cannot see directly. To test whether any document has an email at a given domain:

{ "email": { "$regex": "@example\\.com$" } }

To check whether a specific user's record carries a particular token prefix, combine a pin on the identity with a regex on the secret field:

{ "username": "admin", "apiKey": { "$regex": "^sk_live" } }

If the record comes back, the guess held. If it does not, it failed. That true-or-false behaviour is the seed of full character-by-character recovery, developed in blind and timing.

Reading fields the UI hides

Widening a filter frequently returns whole documents, including fields the front end never displays: password hashes, tokens, internal flags, other users' contact details. When an API serializes the matched document and the client only chooses not to render some fields, the data is already in your response body. Always inspect the raw response, not the rendered page. The most valuable part of an extraction is often a field the UI quietly dropped.

Operator smuggling through parameter handling

Sometimes a single field is used in more than one place, or duplicate parameters are collapsed by the parser in a way that lets an operator reach a spot the developer did not expect. If an endpoint takes the same name as both a scalar and a structured parameter, test whether the structured form wins. This is situational, but when it works it lets you inject an operator into a query the application believed was built only from trusted internal values.

Fixing it

Data extraction closes the same way operator injection does, plus result hygiene:

  • Type-validate every input that reaches a filter so a scalar field cannot arrive as an operator object.
  • Keep scoping conditions server-controlled and separate from user input, so a tenant or owner filter cannot be widened by anything the client sends.
  • Project explicitly. Return only the fields the client needs, so a widened match cannot leak secret fields that were merely hidden in the UI.

Per-driver detail is in preventing NoSQL injection. Next, turn a yes-or-no response into full value recovery in blind and timing, or try it live on a free target.

← Back to NoSQL Injection: The Complete Practical Guide to MongoDB Injection