The vulnerability allows a user with users.edit and api permissions to escalate their privileges by modifying their own permissions. The root cause was a missing authorization check in the application's permission handling logic.
The exploit is triggered by sending a PATCH request to the /api/v1/users/{id} endpoint, which is handled by the App\Http\Controllers\Api\UsersController::update method. This method, in turn, calls App\Actions\Permissions\PreserveUnauthorizedPrivilegedPermissionsAction::run to process the permission changes.
Prior to the patch, the run method did not verify whether a non-administrative user was attempting to alter their own permissions. This allowed a malicious user to craft a request to their own user ID and include any permissions they desired (except for admin and superuser).
The patch rectifies this by introducing a crucial check within PreserveUnauthorizedPrivilegedPermissionsAction::run. It now compares the authenticated user's ID with the target user's ID. If they match and the authenticated user is not an admin, the function aborts the permission change and returns the user's original, unmodified permissions. This same fix was applied to the web controller (App\Http\Controllers\Users\UsersController::update), closing a similar potential vulnerability in the web interface.