🛠️Part IV – Post-Exploitation

Privilege escalation via stale role restoration and missing authorization (ASE "View Admin as Role")

Many plugins implement a "view as role" or temporary role-switching feature by saving the original role(s) in user meta so they can be restored later. If the restoration path relies only on request parameters (e.g., $_REQUEST['reset-for']) and a plugin-maintained list without checking capabilities and a valid nonce, this becomes a vertical privilege escalation.

A real-world example was found in the Admin and Site Enhancements (ASE) plugin (≤ 7.6.2.1). The reset branch restored roles based on reset-for=<username> if the username appeared in an internal array $options['viewing_admin_as_role_are'], but performed neither a current_user_can() check nor a nonce verification before removing current roles and re-adding the saved roles from user meta _asenha_view_admin_as_original_roles:

// Simplified vulnerable pattern
if ( isset( $_REQUEST['reset-for'] ) ) {
    $reset_for_username = sanitize_text_field( $_REQUEST['reset-for'] );
    $usernames = get_option( ASENHA_SLUG_U, [] )['viewing_admin_as_role_are'] ?? [];

    if ( in_array( $reset_for_username, $usernames, true ) ) {
        $u = get_user_by( 'login', $reset_for_username );
        foreach ( $u->roles as $role ) { $u->remove_role( $role ); }
        $orig = (array) get_user_meta( $u->ID, '_asenha_view_admin_as_original_roles', true );
        foreach ( $orig as $r ) { $u->add_role( $r ); }
    }
}

Why it’s exploitable

  • Trusts $_REQUEST['reset-for'] and a plugin option without server-side authorization.

  • If a user previously had higher privileges saved in _asenha_view_admin_as_original_roles and was downgraded, they can restore them by hitting the reset path.

  • In some deployments, any authenticated user could trigger a reset for another username still present in viewing_admin_as_role_are (broken authorization).

Attack prerequisites

  • Vulnerable plugin version with the feature enabled.

  • Target account has a stale high-privilege role stored in user meta from earlier use.

  • Any authenticated session; missing nonce/capability on the reset flow.

Exploitation (example)

On vulnerable builds this removes current roles and re-adds the saved original roles (e.g., administrator), effectively escalating privileges.

Detection checklist

  • Look for role-switching features that persist “original roles” in user meta (e.g., _asenha_view_admin_as_original_roles).

  • Identify reset/restore paths that:

    • Read usernames from $_REQUEST / $_GET / $_POST.

    • Modify roles via add_role() / remove_role() without current_user_can() and wp_verify_nonce() / check_admin_referer().

    • Authorize based on a plugin option array (e.g., viewing_admin_as_role_are) instead of the actor’s capabilities.

Hardening

  • Enforce capability checks on every state-changing branch (e.g., current_user_can('manage_options') or stricter).

  • Require nonces for all role/permission mutations and verify them: check_admin_referer() / wp_verify_nonce().

  • Never trust request-supplied usernames; resolve the target user server-side based on the authenticated actor and explicit policy.

  • Invalidate “original roles” state on profile/role updates to avoid stale high-privilege restoration:

  • Consider storing minimal state and using time-limited, capability-guarded tokens for temporary role switches.

Last updated