CVE-2026-24522

Subscribe <= 1.2.16 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.2.16
PublishedJanuary 26, 2026
Last updatedFebruary 2, 2026
Affected pluginwp-subscribe
Research Plan
Unverified

# 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 other wp_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

  1. 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' ) );
  2. Entry: An authenticated user sends a POST request to admin-ajax.php with action=wps_dismiss_notice.
  3. Execution: WordPress triggers the wps_dismiss_notice method.
  4. Sink: The function calls update_option( 'wps_notice_dismissed', 1 ) or a similar state-changing function without verifying if the user has the manage_options capability.

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.

  1. Identification: Search for wp_create_nonce or check_ajax_referer in the plugin source to identify the nonce action string (likely 'wps_nonce').
  2. Shortcode/Page Check: If the nonce is only loaded on specific pages, check includes/admin.php for wp_enqueue_script calls.
  3. Extraction:
    • Use browser_navigate to http://localhost:8080/wp-admin/profile.php as the Subscriber user.
    • Use browser_eval to extract the nonce:
      browser_eval("window.wps_admin?.nonce || window.wp_subscribe?.nonce")
      (Note: Verify the exact JS object name in the source via grep -r "wp_localize_script" .)

5. Exploitation Strategy

  1. 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).
  2. 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]
  3. Execution: Use http_request to send the payload.

6. Test Data Setup

  1. Install Plugin: Ensure wp-subscribe version 1.2.16 is installed.
  2. Create User:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password
  3. Verify Initial State:
    wp option get wps_notice_dismissed (Expected: error or 0).

7. Expected Results

  • HTTP Response: 200 OK or 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

  1. Check Option State:
    wp option get wps_notice_dismissed
    If the value is now 1, the unauthorized action was successful.
  2. Check for other impacts:
    If the action was wp_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_referer is called with the third parameter $die = false and the return value is not checked, the nonce is irrelevant. Look for: check_ajax_referer( '...', '...', false );.
Research Findings
Static analysis — not yet PoC-verified

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

--- a/admin/class-wp-subscribe-admin.php
+++ b/admin/class-wp-subscribe-admin.php
@@ -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.