Case Study: Privilege Escalation — Admin Demotes Workspace Owner | Farchase
Farchase logoFarchase ← All Case Studies Book a Security Call
Case Studies/Privilege Escalation
HIGHA01 · Broken Access ControlAPI5:2023 · BFLARole Management API

Admin Can Demote the Workspace Owner

A missing hierarchy check on the role-update endpoint let an Admin strip the workspace Owner’s role — seizing effective control of the workspace — just by swapping a user_uuid.

CVSS v3.1
~7.6 High
Endpoint
PATCH /workspace/member
Status
Reported · Fix advised
Vulnerability class
Privilege Escalation
CWE
269 · 285 · 639
Endpoint
PATCH /workspace/member
Root cause
No hierarchy / owner check

Executive Summary

A privilege-escalation flaw was identified in the workspace member role-management API. A user holding Admin privileges could demote the workspace Owner — the highest-privileged role — to a lower tier such as approver, simply by supplying the Owner's user_uuid in a role-update request.

The root cause is a missing server-side authorization check on PATCH /workspace/member. The backend confirmed the requester was an authenticated Admin but never verified whether that Admin was permitted to modify a user of equal or higher privilege. The target is selected entirely from a client-supplied user_uuid, trusted without validating the target's current role against the requester's authority.

Because the Owner controls workspace governance, users, and settings, an Admin who can strip the Owner's role can seize effective control of the workspace, disrupt governance, and potentially lock the legitimate Owner out. This breaks the core invariant of the role model — lower roles must not act on higher roles — and is rated High.

Background & Context

Workspaces use a role hierarchy: Owner (full control of users, roles, settings, ownership), Admin (broad management of members and resources — but not authority over the Owner), and Approver / lower roles (scoped permissions).

The security contract is that an Admin may manage roles at or below their own level. Demoting the Owner lets a subordinate role reach up the hierarchy and neutralize the account above it. Ownership-level changes should be reserved to the Owner via a deliberate, confirmed ownership-transfer flow — never available as a side effect of the general member-role endpoint.

The Vulnerability

What happens

The PATCH /workspace/member endpoint accepts a role, a user_uuid, and a type, and applies the role change to the referenced user. When an Admin calls it:

  • The backend authenticates the session / bearer token (confirms who is asking). ✅
  • It reads the client-supplied user_uuid and role. ✅
  • It never checks whether the requester may modify a user whose current role is Owner — no privilege-hierarchy comparison, no owner-protection rule. ❌
  • It applies the demotion, changing the Owner's role to approver. ❌

Two overlapping weaknesses

  • Vertical privilege escalation (CWE-269): a lower role (Admin) performs an action that should require Owner-level authority.
  • Object-level authorization bypass via a user-controlled key (CWE-639): the target is chosen from a client-supplied user_uuid with no entitlement check binding the action to the requester's permissions.

Technical Walkthrough

Documented for defensive validation. Tokens and UUIDs are placeholders/redacted.

Step 1 — Authenticate as Admin and capture the bearer token and workspace context. Step 2 — Capture a role-update request for a user the Admin is allowed to manage, via an HTTP proxy. Step 3 — Retarget the Owner using the Admin's own valid token:

PATCH /workspace/member HTTP/2
Host: api.nected.ai
Authorization: Bearer <ADMIN_AUTH_TOKEN>
Nected-Ws: <WORKSPACE_ID>
Content-Type: application/json

{
  "role": "approver",
  "user_uuid": "<OWNER_USER_UUID>",
  "type": "team"
}

Step 4 — Send. Forward the request. Step 5 — Observe. The server accepts it and demotes the Owner to approver, confirming the backend does not validate whether the Admin is authorized to modify an Owner-level account.

Root Cause Analysis

  • No privilege-hierarchy validation. The endpoint never compares the requester's role against the target's current role. The rule "you may only change roles at or below your level" is not expressed in code.
  • No owner-protection rule. Nothing prevents the Owner from being modified through the general member-role endpoint. Ownership changes should be gated behind a dedicated, confirmed transfer flow.
  • User-controlled user_uuid trusted blindly (CWE-639). The target is selected from client input with no accompanying entitlement check.

Underlying all three is the recurring mistake: authentication was enforced, authorization on the specific target was not.

Impact Assessment

Technical impact

  • Unauthorized demotion of the Owner to a lower role (approver), stripping their control.
  • Collapse of the role-hierarchy invariant — a subordinate role can act on a superior one.
  • The same primitive likely enables other unauthorized role changes across members.

Business impact

  • Workspace takeover / governance disruption — removing the Owner's authority can leave the workspace effectively controlled by the Admin.
  • Lock-out of the legitimate Owner from managing users, roles, and settings.
  • Trust & continuity risk — a broken authorization model in role management may carry compliance implications.

CVSS v3.1

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:L~7.6 (High)

MetricValueReasoning
Attack VectorNetworkExploited over the API.
Attack ComplexityLowSingle modified request.
Privileges RequiredLowRequires a valid Admin account.
User InteractionNoneNo victim action needed.
IntegrityHighUnauthorized modification of a high-privilege account's role.
AvailabilityLowOwner loses ability to manage the workspace.

Scoring note: the vector computes to ~7.6 (High); dropping confidentiality to C:N gives ~7.1, and scoring the escalation as a scope change (S:C) rises to ~8.5. All defensible readings land in the High band.

Remediation

Primary fix — enforce authorization server-side

function updateMemberRole(requester, targetUuid, newRole):
    target = lookupMember(requester.workspaceId, targetUuid)
    if target is null: return notFound()

    # 1. Same-workspace / membership check
    if target.workspaceId != requester.workspaceId: return forbidden()

    # 2. Owner protection: Owner role may not be changed via this endpoint
    if target.role == OWNER:
        return forbidden("Use the ownership-transfer flow")

    # 3. Hierarchy check: requester must outrank the target and the new role
    if roleRank(requester) <= roleRank(target) or roleRank(requester) <= roleRank(newRole):
        return forbidden("Insufficient privilege for this role change")

    applyRole(target, newRole)
    auditLog(actor=requester, action="roleChange", target=targetUuid, newRole)
    return success()
  • Compare against the target's current role, not just the requester's role in isolation.
  • Protect the Owner explicitly — ownership changes go through a dedicated, confirmed transfer flow.
  • Deny by default.
  • Never trust client-supplied user_uuid for sensitive actions without an entitlement check.

Defense-in-depth

  • Apply the same hierarchy checks to every role-changing operation (promote, demote, remove, invite).
  • Secure ownership-transfer flow with explicit Owner confirmation and step-up MFA.
  • Guard against a workspace with no Owner — reject any change that would leave it ownerless.
  • Audit logging and alerting on any attempt to modify an Owner- or Admin-level account.
  • Automated authorization tests in CI asserting an Admin cannot modify the Owner.

Lessons Learned

  • Authentication ≠ authorization. The server confirmed the requester was an Admin but never asked whether they could act on this Owner.
  • Check the target's rank, not just the actor's. A role change is a comparison between requester and target.
  • Ownership deserves its own door — Owner-level changes should never be reachable through the generic member-role endpoint.
  • User-controlled IDs are authorization decisions.
  • Test access control negatively: "Can an Admin demote the Owner?"

Summary

Type
Privilege Escalation / Broken Access Control
Endpoint
PATCH /workspace/member
Vulnerable param
user_uuid (+ role)
Root cause
No hierarchy/owner-protection check on a user-controlled target
Severity
High · CVSS ~7.6
Fix
Compare requester vs. target role; protect Owner; deny by default

Can a Subordinate Role Reach Up Yours?

Broken access control is the #1 web risk — and hierarchy bugs slip past scanners. Farchase tests every role change against real permissions.