View query injection

Not every CouchDB attack needs a planted function. The view and document-listing API is driven by query-string parameters, and when an application forwards attacker-influenced values into key, startkey, endkey, include_docs, or the _all_docs and _find endpoints, the caller can widen a lookup to read documents outside the range the application intended.

Range parameters

A view query is scoped by key parameters. An application that means to fetch one key may build:

GET /app/_design/d/_view/by_user?key="user:42" HTTP/1.1

If the caller controls the key, or the surrounding startkey/endkey, swapping an exact key for an open range returns every row the view holds:

GET /app/_design/d/_view/by_user?startkey="user:"&endkey="user:￰" HTTP/1.1

￰ is a high code point used as an upper bound so the range covers all keys with the prefix. Dropping both bounds returns the whole view, and descending=true with swapped bounds is a common way to defeat naive bound checks:

GET /app/_design/d/_view/by_user?descending=true HTTP/1.1

include_docs

By default a view returns the emitted key and value. Adding include_docs=true makes CouchDB attach the full source document to each row, which can surface fields the view value deliberately omitted:

GET /app/_design/d/_view/by_user?startkey="user:"&endkey="user:￰"&include_docs=true HTTP/1.1

Where an application relied on a view emitting only safe fields, include_docs=true reintroduces everything the document contains, including password hashes, tokens, and _attachments metadata.

_all_docs

_all_docs is a built-in index of every document _id in the database and takes the same range parameters:

GET /app/_all_docs?include_docs=true HTTP/1.1
POST /app/_all_docs HTTP/1.1
Content-Type: application/json

{ "keys": ["user:42", "user:43", "config:smtp", "_design/pub"] }

With include_docs=true this streams every document body in the database, bypassing any view the application exposed as its only intended read path. A keys array lets the caller name arbitrary identifiers, including design documents, to pull them directly.

_find selectors

The Mango _find endpoint takes a JSON selector in the request body. When an application merges user input into the selector object, the attacker injects operators that broaden the match, the same operator-injection idea seen in other document stores:

POST /app/_find HTTP/1.1
Content-Type: application/json

{ "selector": { "role": { "$gt": null } }, "limit": 9999 }

{ "$gt": null } matches every document that has the field, and { "_id": { "$gt": null } } matches all documents. Raising limit and adding "fields" lets the caller pull large result sets in one request:

POST /app/_find HTTP/1.1
Content-Type: application/json

{ "selector": { "_id": { "$gt": null } }, "fields": ["_id","email","password_hash"], "limit": 100000 }

Probing

Compare a scoped request against one with widened bounds: if startkey/endkey or a $gt: null selector returns more rows than the application's normal view, the parameter is attacker-reachable. A key that accepts a JSON array or object where a string was expected, without error, signals the value lands in the query unescaped.

References

Cookie Consent

We use cookies to enhance your experience. Learn more