CVE-2025-67909

Membership For WooCommerce <= 3.0.3 - Unauthenticated Insecure Direct Object Reference

mediumAuthorization Bypass Through User-Controlled Key
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
3.0.4
Patched in
13d
Time to patch

Description

The Membership For WooCommerce – WordPress Membership Plugin, Restrict Content, Build Online Communities, Paywall & Content Dripping plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 3.0.3 due to missing validation on a user controlled key. This makes it possible for unauthenticated attackers to perform an unauthorized action.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.0.3
PublishedDecember 24, 2025
Last updatedJanuary 5, 2026

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2025-67909 - Membership For WooCommerce IDOR ## 1. Vulnerability Summary The **Membership For WooCommerce** plugin (versions <= 3.0.3) contains an Insecure Direct Object Reference (IDOR) vulnerability. This flaw exists because the plugin's unauthenticated AJAX handlers (or publ…

Show full research plan

Research Plan: CVE-2025-67909 - Membership For WooCommerce IDOR

1. Vulnerability Summary

The Membership For WooCommerce plugin (versions <= 3.0.3) contains an Insecure Direct Object Reference (IDOR) vulnerability. This flaw exists because the plugin's unauthenticated AJAX handlers (or public-facing endpoints) perform sensitive actions (like membership cancellation or status modification) based on a user-supplied membership ID or "key" without verifying that the requester is authorized to modify that specific object. An unauthenticated attacker can exploit this to perform unauthorized actions on any membership record by guessing or enumerating the identifier.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: mwb_wpm_cancel_membership (inferred based on plugin naming conventions and "unauthorized action" description) or mwb_wpm_membership_status_change.
  • Vulnerable Parameter: membership_id or id.
  • Authentication: None required (uses wp_ajax_nopriv_ hooks).
  • Preconditions: A membership record (Post Type: mwb_wpm_membership) must exist in the system.

3. Code Flow (Inferred)

  1. The plugin registers an unauthenticated AJAX hook:
    add_action( 'wp_ajax_nopriv_mwb_wpm_cancel_membership', array( $this, 'mwb_wpm_cancel_membership' ) );
  2. The handler function mwb_wpm_cancel_membership is triggered.
  3. The function retrieves the membership ID from the request:
    $membership_id = $_POST['membership_id'];
  4. The function calls a status update function (e.g., update_post_meta or a plugin-specific wrapper) to change the membership status to 'cancelled'.
  5. The Critical Flaw: The code fails to check if the current user (in this case, an anonymous guest) is the owner of the membership associated with $membership_id.

4. Nonce Acquisition Strategy

The plugin likely uses a nonce for its AJAX requests, localized via wp_localize_script.

  1. Identify Shortcode: The plugin uses shortcodes to display the user dashboard where cancellation buttons appear. The most likely shortcode is [mwb_wpm_membership_dashboard] or [mwb_wpm_dashboard].
  2. Create Test Page:
    wp post create --post_type=page --post_title="Membership Dashboard" --post_status=publish --post_content='[mwb_wpm_dashboard]'
  3. Navigate and Extract:
    • Use browser_navigate to view the newly created page.
    • Use browser_eval to extract the nonce from the localization object.
    • JS Object Name: Likely mwb_wpm_public_obj.
    • Nonce Key: Likely mwb_wpm_nonce.
    • Execution: browser_eval("window.mwb_wpm_public_obj?.mwb_wpm_nonce")

5. Exploitation Strategy

  1. Preparation: Identify a target membership ID. Since memberships are custom post types, they follow standard WordPress ID incrementing.
  2. Request Construction:
    • Method: POST
    • URL: http://<target>/wp-admin/admin-ajax.php
    • Content-Type: application/x-www-form-urlencoded
    • Payload:
      action=mwb_wpm_cancel_membership&membership_id=[TARGET_ID]&nonce=[EXTRACTED_NONCE]
      
  3. Alternative Action: If mwb_wpm_cancel_membership is not the hook, try:
    • mwb_wpm_pause_membership
    • mwb_wpm_membership_action with a status=cancel parameter.

6. Test Data Setup

  1. Install Plugin: Ensure membership-for-woocommerce version 3.0.3 is active.
  2. Create Membership Plan:
    • wp post create --post_type=mwb_wpm_plan --post_title="Gold Plan" --post_status=publish
  3. Create Target Membership:
    • Create a test user: wp user create victim victim@example.com --role=subscriber
    • Create a membership record for the victim:
      wp post create --post_type=mwb_wpm_membership --post_title="Victim Membership" --post_status=publish --post_author=[VICTIM_UID]
    • Note the ID of the created membership (e.g., 123).
  4. Set Initial Status: Ensure the membership is 'active'.
    wp post admin-meta set 123 mwb_wpm_membership_status active (meta key inferred).

7. Expected Results

  • The server should return a JSON response, likely {"success": true} or a message like "Membership cancelled successfully".
  • The post_status or a specific post_meta (like mwb_wpm_membership_status) of the target membership record will change from active to cancelled.

8. Verification Steps

  1. Check Meta via CLI:
    wp post meta get [TARGET_ID] mwb_wpm_membership_status
  2. Check Post Status:
    wp post get [TARGET_ID] --field=post_status
  3. Confirm the status has changed despite the request being unauthenticated.

9. Alternative Approaches

  • Enumeration: If the specific ID is unknown, iterate through recent post IDs.
  • Direct Parameter Manipulation: If the "key" mentioned in the CVE is not the ID, look for parameters like mwb_wpm_key or cancel_key in the source code of includes/class-membership-for-woocommerce-public.php.
  • Status Toggle: If cancellation is not the direct target, check for an mwb_wpm_toggle_status action which might switch a membership between active and paused.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Membership For WooCommerce plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) in versions up to 3.0.3 due to missing validation on user-controlled keys in its AJAX handlers. This allows unauthenticated attackers to perform unauthorized actions, such as membership cancellation, on any record by supplying the target membership ID. The vulnerability stems from the use of unauthenticated AJAX hooks without corresponding ownership or capability checks.

Exploit Outline

The vulnerability is exploited by sending an unauthenticated POST request to /wp-admin/admin-ajax.php. The attacker first extracts a required nonce from the mwb_wpm_public_obj localized script variable found on pages using the plugin's dashboard shortcodes. Using this nonce, the attacker invokes actions like mwb_wpm_cancel_membership or mwb_wpm_membership_status_change while providing a target membership_id. Because the plugin fails to verify that the current user owns the specified membership, it processes the request and modifies the status of the target account.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.