Subscription for WooCommerce – WordPress Recurring Payments Plugin <= 1.8.10 - Authenticated (Customer+) Insecure Direct Object Reference
Description
The Subscription for WooCommerce – WordPress Recurring Payments Plugin plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 1.8.10 due to missing validation on a user controlled key. This makes it possible for authenticated attackers, with Custom-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.8.10What Changed in the Fix
Changes introduced in v1.8.11
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-69347 (Insecure Direct Object Reference) ## 1. Vulnerability Summary The **Subscription for WooCommerce** plugin (versions <= 1.8.10) contains an Insecure Direct Object Reference (IDOR) vulnerability in its subscription action handling logic. The `SpringDevs\S…
Show full research plan
Exploitation Research Plan: CVE-2025-69347 (Insecure Direct Object Reference)
1. Vulnerability Summary
The Subscription for WooCommerce plugin (versions <= 1.8.10) contains an Insecure Direct Object Reference (IDOR) vulnerability in its subscription action handling logic. The SpringDevs\Subscription\Frontend\ActionController::control_action_subscrpt function processes requests to modify subscription statuses (cancel, reactivate, toggle auto-renewal) based on a user-provided subscrpt_id.
While the function validates a WordPress nonce (subscrpt_nonce), the nonce is not bound to a specific subscription ID, and the function fails to verify if the currently authenticated user owns the subscription associated with the provided ID. This allows any authenticated user with "Customer" permissions to perform unauthorized actions on any other user's subscription.
2. Attack Vector Analysis
- Endpoint:
[site-url]/my-account/view-subscription/[ANY_ID]/ - Query Parameters:
subscrpt_id: The target Subscription Post ID (integer).action: The unauthorized action (e.g.,cancelled,reactive,renew-on,renew-off).wpnonce: A validsubscrpt_nonceobtained from the attacker's own session.
- Authentication Level: Authenticated (Customer or higher).
- Preconditions: The attacker must have at least one subscription of their own to access the "View Subscription" page and obtain a valid nonce.
3. Code Flow
- Entry Point: The
ActionControllerclass (inincludes/Frontend/ActionController.php) registers a listener on thebefore_single_subscrpt_contenthook. - Trigger: When a user visits the "View Subscription" endpoint (
/my-account/view-subscription/ID), theMyAccount::view_subscrpt_contentfunction is called, which eventually triggers thebefore_single_subscrpt_contenthook via a template. - Vulnerable Function:
ActionController::control_action_subscrpt()is executed. - Verification (Incomplete):
- Line 35:
wp_verify_nonce( $wpnonce, 'subscrpt_nonce' )verifies the nonce. Sincesubscrpt_nonceis generated without an object-specific action string (seeincludes/Frontend/MyAccount.phpline 111), the same nonce works for any ID for that user.
- Line 35:
- Unauthorized Action (Sink):
- The code proceeds to perform actions based on the
actionparameter. - If
action=cancelled, it callsAction::status( 'pe_cancelled', $subscrpt_id )(Line 52) orAction::status( $action, $subscrpt_id )(Line 54). - If
action=renew-off, it callsupdate_post_meta( $subscrpt_id, '_subscrpt_auto_renew', 0 )(Line 60).
- The code proceeds to perform actions based on the
- Missing Check: There is no check (e.g., comparing
$subscrpt_idownership) before these status modifications occur.
4. Nonce Acquisition Strategy
The subscrpt_nonce is required. It is generated during the rendering of any "View Subscription" page the user is authorized to see.
- Navigate to Subscription Page: The attacker logs in and navigates to their own subscription:
/my-account/view-subscription/[ATTACKER_SUB_ID]/. - Extract Nonce: The nonce is used in the action buttons (Cancel, Renew, etc.).
- Browser Eval:
If the plugin localizes data (not explicitly seen in provided snippets but common), check// The nonce is often found in links within the .customer-details or .actions div // Alternatively, it might be enqueued, but here it's typically in the HTML links Array.from(document.querySelectorAll('a')).find(a => a.href.includes('wpnonce')).href.split('wpnonce=')[1].split('&')[0]window.
5. Exploitation Strategy
The goal is to cancel a victim's subscription.
- Identify Victim ID: Determine the Subscription Post ID of the victim (e.g., via enumeration or leaked data).
- Obtain Nonce: Log in as
attackerand visit/my-account/view-subscription/[ATTACKER_ID]. Extractwpnonce. - Craft Request:
- Method:
GET - URL:
http://localhost:8080/my-account/view-subscription/[ATTACKER_ID]/?subscrpt_id=[VICTIM_ID]&action=cancelled&wpnonce=[NONCE] - Note: The ID in the path doesn't matter as much as the
subscrpt_idparameter, as the hook triggers on the page load.
- Method:
- Execute: Use the
http_requesttool to send the authenticated GET request.
6. Test Data Setup
- Install Requirements: WooCommerce and the "Subscription" plugin.
- Create Product: Create a simple product with subscription enabled.
- Create Users:
victim_user(Subscriber)attacker_user(Subscriber)
- Generate Subscriptions:
- Process a checkout for
victim_userto create subscriptionV_ID. - Process a checkout for
attacker_userto create subscriptionA_ID.
- Process a checkout for
- Ensure Status: Ensure
V_IDstatus isactive.
7. Expected Results
- The server will process the request and redirect (via JavaScript/Location header) to the view page.
- The subscription
V_IDwill have its post status changed tope_cancelled(pending cancellation) orcancelled.
8. Verification Steps
- Check Status via WP-CLI:
wp post get [VICTIM_ID] --field=post_status - Verify Meta (if testing renewal toggle):
wp post meta get [VICTIM_ID] _subscrpt_auto_renew - Observe Response: A successful exploit often results in the attacker being redirected to
/my-account/view-subscription/[VICTIM_ID](if they try to view it) or seeing a success message if the plugin logic allows.
9. Alternative Approaches
- Renewal Toggle: If cancellation is too disruptive for a PoC, use
action=renew-offand verify the_subscrpt_auto_renewmeta changes from1to0. - Status Reaction: If a subscription is cancelled, try
action=reactiveto set it back toactive. - Direct IDOR Enumeration: If nonces were somehow bound to IDs (which they aren't here), the attacker would need to find a way to leak a victim's specific nonce. Given they are not bound, a simple loop through IDs could theoretically disrupt all subscriptions on the platform.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.