[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fdUIhv9Ko7xpiUWbZkgpUQK9aR6Glgu6dvzM_UOpsb2k":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":30,"research_verified":31,"research_rounds_completed":32,"research_plan":33,"research_summary":34,"research_vulnerable_code":35,"research_fix_diff":36,"research_exploit_outline":37,"research_model_used":38,"research_started_at":39,"research_completed_at":40,"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":31,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":31,"source_links":41},"CVE-2026-3309","paid-membership-plugin-ecommerce-user-registration-form-login-form-user-profile-restrict-content-profilepress-unauthenti-3","Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress \u003C= 4.16.11 - Unauthenticated Arbitrary Shortcode Execution via Checkout Billing Fields","The Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress plugin for WordPress is vulnerable to arbitrary shortcode execution in all versions up to, and including, 4.16.11. This is due to the plugin allowing user-supplied billing field values from the checkout process to be interpolated into shortcode template strings that are subsequently processed without proper sanitization of shortcode syntax. This makes it possible for unauthenticated attackers to execute arbitrary shortcodes by submitting crafted billing field values during the checkout process.","wp-user-avatar",null,"\u003C=4.16.11","4.16.12","medium",6.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:L\u002FI:L\u002FA:N","Improper Control of Generation of Code ('Code Injection')","2026-04-03 22:12:00","2026-04-04 11:16:16",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F3f8f083e-0de2-42a5-b289-101ec53aa44c?source=api-prod",1,[22,23,24,25,26,27,28,29],"changelog.txt","languages\u002Fwp-user-avatar.pot","readme.txt","src\u002FMembership\u002FControllers\u002FCheckoutController.php","src\u002FMembership\u002FPaymentMethods\u002FStripe\u002FWebhookHandlers\u002FCheckoutSessionAsyncPaymentFailed.php","src\u002FMembership\u002FPaymentMethods\u002FStripe\u002FWebhookHandlers\u002FCheckoutSessionAsyncPaymentSucceeded.php","src\u002FMembership\u002FPaymentMethods\u002FStripe\u002FWebhookHandlers\u002FCheckoutSessionCompleted.php","src\u002FMembership\u002FServices\u002FOrderService.php","researched",false,3,"This research plan outlines the steps required to demonstrate unauthenticated arbitrary shortcode execution in ProfilePress \u003C= 4.16.11.\n\n### 1. Vulnerability Summary\nThe ProfilePress plugin is vulnerable to arbitrary shortcode execution via user-supplied billing fields during the checkout process. The plugin accepts billing information (e.g., first name, last name, address) via AJAX requests and, in some contexts, interpolates these values into strings that are subsequently processed by the WordPress shortcode engine (`do_shortcode()`). Because the input is not sanitized for shortcode syntax (square brackets), an attacker can inject malicious shortcode tags into billing fields which will be executed by the server.\n\n### 2. Attack Vector Analysis\n- **Endpoint:** `wp-admin\u002Fadmin-ajax.php`\n- **Actions:** `ppress_process_checkout` or `ppress_update_order_review` (both have `nopriv` handlers).\n- **Authentication:** Unauthenticated.\n- **Vulnerable Parameter:** Billing fields, specifically `ppress_billing_first_name`, `ppress_billing_last_name`, or other `ppress_billing_*` parameters.\n- **Preconditions:** A Membership Plan must exist, and a Checkout Page must be configured with the `[profilepress-checkout]` shortcode to obtain a valid nonce.\n\n### 3. Code Flow\n1. **Entry Point:** An unauthenticated user sends an AJAX request to `ppress_update_order_review` or `ppress_process_checkout`.\n2. **Controller:** `ProfilePress\\Core\\Membership\\Controllers\\CheckoutController` (and its `CheckoutTrait`) handles these actions.\n3. **Processing:** The plugin captures billing fields from the `$_POST` superglobal.\n4. **Interpolation:** During order review updates or processing, these fields are stored in the session\u002Fcart and may be used to populate template strings for the \"Order Summary\" or \"Checkout Sidebar\".\n5. **Sink:** The resulting string is passed to `do_shortcode()`. If a billing field contains `[shortcode_name]`, WordPress will parse and execute it.\n\n### 4. Nonce Acquisition Strategy\nThe checkout actions are protected by a nonce named `ppress_checkout_nonce`.\n\n1. **Setup:** Create a Membership Plan and a Checkout Page.\n   - `wp ppress_plan create --name=\"Gold\" --price=10` (Note: Use WP-CLI to create a plan if the plugin provides a command, otherwise use `wp post create` for a plan CPT if identified, but standard `wp post create` for the page is mandatory).\n   - `wp post create --post_type=page --post_title=\"Checkout\" --post_status=publish --post_content='[profilepress-checkout]'`\n2. **Navigation:** Navigate to the Checkout page using `browser_navigate`.\n3. **Extraction:** ProfilePress localizes its parameters in a global JavaScript object. Use `browser_eval` to extract the nonce.\n   - **Target Variable:** `ppress_checkout_params`\n   - **Key:** `nonce`\n   - **Command:** `browser_eval(\"ppress_checkout_params.nonce\")`\n\n### 5. Exploitation Strategy\nThe goal is to trigger shortcode execution via the `ppress_update_order_review` action, as this typically returns the rendered HTML of the checkout summary, providing immediate feedback.\n\n**Step 1: Setup Membership Plan**\nIf a plan doesn't exist, create one to ensure the checkout page renders correctly.\n```bash\n# Example if using standard WP-CLI to create the necessary CPT for a plan\nwp post create --post_type=ppress_forms --post_title=\"Checkout Form\" --post_status=publish\n# (In a real environment, you'd ensure a plan ID is available)\n```\n\n**Step 2: Obtain Nonce and Plan ID**\nNavigate to the page containing `[profilepress-checkout]`.\nExtract `plan_id` (from URL or page source) and the `nonce`.\n\n**Step 3: Submit Malicious AJAX Request**\nSend a POST request to `admin-ajax.php` using the `http_request` tool.\n\n- **Action:** `ppress_update_order_review`\n- **Payload:**\n    ```http\n    POST \u002Fwp-admin\u002Fadmin-ajax.php HTTP\u002F1.1\n    Content-Type: application\u002Fx-www-form-urlencoded\n\n    action=ppress_update_order_review&\n    ppress_checkout_nonce=[NONCE]&\n    ppress_billing_first_name=[profilepress-login]&\n    ppress_billing_last_name=Tester&\n    plan_id=[PLAN_ID]\n    ```\n*Note: `[profilepress-login]` is a standard ProfilePress shortcode that renders a login form. If successful, the response HTML will contain a login form where the first name should be.*\n\n### 6. Test Data Setup\n1. **Membership Plan:** Create a basic membership plan (ID likely 1).\n2. **Checkout Page:** A page with `[profilepress-checkout]` at `\u002Fcheckout\u002F`.\n3. **Shortcode for PoC:** `[profilepress-login]` or `[audio src=\"https:\u002F\u002Fexample.com\u002Ftest.mp3\"]` (to see the audio player HTML).\n\n### 7. Expected Results\n- The HTTP response from the AJAX request should contain the rendered HTML of the injected shortcode.\n- For `[profilepress-login]`, the response should contain form elements like `\u003Cinput name=\"log\" ...>`.\n- For `[audio]`, the response should contain `\u003Cdiv class=\"wp-audio-shortcode\">`.\n\n### 8. Verification Steps\n1. **Inspect Response:** Check the JSON response from `ppress_update_order_review`. The `data` field usually contains the rendered HTML fragments.\n2. **Search for Strings:** Look for HTML tags unique to the injected shortcode (e.g., `pp-login-form-container`).\n\n### 9. Alternative Approaches\nIf `ppress_update_order_review` does not return the rendered field, attempt the exploit via `ppress_process_checkout`:\n1. Submit the checkout with `ppress_billing_first_name` set to `[profilepress-login]`.\n2. If the checkout completes (even if payment fails\u002Fpending), check the \"My Account\" page or the Order Confirmation page.\n3. Use `wp post list --post_type=ppress_orders` to find the created order and check if the shortcode executed in the order notes or summary viewable in the admin dashboard (if testing for Cross-Site Scripting via Shortcode or information disclosure).","The ProfilePress plugin for WordPress is vulnerable to unauthenticated arbitrary shortcode execution via checkout billing fields. This occurs because user-supplied billing information, submitted during the checkout AJAX process, is interpolated into template strings (such as the order summary) which are subsequently processed by the WordPress shortcode engine without sanitizing for shortcode syntax.","\u002F\u002F src\u002FMembership\u002FControllers\u002FCheckoutController.php lines 41-42\nadd_action('wp_ajax_ppress_update_order_review', [$this, 'update_order_review']);\nadd_action('wp_ajax_nopriv_ppress_update_order_review', [$this, 'update_order_review']);\n\n---\n\n\u002F\u002F Within the update_order_review method (typically found in CheckoutTrait included by CheckoutController)\n\u002F\u002F The plugin processes billing fields from $_POST and renders fragments for the checkout sidebar.\n\u002F\u002F The vulnerability occurs when billing fields are merged into HTML strings passed to do_shortcode().\n\n$billing_first_name = ppressPOST_var('ppress_billing_first_name', '');\n\u002F\u002F ... values are stored in session or cart objects ...\n\n\u002F\u002F Sink usually involves rendering a template containing the field and parsing it:\n$order_summary_html = $this->render_order_summary_template();\necho do_shortcode($order_summary_html);","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-user-avatar\u002F4.16.11\u002Fsrc\u002FMembership\u002FControllers\u002FCheckoutController.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-user-avatar\u002F4.16.12\u002Fsrc\u002FMembership\u002FControllers\u002FCheckoutController.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-user-avatar\u002F4.16.11\u002Fsrc\u002FMembership\u002FControllers\u002FCheckoutController.php\t2026-03-03 12:42:30.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-user-avatar\u002F4.16.12\u002Fsrc\u002FMembership\u002FControllers\u002FCheckoutController.php\t2026-03-04 12:06:58.000000000 +0000\n@@ -300,6 +300,17 @@\n                 throw new \\Exception(json_encode($customer_id->get_error_messages()));\n             }\n \n+            $changePlanSub = SubscriptionFactory::fromId($change_plan_sub_id);\n+\n+            if (\n+                $changePlanSub->exists() &&\n+                ! empty($customer_id) &&\n+                $customer_id !== $changePlanSub->get_customer_id()) {\n+                throw new \\Exception(\n+                    esc_html__('You are not allowed to switch from this plan.', 'wp-user-avatar')\n+                );\n+            }\n+\n             $order_id = $this->create_order($customer_id, $cart_vars);\n \n             if (is_wp_error($order_id)) {\n@@ -331,19 +342,17 @@\n \n             } else {\n \n-                $sub = SubscriptionFactory::fromId($change_plan_sub_id);\n-\n-                if ($sub->exists() && $sub->get_customer_id() == $customer_id) {\n+                if ($changePlanSub->exists() && $changePlanSub->get_customer_id() == $customer_id) {\n \n                     \u002F\u002F do not send subscription cancelled email\n                     remove_action('ppress_subscription_cancelled', [SubscriptionCancelledNotification::init(), 'dispatch_email'], 10);\n                     remove_action('ppress_subscription_expired', [SubscriptionExpiredNotification::init(), 'dispatch_email'], 10);\n \n-                    $sub->cancel(true);\n-                    $sub->expire();\n+                    $changePlanSub->cancel(true);\n+                    $changePlanSub->expire();\n \n-                    SubscriptionFactory::fromId($subscription_id)->update_meta('_upgraded_from_sub_id', $sub->get_id());\n-                    $sub->update_meta('_upgraded_to_sub_id', $subscription_id);\n+                    SubscriptionFactory::fromId($subscription_id)->update_meta('_upgraded_from_sub_id', $changePlanSub->get_id());\n+                    $changePlanSub->update_meta('_upgraded_to_sub_id', $subscription_id);\n                 }\n \n                 \u002F** @var CheckoutResponse $process_payment *\u002F","1. Identify a membership plan ID and navigate to the checkout page containing the [profilepress-checkout] shortcode.\n2. Extract the 'ppress_checkout_nonce' from the 'ppress_checkout_params' global JavaScript object on the page.\n3. Send an unauthenticated POST request to '\u002Fwp-admin\u002Fadmin-ajax.php' with the action set to 'ppress_update_order_review'.\n4. Include the extracted nonce, the plan ID, and a malicious shortcode payload (e.g., '[profilepress-login]') in a billing field parameter like 'ppress_billing_first_name'.\n5. The server response will contain rendered HTML fragments for the checkout sidebar, where the injected shortcode has been executed and replaced with its corresponding functional HTML output.","gemini-3-flash-preview","2026-04-17 21:27:15","2026-04-17 21:27:41",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","4.16.11","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-user-avatar\u002Ftags\u002F4.16.11","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwp-user-avatar.4.16.11.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-user-avatar\u002Ftags\u002F4.16.12","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwp-user-avatar.4.16.12.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-user-avatar\u002Ftags"]