IDOR – Attacker Can Promote a User From Another Organization to Administrator

Summary
An Insecure Direct Object Reference vulnerability was identified in the administrator role-assignment functionality of a multi-tenant application.
The affected GraphQL mutation accepts a user-controlled personIds value to identify the user who should receive administrator privileges. By replacing the legitimate user ID with the ID of a user belonging to another organization, an authenticated attacker can submit an unauthorized cross-organization role-assignment request.
The server processes the request without properly verifying whether:
- The target user belongs to the attacker’s organization
- The attacker is authorized to manage the target user
- The role modification is restricted to the attacker’s current tenant
- The target organization permits the requested administrative change
As a result, an attacker can promote a user from another organization to administrator, resulting in unauthorized cross-tenant privilege modification.
This issue indicates that tenant-level authorization controls are missing or incorrectly enforced in the affected role-management operation.
—
Vulnerability Details
Vulnerability Type: Insecure Direct Object Reference
Affected Functionality: Administrator role assignment
Affected Component: GraphQL API
Affected Mutation: AssignAdminRole
Affected Parameter: personIds
Authentication Required: Yes
Cross-Organization Impact: Yes
—
Severity
Suggested Severity: Critical
Suggested CVSS 3.1 Score: 9.1
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
The final severity may depend on:
- The permissions required to access the role-management functionality
- Whether a low-privileged user can access the affected mutation
- The permissions granted to organization administrators
- Whether the promoted user gains access to sensitive organization data
- Whether the promoted user can manage members, integrations, settings, or billing information
Because the issue affects administrator role assignments across separate organizations, successful exploitation could result in complete compromise of the affected tenant.
—
Technical Description
The application uses the AssignAdminRole GraphQL mutation to assign administrator privileges to one or more users.
The mutation accepts a list of user identifiers through the following field:
"personIds": [
"USER-ID"
]
The personIds value is fully controlled by the client.
During legitimate use, this field contains the identifier of a user belonging to the authenticated user’s organization. However, an attacker can replace this value with the identifier of a user belonging to another organization.
The server accepts the modified identifier and performs the administrator role assignment without correctly enforcing tenant-level authorization.
The vulnerable authorization flow appears to rely on the supplied user identifier without validating the relationship between:
- The authenticated user
- The authenticated user’s organization
- The target user
- The target user’s organization
- The requested administrative action
A secure implementation should derive the authenticated user’s organization from the server-side session and verify that the target user belongs to the same organization before performing the requested role modification.
—
Impact
Successful exploitation may allow an authenticated attacker to:
- Promote a user from another organization to administrator
- Modify role assignments across organization boundaries
- Grant unauthorized administrative privileges
- Alter the access-control structure of another tenant
- Provide an unauthorized user with access to sensitive organization data
- Allow the promoted user to manage organization members
- Allow the promoted user to modify organization settings
- Enable unauthorized access to integrations and internal resources
- Facilitate additional privilege-escalation attacks
- Create conditions for account takeover
- Compromise the confidentiality and integrity of another organization
- Cause operational disruption within the victim organization
- Abuse missing tenant-isolation controls
If administrators can invite, remove, suspend, or modify users, the promoted user may gain the ability to take control of the victim organization.
—
Attack Scenario
An attacker creates or uses a valid account in an attacker-controlled organization.
The attacker identifies the GraphQL request used to assign administrator privileges to a user in their own organization.
The attacker then obtains the user ID of a person belonging to another organization. The ID may be exposed through another API response, application functionality, logs, predictable references, or a separate authorization issue.
The attacker replaces the legitimate user ID in the personIds array with the victim user’s ID and sends the modified request.
Because the server does not correctly validate tenant ownership and authorization, the victim user is assigned administrator privileges.
The attacker may then coordinate with the promoted account or abuse additional application functionality to access and compromise the victim organization.
—
Prerequisites
The following conditions are required:
- The attacker has a valid authenticated account
- The attacker can access or reproduce the administrator role-assignment request
- The attacker knows the user ID of a user belonging to another organization
- The target user is valid and active
- The server fails to enforce tenant-level ownership validation
The attacker does not require authorized membership in the victim organization.
No victim interaction is required.
—
Test Setup
Use two completely separate organizations to demonstrate the cross-tenant impact.
Organization A – Attacker Organization
Create or use:
- An attacker-controlled authenticated account
- A regular user who can be legitimately promoted during request interception
Organization B – Victim Organization
Create or use:
- A regular victim user who is not currently an administrator
Before testing, confirm that:
- The attacker account belongs only to Organization A
- The victim user belongs only to Organization B
- The victim user is not already an administrator
- There is no shared membership or authorization relationship between the two organizations
—
Steps to Reproduce
Step 1: Log In to the Attacker Account
Log in using an account belonging to Organization A.
Navigate to the team-management or user-management functionality where user roles can be modified.
—
Step 2: Configure Request Interception
Configure an HTTP interception proxy such as Burp Suite and ensure that the application’s traffic is passing through the proxy.
Enable interception before performing the administrator role-assignment action.
—
Step 3: Capture a Legitimate Administrator Assignment Request
Select a regular user belonging to Organization A and attempt to promote that user to administrator.
Intercept the generated GraphQL request.
The request uses the following operation:
AssignAdminRole
—
Step 4: Locate the Target User ID
In the GraphQL variables, locate the personIds field.
A legitimate request contains a user ID belonging to Organization A:
"personIds": [
"ATTACKER-ORGANIZATION-USER-ID"
]
—
Step 5: Replace the User ID
Replace the legitimate user ID with the ID of a user belonging to Organization B:
"personIds": [
"VICTIM-USER-ID"
]
Do not modify the attacker’s authentication token, session cookie, or authenticated organization context.
The modified request must continue to use the attacker’s authenticated session.
—
Step 6: Send the Modified Request
Forward the modified GraphQL request to the server.
—
Step 7: Verify the API Response
Observe that the server accepts the request instead of rejecting it with an authorization error.
The returned user object may indicate that administrator privileges have been enabled:
"isAdmin": true
The response may also contain details of the victim user, confirming that the role modification was performed against a cross-organization account.
—
Step 8: Verify the Victim User’s Role
Using an authorized account belonging to Organization B, review the victim user’s role.
Observe that the victim user has been promoted to administrator without authorization from Organization B.
This confirms that:
- The attacker modified a user outside their organization
- The server did not enforce tenant ownership validation
- The attacker performed a privileged cross-organization action
—
Affected Request
POST /api/graphql HTTP/2
Host: [REDACTED]
Cookie: [ATTACKER-SESSION-COOKIE]
User-Agent: Mozilla/5.0
Accept: */*
Content-Type: application/json
Origin: [REDACTED]
Referer: [REDACTED]
Connection: close
{
"operationName": "AssignAdminRole",
"variables": {
"input": {
"personIds": [
"VICTIM-USER-ID"
]
}
},
"query": "mutation AssignAdminRole($input: AssignAdminsInput!) {\n assignAdmins(input: $input) {\n ...TeamManagementPersonFragment\n __typename\n }\n}\n\nfragment TeamManagementPersonFragment on Person {\n ...PersonFragment\n isAdmin\n isActive\n notesSeatPlan\n sourcingSeatPlan\n notesSeatCooldownUntil\n sourcingSeatCooldownUntil\n notesSeatPlanData {\n callsCapturedInCurrentSubscriptionPeriod\n freeSessionsRemaining {\n personalRemaining\n userSessionAllowance\n userSessionCount\n __typename\n }\n __typename\n }\n sourcingSeatPlanData {\n seatPlan\n searchesCreated\n monthlyCandidatesSeen\n lifetimeCandidatesSeen\n monthlyCandidatesUsed\n lifetimeCandidatesUsed\n __typename\n }\n atsJobRoles\n usageStats {\n recentSessions(limit: 1) {\n id\n expectedStartTime\n __typename\n }\n __typename\n }\n participant {\n id\n __typename\n }\n __typename\n}\n\nfragment PersonFragment on Person {\n id\n firstName\n lastName\n pictureUrl\n email\n __typename\n}"
}
—
Vulnerable Parameter
"personIds": [
"VICTIM-USER-ID"
]
The application trusts the attacker-controlled personIds value without confirming that:
- The target user belongs to the authenticated user’s organization
- The attacker has permission to manage the target user
- The operation is restricted to the current tenant
- The target organization authorizes the role modification
- The target user is eligible to receive the requested role
—
Expected Result
The server should verify that the target user belongs to the same organization as the authenticated requester.
If the target user belongs to another organization, the request should be rejected.
Example response:
HTTP/2 403 Forbidden
Content-Type: application/json
{
"error": "You are not authorized to modify this user."
}
The victim user’s role should remain unchanged.
—
Actual Result
The server accepts the attacker-controlled user ID and processes the administrator role assignment.
A user belonging to another organization is promoted to administrator without proper authorization.
—
Root Cause
The root cause is missing or insufficient server-side authorization validation.
The server verifies that the request originates from an authenticated user but does not correctly verify whether the authenticated user is authorized to manage the referenced target user.
The affected mutation performs the role assignment based on the supplied personIds value without enforcing tenant ownership.
This represents both an object-level and function-level authorization failure.
—
Remediation
Implement strict server-side authorization checks for every role-management operation.
Before assigning or modifying a role, the server should:
Obtain the authenticated user’s identity from the server-side session or access token.
Obtain the authenticated user’s organization ID from trusted server-side data.
Retrieve the target user from the database using the supplied user ID.
Determine the target user’s organization using server-side records.
Verify that the authenticated user and target user belong to the same organization.
Confirm that the authenticated user has explicit permission to assign administrator roles.
Confirm that the target user is eligible for the requested role.
Reject every cross-organization role-management request.
Avoid relying on client-supplied organization or ownership information.
Apply equivalent authorization controls to all related user-management operations.
Example authorization logic:
authenticated_user = get_authenticated_user(request)
target_user = get_user_by_id(request.person_id)
if target_user is None:
return forbidden()
if target_user.organization_id != authenticated_user.organization_id:
return forbidden()
if not authenticated_user.can_manage_admin_roles:
return forbidden()
assign_admin_role(target_user)
Authorization should be implemented through a centralized policy layer rather than independently in individual GraphQL resolvers.
—
Additional Security Recommendations
The following additional protections are recommended:
- Use deny-by-default authorization policies
- Enforce tenant scoping at the database-query level
- Ensure every user-management query includes the authenticated tenant ID
- Prevent cross-tenant object references from being resolved
- Add automated tests for horizontal and vertical authorization bypasses
- Review all GraphQL mutations that accept user IDs or organization IDs
- Log attempted cross-tenant role modifications
- Alert the security team about repeated unauthorized role-assignment attempts
- Require re-authentication or multi-factor authentication for sensitive role changes
- Record role changes in an immutable audit log
- Notify organization owners when administrator roles are assigned or removed
- Review administrator removal, invitation, suspension, deletion, and seat-assignment operations for similar vulnerabilities
A tenant-scoped database query should be used wherever possible:
SELECT *
FROM users
WHERE id = :target_user_id
AND organization_id = :authenticated_organization_id;
If no matching record is returned, the operation should be rejected.
—
Suggested Validation Tests
Authorized Same-Organization Request
An authorized administrator promotes a user belonging to the same organization.
Expected result: The request succeeds.
Unauthorized Same-Organization Request
A low-privileged user attempts to promote another user from the same organization.
Expected result: The request is rejected with 403 Forbidden.
Cross-Organization Request
An authorized administrator attempts to promote a user from another organization.
Expected result: The request is rejected with 403 Forbidden.
Invalid User ID
An attacker supplies a nonexistent user ID.
Expected result: The request is rejected without disclosing whether the user exists.
Multiple User IDs
An attacker supplies both same-organization and cross-organization user IDs in the personIds array.
Expected result: The entire request is rejected, or only explicitly authorized users are processed after validating every supplied identifier.
Administrator Removal
An attacker attempts to remove administrator privileges from a user in another organization.
Expected result: The request is rejected.
Related Role-Management Operations
Test invitation, deletion, suspension, role removal, and seat-assignment operations.
Expected result: Every operation enforces the same tenant-level authorization policy.
—
Vulnerability Classification
CWE-639: Authorization Bypass Through User-Controlled Key
CWE-862: Missing Authorization
CWE-863: Incorrect Authorization
OWASP Top 10: A01:2021 – Broken Access Control
OWASP API Security Top 10: API1 – Broken Object Level Authorization
OWASP API Security Top 10: API5 – Broken Function Level Authorization
—
Business Impact
The vulnerability undermines the logical separation between independent customer organizations.
A successful attacker can alter administrative privileges in another tenant without authorization. Depending on the permissions available to administrators, this could lead to:
- Exposure of confidential business information
- Unauthorized access to sensitive user data
- Modification of organization-wide settings
- Removal or suspension of legitimate users
- Unauthorized control over integrations
- Security configuration changes
- Operational disruption
- Regulatory and contractual compliance concerns
- Loss of customer trust
- Complete compromise of the affected organization
For a multi-tenant SaaS platform, proper separation between customer organizations is a critical security requirement. A failure in tenant-level role authorization should therefore be treated as a high-priority security issue.
—
Conclusion
The AssignAdminRole GraphQL mutation does not properly enforce tenant-level authorization when processing attacker-controlled personIds values.
An authenticated attacker can replace a legitimate user ID with the ID of a user belonging to another organization and perform an unauthorized administrator role assignment.
Strict server-side validation must be implemented to ensure that the authenticated user is authorized to manage the target user and that both accounts belong to the same organization.