GHSA-H4HF-V6W5-897X

GHSA-H4HF-V6W5-897X is a high-severity missing authorization vulnerability in poweradmin/poweradmin (composer), affecting versions >= 4.0.0, < 4.2.5. It is fixed in 4.2.5, 4.3.4.

Does this CVE actually affect you?

Kodem shows which CVEs are reachable and running in your applications, so you fix what's exploitable, not just what's listed.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Runtime intelligence, not another scanner.

Summary

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

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():

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:

// 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):

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  <-- no permission check happens here
    $userData['password'] = password_hash($userData['password'], PASSWORD_DEFAULT);
}
$success = $this->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:

$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, updateUser() (lines 701 to 727):

if (!$this->apiPermissionService->canEditUser($currentUserId, $targetUserId)) {   // line 708
    ... 403 ...
}
$result = $this->userManagementService->updateUser($targetUserId, $input);         // line 727

So canEditUser returns true for any user_edit_others holder against any target, and updateUser then writes the password with no further check. The V1 controller (lib/Application/Controller/Api/V1/UsersController.php) has the same shape.

Note: perm_templ is separately protected (a PermissionTemplateAssignmentGuard rejects it unless the caller has user_edit_templ_perm), so an attacker cannot elevate their own template through the API. They do not need to. Resetting the administrator's password and logging in as the administrator achieves full control directly.

PoC

Tested on Poweradmin master (commit 7f28c3a97) and the code path is present in the 4.0.x, 4.1.x, 4.2.x and 4.3.x release branches.

Configuration required (both are common in real deployments, neither is the default):

  • The REST API must be enabled (api.enabled = true in config/settings.php). The API is intended for automation such as the Terraform provider, so it is frequently turned on.
  • A delegated "user manager" role must exist: a permission template that grants user_edit_others (and typically user_view_others) but not user_is_ueberuser and not user_passwd_edit_others. This is a normal way to let a helpdesk or team lead manage user accounts.

Setup (performed once by the administrator to create the delegated role and the attacker):

  1. As admin, create a permission template named "UserMgr" of type "user" with the permissions "User is allowed to edit other users" and "User is allowed to see other users and their details" only.
  2. Create a normal user, for example umgr, and assign it the UserMgr template.
  3. As umgr, create an API key from the user's API keys page. Call it, for example, pwa_umgr_key. umgr is now the attacker: a non-admin user with a self-scoped API key.

Exploit request (Burp Suite Repeater). Send this single request. id 1 is the administrator account.

PUT /api/v2/users/1 HTTP/1.1
Host: TARGET_HOST
X-API-Key: pwa_umgr_key
Content-Type: application/json
Content-Length: 30

{"password":"NewAdminPass1!"}

Expected response (HTTP 200):

{"success":true,"data":{"user_id":1},"message":"User updated successfully"}

now we can login to admin user with a new password!

The page refuses with "You do not have permission to edit a superuser account." So the same user with the same permission is denied on the web but allowed on the API.

Record ids are the normal sequential user ids, and the administrator is usually id 1, so no guessing is required. The attacker can also change username, email, active and use_ldap on any account through the same request.

Disclosure

Credit: Found by Saif Salah (https://saifsalah.github.io/)
Found during a security review of Poweradmin. Happy to coordinate a fix timeline and CVE before any public write-up.

Impact

This is a broken access control and privilege escalation issue (CWE-285 Improper Authorization, CWE-266 Incorrect Privilege Assignment).

Who is impacted: any Poweradmin installation that has the REST API enabled and that has created at least one non-admin role holding user_edit_others (a delegated user-manager or helpdesk role). On such an installation, any holder of that role, using nothing more than their own API key, can reset the superuser's password and take over the administrator account. From there they control every zone, record, user and setting in Poweradmin, and, through the PowerDNS backend, the DNS data itself.

The severity is high because the outcome is full administrative takeover from a low-privilege authenticated position, over the network, with a single request and no user interaction. The reason it is not rated critical outright is the precondition that a delegated user_edit_others role exists and the API is enabled. The underlying defect is real regardless of configuration: the API does not enforce the superuser-edit protection or the user_passwd_edit_others permission that the web interface enforces, so the API grants more authority than the permission model intends.

Suggested fix: mirror the web guards in the API. In ApiPermissionService::canEditUser, reject a non-superuser editing a superuser target. In UserManagementService::updateUser, only write the password when the caller is editing their own account or holds user_passwd_edit_others. Apply the same to both V1 and V2 controllers.

The application does not perform an authorization check before performing a sensitive operation. Typical impact: unauthorized access to restricted functionality or data.

GHSA-H4HF-V6W5-897X has a CVSS score of 8.8 (High). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (4.2.5, 4.3.4); upgrading removes the vulnerable code path.

Affected versions

poweradmin/poweradmin (>= 4.0.0, < 4.2.5) poweradmin/poweradmin (>= 4.3.0, < 4.3.4)

Security releases

poweradmin/poweradmin → 4.2.5 (composer) poweradmin/poweradmin → 4.3.4 (composer)

Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.

Already deployed Kodem?

See it in your environmentNew to Kodem? Get a demo →

Remediation advice

Upgrade the following packages to resolve this vulnerability:

poweradmin/poweradmin to 4.2.5 or later; poweradmin/poweradmin to 4.3.4 or later

Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.

Frequently Asked Questions

  1. What is GHSA-H4HF-V6W5-897X? GHSA-H4HF-V6W5-897X is a high-severity missing authorization vulnerability in poweradmin/poweradmin (composer), affecting versions >= 4.0.0, < 4.2.5. It is fixed in 4.2.5, 4.3.4. The application does not perform an authorization check before performing a sensitive operation.
  2. How severe is GHSA-H4HF-V6W5-897X? GHSA-H4HF-V6W5-897X has a CVSS score of 8.8 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
  3. Which versions of poweradmin/poweradmin are affected by GHSA-H4HF-V6W5-897X? poweradmin/poweradmin (composer) versions >= 4.0.0, < 4.2.5 is affected.
  4. Is there a fix for GHSA-H4HF-V6W5-897X? Yes. GHSA-H4HF-V6W5-897X is fixed in 4.2.5, 4.3.4. Upgrade to this version or later.
  5. Is GHSA-H4HF-V6W5-897X exploitable, and should I be worried? Whether GHSA-H4HF-V6W5-897X is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
  6. What actually determines whether GHSA-H4HF-V6W5-897X is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
  7. How do I fix GHSA-H4HF-V6W5-897X?
    • Upgrade poweradmin/poweradmin to 4.2.5 or later
    • Upgrade poweradmin/poweradmin to 4.3.4 or later

Other vulnerabilities in poweradmin/poweradmin

Stop the waste.
Protect your environment with Kodem.