🛠️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_rolesand 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()withoutcurrent_user_can()andwp_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