[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fntJLTFQxnuSBj2iG2PbudFWAJe0P6-8mvV6flrE1jYg":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":9,"research_fix_diff":35,"research_exploit_outline":36,"research_model_used":37,"research_started_at":38,"research_completed_at":39,"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":40},"CVE-2026-4987","sureforms-unauthenticated-payment-amount-validation-bypass-via-formid","SureForms \u003C= 2.5.2 - Unauthenticated Payment Amount Validation Bypass via 'form_id'","The SureForms – Contact Form, Payment Form & Other Custom Form Builder plugin for WordPress is vulnerable to Payment Amount Bypass in all versions up to, and including, 2.5.2. This is due to the create_payment_intent() function performing a payment validation solely based on the value of a user-controlled parameter. This makes it possible for unauthenticated attackers to bypass configured form payment-amount validation and create underpriced payment\u002Fsubscription intents by setting form_id to 0.","sureforms",null,"\u003C=2.5.2","2.6.0","high",7.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:H\u002FA:N","Improper Input Validation","2026-03-27 12:56:32","2026-03-28 01:25:46",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fc4772b32-a730-44f2-b43c-f9bd5abb6541?source=api-prod",1,[22,23,24,25,26,27,28,29],"admin\u002Fadmin.php","admin\u002Fanalytics.php","assets\u002Fbuild\u002Fblocks.asset.php","assets\u002Fbuild\u002Fblocks.js","assets\u002Fbuild\u002Fdashboard.asset.php","assets\u002Fbuild\u002Fdashboard.css","assets\u002Fbuild\u002Fdashboard.js","assets\u002Fbuild\u002Fentries.asset.php","researched",false,3,"# Vulnerability Research Plan: CVE-2026-4987 - SureForms Payment Bypass\n\n## 1. Vulnerability Summary\nThe **SureForms** plugin (up to version 2.5.2) contains a logic flaw in its payment processing workflow. The function `create_payment_intent()` (likely located in `SRFM\\Inc\\Payments\\Stripe\\Stripe_Helper` or a similar payment-handling class) is responsible for generating a Stripe\u002Fpayment provider intent. \n\nThe vulnerability arises because the server-side validation of the payment `amount` is conditionally linked to a provided `form_id`. If an attacker provides a `form_id` of `0`, the application fails to look up the \"official\" price for the form and instead trusts the user-provided `amount` parameter. This allows an unauthenticated user to create a payment intent for an arbitrary (lower) value than intended by the site administrator.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** WordPress AJAX API (`\u002Fwp-admin\u002Fadmin-ajax.php`).\n- **Action:** `srfm_create_payment_intent` (inferred from plugin prefix `srfm` and function name `create_payment_intent`).\n- **HTTP Method:** `POST`.\n- **Authentication:** Unauthenticated (requires `wp_ajax_nopriv_` registration).\n- **Vulnerable Parameter:** `form_id` (set to `0`) and `amount`.\n- **Preconditions:** The site must have Stripe or another payment provider configured within SureForms.\n\n## 3. Code Flow\n1. **Entry Point:** A user interacts with a SureForms payment form on the frontend.\n2. **AJAX Call:** The frontend JS triggers a POST request to `admin-ajax.php` with the action `srfm_create_payment_intent`.\n3. **Handler:** The handler calls `create_payment_intent()`.\n4. **Bypass Logic (Vulnerable Path):**\n   - The code checks `$_POST['form_id']`.\n   - If `form_id` is a valid post ID, it retrieves the form configuration: `$form_settings = get_post_meta( $form_id, '_srfm_form_settings', true );`.\n   - It would then validate `$_POST['amount']` against `$form_settings['payment_amount']`.\n   - **The Bug:** If `form_id` is `0`, the logic either skips the meta lookup or handles the empty result by defaulting to the value passed in the `amount` parameter without cross-referencing a source of truth.\n5. **Sink:** The unvalidated amount is passed to `Stripe_Helper` to create a `PaymentIntent` via the Stripe API.\n\n## 4. Nonce Acquisition Strategy\nSureForms likely localizes a nonce for frontend form submissions. Based on standard WordPress plugin patterns and the provided file list, the nonce is likely registered via `wp_localize_script`.\n\n- **Strategy:**\n    1. Identify a page containing a SureForms payment form.\n    2. The plugin uses the prefix `srfm`. The localized object is likely named `srfm_vars` or `srfm_frontend`.\n    3. **Action:**\n        - Create a test payment form using the plugin.\n        - Place the form on a public page: `wp post create --post_type=page --post_status=publish --post_content='[sureforms id=\"XYZ\"]'` (where XYZ is the form ID).\n        - Navigate to the page using `browser_navigate`.\n        - Execute `browser_eval(\"window.srfm_frontend?.nonce\")` or `browser_eval(\"window.srfm_vars?.nonce\")` to extract the token.\n- **Verification:** Check the `wp_verify_nonce` or `check_ajax_referer` call in the source (if accessible) to confirm the action string (e.g., `srfm_nonce` or `srfm_form_nonce`).\n\n## 5. Exploitation Strategy\n### Step-by-Step Plan:\n1. **Target Identification:** Find a published SureForms payment form and its legitimate `form_id`. Note the actual price (e.g., $100.00).\n2. **Nonce Extraction:** Access the page where the form is hosted and extract the AJAX nonce from the `srfm_frontend` JS object.\n3. **Draft Malicious Request:**\n   - **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php`\n   - **Method:** `POST`\n   - **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n   - **Body:**\n     ```\n     action=srfm_create_payment_intent\n     &_ajax_nonce=[EXTRACTED_NONCE]\n     &form_id=0\n     &amount=100\n     &currency=usd\n     &payment_method_id=pm_card_visa\n     ```\n     *(Note: `amount=100` represents $1.00 in Stripe's smallest currency unit).*\n4. **Execution:** Send the request via `http_request`.\n5. **Observation:** A successful exploit will return a `200 OK` with a JSON body containing a Stripe `client_secret` or success message, despite the `amount` being incorrect for the site's forms.\n\n## 6. Test Data Setup\n1. **Configure Plugin:** Activate `sureforms`.\n2. **Mock Payment Setup:** If a real Stripe key isn't available, check if the plugin has a \"Test Mode\" or \"Offline Payment\" mode.\n3. **Create High-Value Form:**\n   - Create a new form (Post Type: `srfm_forms`).\n   - Set the payment amount to `10000` ($100.00).\n   - Get the `$REAL_FORM_ID`.\n4. **Publish Page:**\n   - `wp post create --post_type=page --post_title=\"Payment Page\" --post_status=publish --post_content='[sureforms id=\"$REAL_FORM_ID\"]'`\n\n## 7. Expected Results\n- **Success:** The server returns a JSON response indicating a payment intent was created for the value `100` (e.g., `{\"success\":true,\"data\":{\"client_secret\":\"pi_...\"}}`).\n- **Control Test:** Sending the same request with the `$REAL_FORM_ID` and `amount=100` should return an error (e.g., `Invalid amount`).\n\n## 8. Verification Steps\n1. **Check Database:** Query the `wp_srfm_entries` table (referenced in `admin\u002Fanalytics.php`) to see if a record was created with the manipulated amount.\n   - `wp db query \"SELECT * FROM wp_srfm_entries ORDER BY id DESC LIMIT 1;\"`\n2. **Check Post Meta:** If the plugin saves transient payment data, check the metadata for the most recent entry.\n\n## 9. Alternative Approaches\n- **REST API:** If the AJAX endpoint fails, check for a registered REST route. `admin\u002Fanalytics.php` mentions `register_rest_route`. Look for a route like `sureforms\u002Fv1\u002Fpayments`.\n- **Parameter Variation:** If `form_id=0` is blocked by a global check, try `form_id=null`, `form_id[]=` or a non-existent ID (e.g., `999999`) to see if the fallback to the user-supplied `amount` still occurs.\n- **Subscription Bypass:** The description mentions \"subscription intents.\" If the form supports recurring payments, try adding `plan_id` or `billing_cycle` parameters while keeping `form_id=0`.","The SureForms plugin for WordPress is vulnerable to a payment amount validation bypass in the `create_payment_intent()` function. Due to improper input validation, unauthenticated attackers can set the `form_id` parameter to 0, causing the server to skip price verification against form settings and trust a user-provided amount instead.","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsureforms\u002F2.5.2\u002Fadmin\u002Fadmin.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsureforms\u002F2.6.0\u002Fadmin\u002Fadmin.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsureforms\u002F2.5.2\u002Fadmin\u002Fadmin.php\t2026-03-12 11:46:10.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsureforms\u002F2.6.0\u002Fadmin\u002Fadmin.php\t2026-03-23 10:59:44.000000000 +0000\n@@ -108,8 +108,6 @@\n \t\t\tadd_action( 'admin_menu', [ $this, 'maybe_add_entries_badge' ], 99 );\n \t\t}\n \n-\t\tadd_action( 'admin_menu', [ $this, 'add_payments_badge' ], 99 );\n-\n \t\tadd_filter( 'wpforms_current_user_can', [ $this, 'disable_wpforms_capabilities' ], 10, 3 );\n \n \t\tadd_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_pointer' ] );\n@@ -662,34 +660,6 @@\n \t}\n \n \t\u002F**\n-\t * Summary of add_payments_badge\n-\t *\n-\t * @since 2.0.0\n-\t * @return void\n-\t *\u002F\n-\tpublic function add_payments_badge() {\n-\t\tif ( ! Helper::current_user_can() ) {\n-\t\t\treturn;\n-\t\t}\n-\n-\t\tglobal $submenu;\n-\t\tif ( isset( $submenu['sureforms_menu'] ) ) {\n-\t\t\tforeach ( $submenu['sureforms_menu'] as $index => $sub_item ) {\n-\t\t\t\tif ( isset( $sub_item[2] ) && SRFM_PAYMENTS === $sub_item[2] ) {\n-\t\t\t\t\tob_start();\n-\t\t\t\t\t?>\n-\t\t\t\t\t\u003Cspan style=\"color: #4ADE80;font-size: 9px;font-weight: 600;\">\u003C?php echo esc_html__( 'New', 'sureforms' ); ?>\u003C\u002Fspan>\n-\t\t\t\t\t\u003C?php\n-\t\t\t\t\t$new_badge_html = ob_get_clean();\n-\t\t\t\t\t\u002F\u002F phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Adding notifications for submenu item.\n-\t\t\t\t\t$submenu['sureforms_menu'][ $index ][0] .= $new_badge_html;\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\t\u002F**\n \t * Mark the user's visit to the entries page.\n \t *\n \t * @since 1.7.3","The exploit targets the AJAX endpoint used for creating Stripe payment intents. An unauthenticated attacker first obtains a valid AJAX nonce from the site's frontend (usually found in the `srfm_frontend` or `srfm_vars` JavaScript objects). The attacker then sends a POST request to `\u002Fwp-admin\u002Fadmin-ajax.php` with the action `srfm_create_payment_intent`. By providing a `form_id` of `0` and a self-defined `amount` parameter, the attacker forces the plugin to ignore the server-side price configuration associated with real forms. If the plugin has an active payment provider like Stripe configured, it will return a valid payment intent (e.g., a `client_secret`) for the attacker's lower amount, allowing them to complete a transaction for an arbitrary price.","gemini-3-flash-preview","2026-04-17 22:33:53","2026-04-17 22:34:18",{"type":41,"vulnerable_version":42,"fixed_version":11,"vulnerable_browse":43,"vulnerable_zip":44,"fixed_browse":45,"fixed_zip":46,"all_tags":47},"plugin","2.5.2","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsureforms\u002Ftags\u002F2.5.2","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsureforms.2.5.2.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsureforms\u002Ftags\u002F2.6.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsureforms.2.6.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsureforms\u002Ftags"]