Skip to content

[Security] Missing Authorization on Password Change Endpoint Allows Any User to Take Over Any Account #1623

Description

@geo-chen

Summary

The POST /users/:id/change-password endpoint accepts the target user's ID from the URL path but performs no authorization check to verify that the requesting user is either the account owner or an administrator. Any authenticated user with the lowest-privilege (normal) role can change the password of any other user, including the administrator, effectively taking over arbitrary accounts.

Details

The handler PostUserChangePassword is defined in core/controllers/user_v2.go:

func PostUserChangePassword(c *gin.Context) {
    // get id -- taken directly from URL, no ownership check
    id, err := primitive.ObjectIDFromHex(c.Param("id"))
    ...
    // get user (authenticated requester, used only for audit timestamp)
    u := GetUserFromContextV2(c)
    modelSvc := service.NewModelServiceV2[models.UserV2]()

    // update password for the URL-supplied id, no check that id == u.Id or u.Role == admin
    user, err := modelSvc.GetById(id)
    ...
    user.Password = utils.EncryptMd5(payload.Password)
    if err := modelSvc.ReplaceById(user.Id, *user); err != nil {
        ...
    }
    HandleSuccess(c)
}

The function retrieves the authenticated caller via GetUserFromContextV2(c) (used only to set the updatedBy audit field), then loads and replaces the password of the account identified by :id in the URL. There is no comparison between u.Id and id, and no check for u.Role == "admin". The route is registered on groups.AuthGroup (requires a valid JWT) but any valid JWT suffices regardless of the holder's role.

The GET /users endpoint is likewise accessible to all authenticated users and returns the full list of user records including their ObjectIDs, making it trivial for a low-privileged attacker to enumerate and target any account.

Additionally, POST /users (create user) has no role check either. A normal user can supply "role":"admin" in the request body and create a new administrator account, which is a parallel privilege-escalation path.

PoC

(available on request)

Impact

Any authenticated user, regardless of role, can permanently take over the administrator account or any other account by resetting its password. Combined with Crawlab's ability to execute arbitrary spider scripts on worker nodes, account takeover of the administrator leads to full remote code execution across the cluster. In multi-tenant or team deployments this also allows horizontal takeover of any peer account.

Affected Versions: confirmed on v0.6.3
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CWE: CWE-639 -- Authorization Bypass Through User-Controlled Key

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions