The Field That Wasn't on the Form: XHack AI Escalated a Standard Member to Workspace Owner via Mass Assignment
Redacted (Team Collaboration SaaS, ~150 employees)
3 days
High
Severity< 48h
Time to DiscoveryWorkspace Owner
Privilege Gained8.8
CVSSv3 ScoreOn This Page
Key outcome
Mass-assignment privilege escalation surfaced autonomously by XHack AI and confirmed by human analysts. Role and tenant fields removed from the writable object; server-side field allow-listing applied to every update endpoint.
Table of Contents
17
Executive Summary
A mid-market team-collaboration SaaS, referred to throughout this document only as Redacted, engaged XHack for an AI-assisted security audit of their web application and API. The product is a workspace-based tool: each customer organisation is a workspace, and users within a workspace hold roles such as Member, Admin, or Owner that govern billing, member management, and data export.
During autonomous enumeration of the authenticated API, XHack AI observed that the JSON object returned by the "get my profile" endpoint included a role field, even though the user interface never displayed or offered to edit it. XHack AI then tested whether the corresponding "update my profile" endpoint would accept that same field on the way back in. It did. A standard Member could send their own role as owner in a routine profile-update request and the server would persist it, with no separate authorisation check on the field.
The finding was surfaced by XHack AI within the first two days of testing, triaged to High severity, and independently confirmed by the XHack human analyst team before disclosure. The client shipped a fix the following day.
No workspace data was accessed beyond the two test accounts provided, and no persistent privileged accounts were left behind.
Client Background
The client operates a subscription SaaS used by product, design, and engineering teams to plan and track work. Their customer base ranges from small teams to departments inside larger enterprises. A single workspace can contain hundreds of users and holds project data, internal documents, integration credentials, and billing information.
Because roles inside a workspace govern who can invite or remove members, change billing, and export the entire workspace's data, the boundary between Member and Owner is the single most important authorisation decision in the product. For commercial reasons the client cannot be identified beyond this description.
Scope of the Engagement
- Target: the production web application and its authenticated REST API
- Authentication levels tested: unauthenticated, standard Member, and Workspace Owner
- Testing approach: grey-box. XHack received two standard Member accounts inside a dedicated test workspace pre-populated with realistic dummy data.
- Out of scope: third-party integrations, the mobile applications, and billing-provider internals
Discovery: How XHack AI Found It
XHack AI began by mapping the authenticated API surface using one of the provided Member accounts. Rather than testing only the actions the interface offered, it recorded the full shape of every object the API returned, then compared what the API was willing to read against what it was willing to write.
The "get my profile" endpoint returned an object that looked, in sanitised form, like this:
GET /api/v2/me HTTP/1.1
Host: [redacted]
Authorization: Bearer <member-token>
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "usr_8f21",
"email": "[redacted]",
"name": "Test Member",
"role": "member",
"workspace_id": "ws_4a90",
"email_verified": true
}
The interface only ever let a user change their display name. But the object carried role, workspace_id, and email_verified — fields the UI never touched. XHack AI flagged this asymmetry automatically: a response object exposing security-relevant fields that the client never sends is a classic indicator of a mass-assignment weakness on the matching write endpoint.
XHack AI then issued the profile update the interface would send for a name change, but added a single extra field the interface never includes:
PATCH /api/v2/me HTTP/1.1
Host: [redacted]
Authorization: Bearer <member-token>
Content-Type: application/json
{ "name": "Test Member", "role": "owner" }
The server responded 200 OK and echoed the updated object with "role": "owner". A follow-up GET /api/v2/me confirmed the change had persisted. The account now held Owner-level permissions across the entire workspace — member management, billing, and full data export — reached from a standard Member session with one request and no interface path.
Why This Happened
The update handler bound the incoming JSON body onto the user record wholesale — the common update(request_body) / Object.assign(user, body) pattern — instead of copying only an explicit allow-list of user-editable fields. Authentication was correct: the request was properly tied to the logged-in Member. What was missing was object property-level authorisation: the server never asked whether this user was permitted to modify that specific field. name and role were treated identically, so a field the user should never control was writable simply because it existed on the model.
The interface never sent role, so in normal use the flaw was invisible. It became exploitable the moment anyone added the field by hand — which is exactly what an attacker, or an autonomous agent probing the write surface, does.
Technical Breakdown
| Property | Detail |
|---|---|
| Vulnerability Class | Mass Assignment / Broken Object Property Level Authorization |
| OWASP API Top 10 | API3:2023 Broken Object Property Level Authorization |
| OWASP Top 10 | A01:2021 Broken Access Control |
| CWE | CWE-915 Improperly Controlled Modification of Dynamically-Determined Object Attributes |
| CVSSv3 Score | 8.8 (High) |
| Attack Vector | Network |
| Authentication Required | Yes (any standard Member) |
| Privilege Required | Low |
| User Interaction Required | None |
| Impact | Full self-promotion to Workspace Owner; workspace-wide member, billing, and export control |
Triage and Confirmation
XHack AI flagged the finding for human review at High severity. The XHack analyst team validated it independently:
- Reproduced the escalation on the second, separately provisioned Member account, confirming the flaw was systemic rather than specific to one test user.
- Enumerated the writable field set by replaying the update with each security-relevant field observed in the read response.
roleandemail_verifiedwere both writable;workspace_idwas writable but rejected downstream by a foreign-key constraint, so cross-workspace movement was not achievable through this endpoint. - Mapped the blast radius of Owner to confirm what the promoted account could actually do: invite and remove members, change the workspace's billing plan, and trigger a full workspace data export.
- Checked
email_verifiedabuse — flipping it totrueon an unverified account bypassed the email-verification gate, a secondary finding rolled into the same report. - Reviewed available logs for prior use of the
rolefield on the update endpoint by non-Owner accounts. No evidence of prior exploitation was found.
The vulnerability was confirmed valid, reliably reproducible, and High in severity.
Impact Assessment
Confidentiality
- An Owner can export the entire workspace: projects, documents, and stored integration references. Any Member could grant themselves that access.
Integrity
- Owner rights include removing other members and reassigning roles, allowing an attacker to lock legitimate Owners out of their own workspace and establish a durable privileged foothold.
- The writable
email_verifiedfield let an attacker satisfy a trust gate the application assumed only its own mail flow could set.
Availability
- Member-management and billing controls could be used to disrupt a workspace — removing users or downgrading the plan — as a targeted denial of service against a specific customer organisation.
Regulatory Exposure
- Workspaces belonging to business customers contain personal data of that customer's own staff. Unauthorised export would likely constitute a reportable personal-data breach under GDPR Article 33, with a 72-hour notification obligation.
Remediation
Immediate (Completed Next Day)
- Replace wholesale body-binding with an explicit allow-list. The update handler now copies only
name(and other genuinely user-editable fields) from the request. Any other field in the body is ignored, not written. - Move
rolechanges to a dedicated, authorised endpoint that verifies the caller already holds Owner rights before changing anyone's role — including a request to change one's own. - Stop returning
role,workspace_id, andemail_verifiedfrom the profile read endpoint unless the caller is authorised to see them, reducing the signal an attacker gets for free.
Short-Term (Within One Week)
- Audit every write endpoint for the same binding pattern. Two others (a workspace-settings update and a member-invite handler) accepted fields they should not have and were tightened in the same release.
- Introduce serializer-level input schemas so each endpoint declares exactly which fields it accepts, and unexpected fields are rejected rather than silently bound.
- Add automated tests that send known-forbidden fields (
role,is_admin,email_verified) to every update endpoint and fail the build if any is accepted.
Recommended Longer-Term Controls
- Treat object property-level authorisation as a first-class rule: every field on a model is either explicitly user-editable or it is not, and the framework enforces the distinction.
- Run periodic AI-assisted API regression scans against staging, since mass-assignment regressions reappear whenever a new field is added to a shared model.
Outcome
The client deployed the allow-list fix the day after disclosure and completed the wider endpoint audit within the week. A re-test by XHack confirmed that the update endpoint now silently ignores role and every other non-editable field, that role changes flow only through the authorised endpoint, and that the new input schemas reject unexpected fields outright.
No evidence of prior exploitation was found. The client's engineering team noted that the profile model had grown over time and that the update handler had bound the whole request body since the earliest version, long before role was ever added to the model.
This case reflects one of XHack AI's structural strengths: it does not test only the actions the interface offers. It records the full shape of every object the API returns and systematically probes the gap between what an application is willing to reveal and what it is willing to accept. Mass assignment lives precisely in that gap, and it is easy to miss in an engagement scoped to the visible UI. XHack AI surfaced it in under 48 hours.
Engagement details
Redacted (Team Collaboration SaaS, ~150 employees)
SaaS / Productivity
AI-Assisted Security Audit
3 days
2026
Start your engagement
Run an AI-Powered Audit
XHack delivers the same rigorous methodology behind every case study. Let us pressure-test your defences.
Run an AI-Powered Audit