Skip to content

Any authenticated user (incl. BASIC) can change the account-holder/admin email

High
mjashanks published GHSA-j82g-67x3-xcwh Jul 22, 2026

Package

budibase/budibase:latest

Affected versions

<3.40.0

Patched versions

3.40.0

Description

Summary

PUT /api/global/users/tenant/owner (changeTenantOwnerEmail) reassigns the email of the tenant account holder (the top-privilege admin), saving with isAccountHolder:true, allowChangingEmail:true (deliberately bypassing the normal "cannot change the account-holder" protection). The route's only authorization is the cloudRestricted middleware, which is a no-op when SELF_HOSTED / DISABLE_ACCOUNT_PORTAL is set; the router-wide guard then requires only that the caller is authenticated. So any logged-in user — including the lowest-privilege BASIC app user — can repoint the admin's email to an attacker address, then run the public password-reset flow to fully take over the admin account.

Details

  • Route: packages/worker/src/api/routes/global/users.ts:80-84PUT /api/global/users/tenant/owner on the cloudRestrictedRoutes group.
  • Group: packages/worker/src/api/routes/endpointGroups/standard.tscloudRestrictedRoutes = group(cloudRestricted) (no adminOnly/role check).
  • packages/worker/src/middleware/cloudRestricted.ts:9-26 — the isValidInternalAPIKey check is wrapped in if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL); in self-host it falls straight through to next().
  • Router guard packages/worker/src/api/index.ts (~line 160) requires only ctx.isAuthenticated && ctx.user.budibaseAccess (every global user, any role).
  • Handler packages/worker/src/api/controllers/global/users.ts:109-141 — takes originalEmail, newAccountEmail, tenantIds from the request body; sets tenantUser.email = newAccountEmail, clears SSO, saves isAccountHolder:true, allowChangingEmail:true.

PoC

  1. An admin/account-holder exists (admin@poc.local). Create + log in a normal BASIC user (basic@poc.local, verified builder.global=false, admin.global=false).
  2. As the BASIC user:
curl -s -b basic.ck -X PUT localhost:10000/api/global/users/tenant/owner \
  -H 'Content-Type: application/json' \
  -d '{"originalEmail":"admin@poc.local","newAccountEmail":"attacker@evil.test","tenantIds":["default"]}'

Result

  • Response: HTTP 200 OK.
  • Admin email reassigned: login as admin@poc.local403; login as attacker@evil.test (now the admin account) → 200. This email-hijack primitive is what was demonstrated live — a BASIC user moved the top-privilege account-holder identity to an attacker-controlled address.
  • Takeover completion (caveat — honesty): the next step is the standard public password-reset against attacker@evil.test. On the PoC instance this returned 400 because no SMTP was configured on the test box (reset cannot send the mail), so the reset→login leg was not executed end-to-end here. On any production self-host (SMTP configured — the normal state), the reset email lands in the attacker's mailbox → new password → full admin/account-holder takeover (global builder + admin → app source, all datasource credentials, all data, and builder-only server-side automation steps incl. the self-host bash/executeScript step = server RCE). The completion is a generic SMTP-gated consequence of owning the account email, not a separate vulnerability — but it is flagged here as not-live-reproduced rather than claimed.

Impact

Privilege escalation
Account take over
Admin account take over

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N

CVE ID

No known CVE

Weaknesses

Weak Password Recovery Mechanism for Forgotten Password

The product contains a mechanism for users to recover or change their passwords without knowing the original password, but the mechanism is weak. Learn more on MITRE.

Missing Authorization

The product does not perform an authorization check when an actor attempts to access a resource or perform an action. Learn more on MITRE.

Credits