Coordinated Disclosure Timeline

Summary

Umbraco CMS is vulnerable to an authorization bypass issue (GHSL-2026-065) that allows unauthorized users to assign users to groups, potentially leading to privilege escalation and improper access control.

Project

Umbraco CMS

Tested Version

v17.2.0

Details

Missing Authorization Check for Group Assignment (GHSL-2026-065)

The UpdateUserGroupsUserController.UpdateUserGroups method performs the following authorization check:

AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
    User,
    UserPermissionResource.WithKeys(requestModel.UserIds.Select(x => x.Id)),
    AuthorizationPolicies.UserPermissionByResource);

Missing Validation:

The authorization check validates whether the current user can modify the TARGET users, but it does NOT validate whether the current user can assign the specified USER GROUPS.

Comparison with UpdateUserController:

The single-user update endpoint (UpdateUserController) includes proper group assignment validation via UserEditorAuthorizationHelper.IsAuthorized (called in UserService), which checks:

However, UpdateUserGroupsUserController bypasses this validation entirely and calls UserGroupService.UpdateUserGroupsOnUsersAsync directly, which only validates that the admin group won’t become empty (UserGroupService) but does NOT check which groups can be assigned.

Attack Scenario:

  1. Attacker Profile: Authenticated backoffice API user with SectionAccessUsers permission (access to Users section), but NOT an admin. It looks like the only group with access to Users section by default is Administrators, but Umbraco allows creation of custom new groups.
  2. Target: The attacker’s own API user account (or another non-API non-admin user).
  3. Attack Steps:
    POST /umbraco/management/api/v1/user/set-user-groups HTTP/1.1
    Authorization: Bearer <attacker's token>
    Content-Type: application/json
    
    {
      "userIds": [
        { "id": "<target's user GUID>" }
      ],
      "userGroupIds": [
        { "id": "<admin group GUID>" },
        { "id": "<other groups...>" }
      ]
    }
    

    The admin group GUID is a well-known constant: Constants.Security.AdminGroupKey, but also can be retrieved by GET /umbraco/management/api/v1/user-group API call. User GUID can be retrieved by GET /umbraco/management/api/v1/user API call.

  4. Result: Attacker’s account is added to the admin group. Note that for non-API user with the same permissions is allowed to change some user properties, but it is not allowed to perform the group assignment, i.e. it is correctly restricted.

Impact

This issue allows authenticated non-admin API users to gain admin privileges.

CWEs

Credit

This issue was discovered with the GitHub Security Lab Taskflow Agent and manually verified by GHSL team member @JarLob (Jaroslav Lobačevski).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2026-065 in any communication regarding this issue.