Authorization disclaimer. This walkthrough is exclusively for the intentionally vulnerable PortSwigger Web Security Academy lab linked below. Only test systems you own or are explicitly authorized to assess.

Overview

The profile endpoint is intended to update an email address, but it binds additional client-supplied JSON properties to the user object. Its response reveals a roleid field, and adding "roleid": 2 to the update request promotes the normal lab account to administrator.

The objective defines administrator access as `roleid` 2 and requires promotion of the supplied `wiener` account before deleting `carlos`.
The objective defines administrator access as `roleid` 2 and requires promotion of the supplied `wiener` account before deleting `carlos`.

Lab objective/setup

Sign in with wiener:peter, obtain administrator privileges, open /admin, and delete the synthetic user carlos.

What I observed

Direct access to /admin was denied to the normal account. Updating the email address generated a JSON response containing the full user representation, including roleid. This both disclosed the privilege attribute and suggested that the endpoint might bind fields beyond the visible form.

Before changing the profile object, `/admin` refuses the `wiener` session with an administrator-only message.
Before changing the profile object, `/admin` refuses the `wiener` session with an administrator-only message.
A normal email update returns the complete account JSON, including the authorization field `roleid: 1`.
A normal email update returns the complete account JSON, including the authorization field `roleid: 1`.

Why it is vulnerable

The server maps an untrusted request body onto a privileged domain object without an allowlist. A client should be permitted to change only explicitly supported profile fields; authorization-sensitive properties such as roles must be controlled by a separate privileged workflow.

Walkthrough in the authorized PortSwigger lab

  1. Log in as wiener and capture POST /my-account/change-email.
  2. Add the role property to the JSON body while retaining a valid email value:
1
{"email":"[email protected]","roleid":2}
  1. Replay the request and confirm that the response reports role ID 2.
  2. Visit /admin and use the lab interface to delete carlos.
Adding `"roleid": 2` to the profile JSON is accepted, and the response confirms that the same account now has role ID `2`.
Adding `"roleid": 2` to the profile JSON is accepted, and the response confirms that the same account now has role ID `2`.
After the mass-assignment update, `/admin` opens in the existing session and provides the delete action for `carlos`.
After the mass-assignment update, `/admin` opens in the existing session and provides the delete action for `carlos`.
Deleting `carlos` produces the success message and solved banner, leaving only `wiener` in the Users list.
Deleting `carlos` produces the success message and solved banner, leaving only `wiener` in the Users list.

Verification

The same session is denied before the profile update and admitted after the response confirms roleid: 2. Deleting Carlos then causes the Academy to mark the lab solved, tying the outcome to the unauthorized role modification.

Impact and defensive lesson

Mass assignment of privilege fields can produce immediate administrator takeover. Use dedicated request DTOs or explicit field allowlists, ignore unknown properties, keep role management behind privileged endpoints, and avoid returning unnecessary internal account attributes.

References