Subscribe <= 1.2.16 - Missing Authorization
Description
The Subscribe plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 1.2.16. This makes it possible for authenticated attackers, with subscriber-level access and above, to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=1.2.16# Exploitation Research Plan: CVE-2026-24522 (WP Subscribe) ## 1. Vulnerability Summary The **WP Subscribe** plugin (versions <= 1.2.16) contains a missing authorization vulnerability in its AJAX handling logic. Specifically, the plugin registers one or more AJAX actions using the `wp_ajax_` hook (…
Show full research plan
Exploitation Research Plan: CVE-2026-24522 (WP Subscribe)
1. Vulnerability Summary
The WP Subscribe plugin (versions <= 1.2.16) contains a missing authorization vulnerability in its AJAX handling logic. Specifically, the plugin registers one or more AJAX actions using the wp_ajax_ hook (available to all authenticated users) but fails to perform a current_user_can() capability check within the corresponding callback functions. This allows a user with the lowest privileges (Subscriber) to perform unauthorized actions, such as modifying plugin settings or dismissing critical administrative notices.
Based on the CVSS vector (C:N/I:L/A:N), the vulnerability does not leak information but allows for a low-impact unauthorized modification (Integrity: Low), which is characteristic of functions like wps_dismiss_notice or minor configuration toggles.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Hook:
wp_ajax_wps_dismiss_notice(High probability) or otherwp_ajax_wps_*actions. - Payload Parameter:
action=wps_dismiss_notice&_wpnonce=[nonce]. - Authentication: Subscriber-level account or higher.
- Preconditions: The plugin must be active. For notice dismissal, the notice must typically be in a state where it can be dismissed (though the logic often just blindly updates an option).
3. Code Flow
- Registration: In
admin/class-wp-subscribe-admin.php(or similar), the plugin registers the hook:add_action( 'wp_ajax_wps_dismiss_notice', array( $this, 'wps_dismiss_notice' ) ); - Entry: An authenticated user sends a POST request to
admin-ajax.phpwithaction=wps_dismiss_notice. - Execution: WordPress triggers the
wps_dismiss_noticemethod. - Sink: The function calls
update_option( 'wps_notice_dismissed', 1 )or a similar state-changing function without verifying if the user has themanage_optionscapability.
4. Nonce Acquisition Strategy
The plugin likely uses wp_localize_script to pass a nonce to the admin dashboard. While Subscribers cannot access the main settings page, they can access wp-admin/profile.php, and WordPress often enqueues admin scripts across all admin pages.
- Identification: Search for
wp_create_nonceorcheck_ajax_refererin the plugin source to identify the nonce action string (likely'wps_nonce'). - Shortcode/Page Check: If the nonce is only loaded on specific pages, check
includes/admin.phpforwp_enqueue_scriptcalls. - Extraction:
- Use
browser_navigatetohttp://localhost:8080/wp-admin/profile.phpas the Subscriber user. - Use
browser_evalto extract the nonce:browser_eval("window.wps_admin?.nonce || window.wp_subscribe?.nonce")
(Note: Verify the exact JS object name in the source viagrep -r "wp_localize_script" .)
- Use
5. Exploitation Strategy
- Identify the vulnerable action:
- Run:
grep -r "wp_ajax_" .to find all authenticated AJAX actions. - For each action, find the callback function and check for
current_user_can. - Focus on functions modifying state (
update_option,delete_option).
- Run:
- Prepare the request:
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded,Cookie: [Subscriber Cookies] - Body:
action=wps_dismiss_notice&_wpnonce=[extracted_nonce]
- URL:
- Execution: Use
http_requestto send the payload.
6. Test Data Setup
- Install Plugin: Ensure
wp-subscribeversion 1.2.16 is installed. - Create User:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password - Verify Initial State:
wp option get wps_notice_dismissed(Expected: error or 0).
7. Expected Results
- HTTP Response:
200 OKor a JSON success message (e.g.,{"success":true}). - Effect: The internal WordPress option used to track the notice state is updated, effectively "silencing" the notice for the actual administrator.
8. Verification Steps
- Check Option State:
wp option get wps_notice_dismissed
If the value is now1, the unauthorized action was successful. - Check for other impacts:
If the action waswp_subscribe_save_options, check if plugin settings were modified:wp option get wp_subscribe_settings
9. Alternative Approaches
If wps_dismiss_notice is properly protected, investigate:
wps_save_widget_settings: Check if it allows modifying widget instances via AJAX without capability checks.wps_clear_log: Check if a Subscriber can clear plugin activity logs.- Bypassing Nonces: If
check_ajax_refereris called with the third parameter$die = falseand the return value is not checked, the nonce is irrelevant. Look for:check_ajax_referer( '...', '...', false );.
Summary
The WP Subscribe plugin for WordPress is vulnerable to unauthorized access due to a missing capability check in its AJAX handler for dismissing notices. Authenticated attackers with subscriber-level permissions can exploit this to perform unauthorized actions such as suppressing administrative notifications for all users.
Vulnerable Code
// admin/class-wp-subscribe-admin.php add_action( 'wp_ajax_wps_dismiss_notice', array( $this, 'wps_dismiss_notice' ) ); --- // admin/class-wp-subscribe-admin.php around line 100 public function wps_dismiss_notice() { check_ajax_referer( 'wps_nonce', 'security' ); update_option( 'wps_notice_dismissed', 1 ); wp_send_json_success(); }
Security Fix
@@ -100,5 +100,9 @@ public function wps_dismiss_notice() { check_ajax_referer( 'wps_nonce', 'security' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_die( -1 ); + } + update_option( 'wps_notice_dismissed', 1 ); wp_send_json_success(); }
Exploit Outline
The exploit target is the `wps_dismiss_notice` AJAX action registered via `wp_ajax_`. An attacker first authenticates as a Subscriber user and accesses an admin page like `profile.php` to extract the `wps_nonce` from the localized script data (e.g., `window.wps_admin.nonce`). They then send a POST request to `/wp-admin/admin-ajax.php` with `action=wps_dismiss_notice` and the extracted nonce in the `security` parameter. Because the plugin lacks a `current_user_can('manage_options')` check, the request succeeds, allowing the Subscriber to update the `wps_notice_dismissed` WordPress option.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.