June 12, 2026
How I Bypassed UI Restraints to Leak Organization Roles, Members, and Invites (BAC)
Introduction
OWL
3 min read
Introduction
While looking around a platform and doing bug bounty research, I ran into a simple architectural flaw which was: the frontend and the backend were checking permissions in totally different ways.
By using this, I was able to completely bypass UI restrictions using a low-privileged account and extract sensitive organizational data, including custom role definitions, entire member directories, and pending user email invitations.
Discovery
While looking at the organization settings on redacted.com, I was focusing on how user privileges are handled. The application has a highly restricted, low-privileged role called Trust Sharer (The lowest role).
Naturally, a Trust Sharer shouldn't be allowed to view administrative settings. When I logged into the test account with this restricted role and tried to access the Roles Management interface through the browser at: https://app.redacted.com/settings/organization/roles Or when I tried the members at
[https://app.redacted.com/settings/organization/](https://app.redacted.com/settings/organization/roles)``users
Or pending invites at
[https://app.redacted.com/settings/organization/i](https://app.redacted.com/settings/organization/roles)``nvitations
I got this:
The frontend app did exactly what it was supposed to do. It checked my role, blocked the view, and gave back "You don't have permission to access this page" page .
But as we know, what the UI shows you is not everything, The real question is "Does the server actually back it up?" So let's test it.
Investigating
To see what was happening behind the scenes, I needed to capture a valid authorization token from my low-privileged session. I triggered a simple profile update action in the UI, caught the traffic in Burp Suite, and extracted my Authorization: Bearer <trust_sharer_token> header.
Now since I got my token instead of using the browser, I took that token straight to the backend API responsible for fetching organizational roles:
GET /organizations/{organization_id}/roles HTTP/2
Host: auth.redacted.com
Authorization: Bearer <trust_sharer_token>GET /organizations/{organization_id}/roles HTTP/2
Host: auth.redacted.com
Authorization: Bearer <trust_sharer_token>I sent and guess what? HTTP/2 200 OK.
The server didn't care that my token belonged to a restricted Trust Sharer account. It happily dumped the entire list of organization roles, internal IDs, permission mappings, and custom roles. You can see the full response payload right here.
Now that I found this I tested it on more than just this endpoint and found 2 more vulnerable endpoints which are
/organizations/{organization_id}/users
/organizations/{organization_id}/invitations/organizations/{organization_id}/users
/organizations/{organization_id}/invitationsSending the same request again for these 2 endpoints gave back 200 OK leaking even more information for a low-privilege user
Isolating the Bug
To confirm whether this was an authentication breakdown (session issue) or a pure Broken Function Level Authorization (BFLA) flaw, I performed a quick matrix test:
- No Token: Requests made without an
Authorizationheader returned a clean403 Forbidden. (Authentication is working). - Cross-Tenant Token: Requests made using a valid token from a completely different organization identifier returned a
403 Forbidden. (Tenant isolation is working). - Low-Privileged Internal Token: Requests made using the target organization's low-privileged Trust Sharer account returned a
200 OK.
This proved that while authentication and tenant scoping were handling things fine, the backend was completely skipping role-level validation for these specific endpoints.
Root Cause Analysis
o, what went wrong here?
It's a design habit that a lot of web apps fall into: relying on client-side security. The frontend engineers built robust validation rules to hide menus and block direct URL routing for restricted roles. But the backend API developers provisioned the /roles, /users, and /invitations data endpoints under the broad assumption that if a token was valid and belonged to the tenant ID, the request was safe to process.
When these layers drift out of sync, an attacker doesn't need to fight the UI. They just go straight to the backend and replay the query.
Takeaways for Hunters:
Whenever you're testing an app that has multi-tiered roles (Admin, Member, Viewer, Guest, etc.):
- Don't stop when a button is grayed out or a page throws an "Access Denied" screen in the browser.
- Map out the API routes using your highest-privileged account.
Thanks for reading! Let me know if you have any questions, and happy hunting!