WF-8b0e0f22-de42-4da9-a0c1-ae41ba57be03-sureforms

SureForms – Drag and Drop Form Builder for WordPress <= 2.2.1 - Unauthenticated Stripe Payment Amount Manipulation

highImproper Input Validation
7.5
CVSS Score
7.5
CVSS Score
high
Severity
2.2.2
Patched in
0d
Time to patch

Description

The SureForms – Drag and Drop Form Builder for WordPress plugin for WordPress is vulnerable to Improper Input Validation in all versions up to, and including, 2.2.1. This is due to the plugin accepting the payment amount directly from user-controlled POST data in the 'create_payment_intent' and 'create_subscription_intent' functions without validating it against the form's configured price. This makes it possible for unauthenticated attackers to modify the payment amount to any arbitrary value when submitting a Stripe payment form, potentially purchasing products or services at significantly reduced prices.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
None
Confidentiality
High
Integrity
None
Availability

Technical Details

Affected versions<=2.2.1
PublishedFebruary 13, 2026
Last updatedFebruary 13, 2026
Affected pluginsureforms

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the process for exploiting an Improper Input Validation vulnerability in the SureForms plugin (<= 2.2.1). ## 1. Vulnerability Summary The **SureForms** plugin for WordPress allows unauthenticated users to manipulate the payment amount sent to Stripe. The plugin's AJAX ha…

Show full research plan

This research plan outlines the process for exploiting an Improper Input Validation vulnerability in the SureForms plugin (<= 2.2.1).

1. Vulnerability Summary

The SureForms plugin for WordPress allows unauthenticated users to manipulate the payment amount sent to Stripe. The plugin's AJAX handlers create_payment_intent and create_subscription_intent accept a user-provided amount parameter from the POST request. Crucially, the plugin fails to validate this amount against the server-side configuration of the form. An attacker can submit a form and override the intended price with an arbitrary value (e.g., changing $100.00 to $1.00), which is then used to generate a Stripe Payment Intent.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Actions (Inferred): sureforms_create_payment_intent and sureforms_create_subscription_intent (based on the function names provided in the vulnerability details).
  • Vulnerable Parameter: amount (and potentially currency or subscription_plan_id in the subscription variant).
  • Authentication: Unauthenticated (uses wp_ajax_nopriv_ hooks).
  • Preconditions:
    1. A form with Stripe payment integration must be active.
    2. The plugin must be configured with Stripe API keys (Test mode is sufficient for PoC).

3. Code Flow

  1. Entry Point: The AJAX request hits admin-ajax.php with the action sureforms_create_payment_intent.
  2. Hook Registration: The plugin likely registers this in its initialization or Stripe service class:
    add_action( 'wp_ajax_sureforms_create_payment_intent', [ $this, 'create_payment_intent' ] );
    add_action( 'wp_ajax_nopriv_sureforms_create_payment_intent', [ $this, 'create_payment_intent' ] );
    
  3. Vulnerable Logic: Inside the create_payment_intent function:
    • The function retrieves $amount = $_POST['amount'];.
    • The function might retrieve $form_id = $_POST['form_id'];.
    • The Bug: Instead of using $form_id to look up the "official" price from the database (e.g., get_post_meta($form_id, 'sf_form_price', true)), it passes the $amount directly to the Stripe API call.
  4. Stripe Sink:
    \Stripe\PaymentIntent::create([
        'amount'   => $amount, // Manipulated value used here
        'currency' => $_POST['currency'],
        // ...
    ]);
    

4. Nonce Acquisition Strategy

The plugin likely uses a nonce to protect AJAX calls, localized for the frontend.

  1. Identify Shortcode: Locate the shortcode used to render forms. (Inferred: [sureforms id="123"]).
  2. Setup: Create a new page containing a payment-enabled form.
  3. Extraction:
    • Navigate to the page.
    • Search for wp_localize_script output in the page source.
    • The object is likely named sureforms_params, sureforms_frontend, or sf_params.
    • Inferred JS Access: window.sureforms_params?.nonce or window.sureforms_frontend?.ajax_nonce.

5. Exploitation Strategy

Step 1: Data Setup

  1. Enable "Stripe" in SureForms settings.
  2. Create a form with a "Stripe" field.
  3. Set the form price to a high value (e.g., 50000 for $500.00).
  4. Publish the form on a new page.
  5. Record the form_id.

Step 2: Nonce Extraction

Use the browser_navigate and browser_eval tools to grab the nonce:

  • browser_navigate("http://localhost:8080/target-page/")
  • browser_eval("window.sureforms_params.nonce") (Verify exact name during the process).

Step 3: Manipulated Payment Intent Request

Send the malicious request to admin-ajax.php. We will attempt to create a Payment Intent for $1.00 (amount=100) for a form that is supposed to cost $500.00.

Request Details:

  • Method: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=sureforms_create_payment_intent&amount=100&currency=usd&form_id=[FORM_ID]&_ajax_nonce=[NONCE]
    

Step 4: Verification

The server will return a JSON response containing a Stripe client_secret.

  1. Analyze the response.
  2. Verify the amount associated with the client_secret (if returned in the response metadata) or check the Stripe logs.
  3. In many cases, the response itself will echo back the successful intent creation.

6. Test Data Setup

  • Shortcode: [sureforms id="PAYMENT_FORM_ID"]
  • Plugin Settings: Ensure Stripe is in "Test Mode".
  • Form Field: A "Stripe Payment" field must be present in the form configuration.

7. Expected Results

A successful exploit will return a 200 OK response with a JSON payload:

{
    "success": true,
    "data": {
        "client_secret": "pi_...",
        "amount": 100
    }
}

If the amount in the response is 100 (signifying $1.00) despite the form being configured for $500.00, the vulnerability is confirmed.

8. Verification Steps (Post-Exploit)

Since we cannot easily check the Stripe Dashboard in an automated test environment without API access, we can:

  1. Check Logs: If the plugin logs transactions to a custom table or wp_options, use wp db query to inspect.
  2. Verify Code Path: Use grep to confirm that create_payment_intent in the source code reads directly from $_POST['amount'] without comparing it to get_post_meta.

9. Alternative Approaches

If sureforms_create_payment_intent is not the exact action name:

  1. Search the codebase for add_action( 'wp_ajax_nopriv_.
  2. Search for the string create_payment_intent to find the handler registration.
  3. If a simple amount parameter doesn't work, check if the plugin expects the amount inside an array (e.g., form_data[amount]).

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.