Membership For WooCommerce <= 3.0.3 - Unauthenticated Insecure Direct Object Reference
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:NTechnical Details
<=3.0.3Source Code
WordPress.org SVN# 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) ormwb_wpm_membership_status_change. - Vulnerable Parameter:
membership_idorid. - 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)
- The plugin registers an unauthenticated AJAX hook:
add_action( 'wp_ajax_nopriv_mwb_wpm_cancel_membership', array( $this, 'mwb_wpm_cancel_membership' ) ); - The handler function
mwb_wpm_cancel_membershipis triggered. - The function retrieves the membership ID from the request:
$membership_id = $_POST['membership_id']; - The function calls a status update function (e.g.,
update_post_metaor a plugin-specific wrapper) to change the membership status to 'cancelled'. - 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.
- 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]. - Create Test Page:
wp post create --post_type=page --post_title="Membership Dashboard" --post_status=publish --post_content='[mwb_wpm_dashboard]' - Navigate and Extract:
- Use
browser_navigateto view the newly created page. - Use
browser_evalto 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")
- Use
5. Exploitation Strategy
- Preparation: Identify a target membership ID. Since memberships are custom post types, they follow standard WordPress ID incrementing.
- 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]
- Alternative Action: If
mwb_wpm_cancel_membershipis not the hook, try:mwb_wpm_pause_membershipmwb_wpm_membership_actionwith astatus=cancelparameter.
6. Test Data Setup
- Install Plugin: Ensure
membership-for-woocommerceversion 3.0.3 is active. - Create Membership Plan:
wp post create --post_type=mwb_wpm_plan --post_title="Gold Plan" --post_status=publish
- 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).
- Create a test user:
- 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_statusor a specificpost_meta(likemwb_wpm_membership_status) of the target membership record will change fromactivetocancelled.
8. Verification Steps
- Check Meta via CLI:
wp post meta get [TARGET_ID] mwb_wpm_membership_status - Check Post Status:
wp post get [TARGET_ID] --field=post_status - 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_keyorcancel_keyin the source code ofincludes/class-membership-for-woocommerce-public.php. - Status Toggle: If cancellation is not the direct target, check for an
mwb_wpm_toggle_statusaction which might switch a membership betweenactiveandpaused.
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.