A AegiFlow
HIGHCVSS 8.8

GHSA-h4hf-v6w5-897x

Poweradmin: API user-update endpoint leads to a non-admin reset any user's password and take over the superuser account

Published
2026-07-24
Modified
2026-07-24
Sources
github-advisory

Summary

### Summary The REST API user-update endpoint (`PUT/PATCH /api/v2/users/{id}` and the V1 equivalent) does not enforce two authorization rules that the web interface enforces. A user who holds the `user_edit_others` permission but is not a superuser can: 1. edit user accounts that belong to a superuser, and 2. set the password of any account, even without the `user_passwd_edit_others` permission. Because of this, a non-admin "user manager" role can send a single API request that changes the administrator's password, then log in as the administrator. This is a full privilege escalation and account takeover. The same actions are explicitly blocked in the web UI, so the API is inconsistent with the application's own permission model. ### Details Poweradmin's permission model treats these as three distinct permissions: - `user_edit_others` (id 57): "User is allowed to edit other users." - `user_passwd_edit_others` (id 58): "User is allowed to edit the password of other users." - `user_is_ueberuser` (id 53): full admin. The existence of a separate `user_passwd_edit_others` permission means that "edit other users" is not supposed to include changing their passwords. The web UI enforces this, and it additionally forbids any non-superuser from editing a superuser account at all. The API skips both rules. **Where the API is missing the superuser check.** `lib/Domain/Service/ApiPermissionService.php`, `canEditUser()`: ```php public function canEditUser(int $userId, int $targetUserId): bool { if ($this->userHasPermission($userId, 'user_is_ueberuser')) { return true; } if ($userId === $targetUserId && $this->userHasPermission($userId, 'user_edit_own')) { return true; } // User with user_edit_others can edit ALL users, including superusers if ($this->userHasPermission($userId, 'user_edit_others')) { return true; } return false; } ``` There is no check on whether the target is a superuser. Compare this with the web controller `lib/Application/Controller/EditUserController.php` (lines 237 to 243), which explicitly refuses: ```php // Prevent non-superusers from editing superuser accounts (privilege escalation protection) $targetIsSuperuser = UserManager::isUserSuperuser($this->db, $editId); $currentIsSuperuser = UserManager::verifyPermission($this->db, 'user_is_ueberuser'); if ($targetIsSuperuser && !$currentIsSuperuser) { $this->showError(_('You do not have permission to edit a superuser account.')); } ``` The comment in the maintainers' own code calls this "privilege escalation protection." The API has no equivalent. **Where the API is missing the password permission check.** `lib/Domain/Service/UserManagementService.php`, `updateUser()` (lines 393 to 413) writes the password whenever one is supplied, with no permission check at all (it only refuses when the target is an external-auth user): ```php if (!empty($userData['password'])) { $user = $this->userRepository->getUserById($userId); $authMethod = $user['auth_method'] ?? 'sql'; $externalAuthMethods = ['oidc', 'saml', 'ldap']; if (in_array($authMethod, $externalAuthMethods, true)) { return [ 'success' => false, 'message' => '...', 'status' => 400 ]; } // Hash password if allowed userRepository->updateUser($userId, $userData); ``` Compare the web path `lib/Domain/Model/UserManager.php`, `editUser()`, which only writes the password column when the caller has the right permission: ```php $edit_own_perm = self::verifyPermission($this->db, 'user_edit_own'); $passwd_edit_others_perm = self::verifyPermission($this->db, 'user_passwd_edit_others'); if ($user_password != "" && ($edit_own_perm || $passwd_edit_others_perm)) { ... $query .= ", password = :password"; } ``` **The call path.** `lib/Application/Controller/Api/V2/UsersController.php`,

Affected packages

EcosystemPackageAffected versionsFixed versions
Packagistpoweradmin/poweradmin4.2.5, 4.3.4

Remediation: Upgrade to 4.2.5 or later.

References

Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.