The vulnerability is a privilege escalation in Budibase where a user with workspace-scoped builder permissions can escalate their privileges to global admin. This is caused by a combination of a weak authorization middleware and an unsafe SDK function.
The exploit flow starts with a request to the /api/public/v1/roles/assign endpoint.
- The request is first processed by the
builderOrAdmin middleware in packages/backend-core/src/middleware/builderOrAdmin.ts. This middleware is flawed because it allows users who are builders for a specific app (workspace-scoped) to pass, while the endpoint they are accessing can modify global roles. It should restrict access to global admins or global builders.
- After the middleware, the request reaches the
assign function in the controller at packages/server/src/api/controllers/public/roles.ts. Prior to the fix, this function took the request body, which can contain { "builder": true } or { "admin": true }, and passed these properties directly to the SDK function sdk.publicApi.roles.assign without any validation.
- The
assign function in the SDK at packages/pro/src/sdk/publicApi/roles.ts receives the user IDs and the properties to assign. It then unconditionally sets the global property to true for the builder or admin role (e.g., user.builder = { global: true }), effectively granting global privileges.
The patch was applied in the controller (packages/server/src/api/controllers/public/roles.ts) by adding a validation function validateGlobalRoleUpdate that is called within the assign and unAssign functions. This new function checks if the user making the request is a global admin or global builder before allowing the assignment of global roles, thus mitigating the vulnerability at the controller level. The unAssign function was also patched as it had a similar vulnerability.
The vulnerable functions that would appear in a runtime profile during exploitation are the middleware builderOrAdmin, the controller function assign, and the SDK function assign.