Mutable Identity Claim Leads to Full Account Takeover
The Risk
An attacker could log in as any other user on the platform, including staff and administrators, simply by editing a hidden identity field the app trusted to decide who they were. Anyone could create a free account in seconds, and that account could read the secret identity codes of other users straight from ordinary screens, then paste one in to become that person. No password, no one-time code, and nothing for the victim to click. In testing, a brand-new free account was used to fully take over a senior regional administrator and see everything that administrator could.
The Vulnerability
The platform outsourced login to a cloud identity provider. After signing in, each user received a signed access token, and the backend read one field from that token, a user-editable identity field, to decide which account the request belonged to. The application's own permission checks were correct: they faithfully enforced whatever rights belonged to the identity the token asserted.
The problem was one layer down, at the identity provider. The provider was configured so the identity field was both mutable and writable by the end user. Every issued access token carried the self-service scope, the scope the provider requires to let a user update their own attributes. That meant a user could call the provider's standard attribute-update operation and overwrite their own identity field with any value they liked.
- The backend trusted the identity field as the authenticated principal.
- The identity provider let the end user rewrite that exact field using their own access token.
- The provider accepted any 32-character value with no validation, including values bound to no account at all.
This is identity forgery at the provider layer, not a flaw in the application's authorization logic. The backend was not failing to check who you were; the provider was letting you choose who you were.
The Attack
The full chain runs from a single fresh, self-signup, lowest-privilege account. The attacker never authenticates as the victim at any point.
Harvesting a Victim Identity
The identity codes of other users were not secret. Two ordinary editor APIs disclosed them in plaintext to any low-privilege account:
- A member-roster endpoint returned every co-editor on a shared data source, each with its identity code.
- A current-workspace endpoint returned the region administrator's identity code directly in its response.
So the attacker could pull a target identity code from a single in-product read, with no special access.
Rewriting the Identity Field
With a target code in hand, the attacker called the identity provider's self-service attribute-update operation, passing their own access token and the victim's value:
POST https://<cloud-identity-provider>/
(self-service update-user-attributes operation)
{"access_token":"<attacker-own-token>",
"attributes":[{"name":"<identity-field>","value":"<victim-code>"}]} The provider returned an empty success response. The attacker then logged in again with their own email and password. The freshly minted token now carried the victim's identity value, even though the provider-internal account id stayed the attacker's own.
Assuming the Account
A call to the current-user endpoint with the new token returned the victim's account in full. From that point on, every authenticated editor API, profile, subscriptions, data-source content, member rosters, messages, and the change-and-approval workflow, served the session as the victim.
The Impact
The takeover was unconstrained. Any account on the platform could be assumed by changing a single value, and the access granted always matched the assumed identity exactly.
The chain was demonstrated end to end against two targets:
| Assumed Identity | Result |
|---|---|
| Peer editor (level 1) | Full session as the victim editor and their data sources |
| Region administrator (level 6) | Full session as a senior administrator, including their home data source and the member rosters of every region they managed |
The administrator takeover is the headline result: a fresh self-signup account harvested a region administrator's identity code from one ordinary in-product read, assumed it, and the backend served the session as that administrator. Because both the identity codes and the takeover mechanism were fully self-service, the attack required no secret the attacker did not already have access to, no privileged starting point, and no interaction from any victim. Selecting a transit-agency editor's code instead yielded takeover of that editor's account and tenant scope; only one value in the request changes.
Remediation
- Recreate the identity field at the cloud identity provider as immutable, and remove end-user write permission from the application client. Mutable custom attributes generally cannot be flipped to immutable in place, so a new attribute or a directory migration is required.
- Bind the application's notion of identity to the provider's immutable subject id, and map that subject to the internal user identity server-side. Never trust a user-writable attribute as the authenticated principal.
- Stop disclosing other users' internal identity codes in plaintext through ordinary roster and data-source endpoints.
- Invalidate existing sessions and audit attribute-update events for anomalous writes to the identity field.