Skip to content

Missing admin-target guard in UsersController::actionActivateUser can lead to permission escalation

Moderate
angrybrad published GHSA-jqf5-vfg6-8cx5 Aug 25, 2026

Package

composer craftcms/cms (Composer)

Affected versions

>= 5.0.0-RC1, < 5.10.12

Patched versions

5.10.12

Description

A user with administrateUsers permissions can activate a deactivated administrator account.

This requires a control panel user account with administrateUsers permissions.

$this-&gt;userActionChecks();
$this-&gt;requirePermission('administrateUsers');   // 1079
$userId = $this-&gt;request-&gt;getRequiredBodyParam('userId'); // 1082
$user = Craft::$app-&gt;getUsers()-&gt;getUserById($userId);    // 1083
Craft::$app-&gt;getUsers()-&gt;activateUser($user);             // 1090  — no admin guard

The mirror action actionDeactivateUser (2199-2200) explicitly does if ($user-&gt;admin) $this-&gt;requireAdmin(false);. actionActivateUser does not.

A non-admin with administrateUsers can activate a pending/inactive admin account. When combined with activating and then resetting an admin’s password, or resurrecting a deliberately deactivated admin, this creates a privilege gap and breaks the symmetry between deliberate activation and deactivation.

Data flow

  1. POST users/activate-user, userId=<admin id>.
  2. Guard: administrateUsers (non-admin grantable). No admin-target check.
  3. Sink: activateUser($user) activates the admin account.

Report ID: 1569

Severity

Moderate

CVE ID

No known CVE

Weaknesses

Missing Authorization

The product does not perform an authorization check when an actor attempts to access a resource or perform an action. Learn more on MITRE.