What role bypass means
WordPress has a capability system: Subscriber, Author, Editor, Administrator. Each role has a set of allowed actions. The system works — when used correctly.
A role bypass vulnerability is a bug where an action that should require admin capability runs without checking. A user with no special privileges (or even an unauthenticated visitor) can trigger admin-level actions: install plugins, create users, modify content.
These bugs are extremely common in WordPress plugins. In 2025 alone, Wordfence/Patchstack disclosed 200+ role bypass CVEs.
This article is how we audit WordPress sites for these vulnerabilities and patch them.
How a role bypass looks in code
The vulnerable pattern:
add_action('wp_ajax_my_plugin_action', 'my_plugin_handler');
add_action('wp_ajax_nopriv_my_plugin_action', 'my_plugin_handler');
function my_plugin_handler() {
$option_name = $_POST['option'];
$option_value = $_POST['value'];
update_option($option_name, $option_value);
wp_send_json_success();
}Bugs: 1. wp_ajax_nopriv_ makes the action callable without auth at all 2. No current_user_can() check — any user reaching the endpoint can update any WP option 3. No nonce verification — CSRF attack possible 4. Direct $_POST without sanitization — XSS or worse
A subscriber-level user (or even a guest) sends:
POST /wp-admin/admin-ajax.php?action=my_plugin_action
option=siteurl&value=http://attacker.comWordPress's site URL is now the attacker's site. Game over.
The correct pattern
The same handler, secure:
add_action('wp_ajax_my_plugin_action', 'my_plugin_handler');
// NO wp_ajax_nopriv unless deliberately for guests
function my_plugin_handler() {
// Auth check
if (!current_user_can('manage_options')) {
wp_send_json_error('Insufficient permissions', 403);
}
// Nonce check
check_ajax_referer('my_plugin_action', 'nonce');
// Validate input
$option_name = sanitize_text_field($_POST['option']);
// Only allow safe options
$allowed_options = array('my_plugin_setting_1', 'my_plugin_setting_2');
if (!in_array($option_name, $allowed_options)) {
wp_send_json_error('Invalid option', 400);
}
$option_value = sanitize_text_field($_POST['value']);
update_option($option_name, $option_value);
wp_send_json_success();
}Three layers: capability check, nonce check, input whitelist.
Auditing your site
For each plugin, the audit asks:
- Does it expose AJAX endpoints? (
add_action('wp_ajax_*')) - Does it expose REST endpoints? (
register_rest_route()) - For each, is there a capability check?
- For each, is there a nonce check?
- Is user input sanitized?
Automated search:
cd /var/www/yoursite
# Find all AJAX endpoints
grep -rE "add_action\s*\(\s*['"]wp_ajax_[^'"]+['"]" wp-content/plugins/ > /tmp/ajax-endpoints.txt
# Find ones without current_user_can on the same handler
grep -rE "wp_ajax_nopriv_" wp-content/plugins/ > /tmp/nopriv-endpoints.txt
# Find REST endpoints
grep -rE "register_rest_route" wp-content/plugins/ > /tmp/rest-endpoints.txtFor each endpoint, open the handler function. Check whether:
if (!current_user_can('manage_options')) { wp_die(...); }…appears before any sensitive operation.
Common vulnerable patterns in 2026
Pattern 1 — Missing capability check on REST endpoint
register_rest_route('myplugin/v1', '/save-option', array(
'methods' => 'POST',
'callback' => 'my_save_option',
// missing 'permission_callback'
));Without permission_callback, the endpoint is callable by anyone.
Fix:
register_rest_route('myplugin/v1', '/save-option', array(
'methods' => 'POST',
'callback' => 'my_save_option',
'permission_callback' => function() {
return current_user_can('manage_options');
},
));Pattern 2 — Permission check on form display, not on action
The plugin's UI is only shown to admins (good), but the form's POST handler doesn't re-check (bad). A subscriber can send the POST directly.
Fix: every action handler must re-check capabilities, regardless of where the form came from.
Pattern 3 — Capability check for action exists, but check is `is_user_logged_in()`
A subscriber is logged in, so passes. Should be current_user_can('manage_options').
Fix: use the most restrictive capability the action needs.
Pattern 4 — Nonce check but no capability check
Nonce only prevents CSRF (attacker forcing your browser to send request). Doesn't prevent a malicious authenticated user from doing the action themselves.
Fix: both checks. Nonce for CSRF, capability for authorization.
Patching third-party plugins
For your own code: fix directly.
For third-party plugins: this is harder. Options:
Option A — Wait for vendor patch
Subscribe to Patchstack or Wordfence Intelligence for early disclosure. Vendors typically patch within 7-30 days of disclosure. WAF rules can block exploitation in the gap.
Option B — WAF rule block
Cloudflare WAF, Wordfence, Patchstack mitigations all support rule-based blocking of specific exploit patterns. For known CVEs, vendor-provided rules typically deploy within hours.
Option C — Manual patch
If the vulnerability is critical and patch is slow, you can edit plugin code directly. Document the change. Re-apply on plugin update (otherwise update reverts the patch).
Option D — Replace the plugin
For plugins with consistent vulnerability history, replace with a maintained alternative.
Monitoring for new vulnerabilities
Set up:
- Patchstack (or Wordfence Intelligence) — daily emails when CVEs are published for plugins you use
- WP-CLI cron to detect new admin users (potential signs of bypass exploitation):
0 * * * * cd /var/www/yoursite && wp user list --role=administrator --field=user_login > /tmp/admins.txt && diff /tmp/admins.prev.txt /tmp/admins.txt | mail -s "Admin changes" admin@yoursite.com; cp /tmp/admins.txt /tmp/admins.prev.txt- File integrity monitoring — alerts on new PHP files in
wp-content/mu-plugins/
Quick scan with WPScan
wpscan --url https://yoursite.com --enumerate vp,vt,u --api-token YOURTOKENThis enumerates plugins, themes, users and cross-references against the WPScan vulnerability database. Free tier supports 25 scans/day.
Common mistakes during role audit
- Trusting "I have Wordfence" — Wordfence scans for known CVEs, not unknown bugs in your custom code
- Only checking own code — third-party plugins are the bigger surface
- Not re-auditing after plugin update — new versions can introduce new bugs
- Treating Subscriber as low-risk — Subscribers can register on most sites; never trust their input
When to call a specialist
A code-level WordPress security audit is highly specialized. We audit plugins and custom code finding 5-15 issues per typical site, with detailed remediation.
Security audit within days. For active compromise see hacked website repair.

