Paymattic – Secure, Simple Payment & Donation with Subscription Payments, Recurring Donations, Customer Management <= 4.6.19 - Missing Authorization
Description
The Paymattic – Secure, Simple Payment & Donation with Subscription Payments, Recurring Donations, Customer Management plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 4.6.19. This makes it possible for unauthenticated attackers to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=4.6.19What Changed in the Fix
Changes introduced in v4.6.20
Source Code
WordPress.org SVN### 1. Vulnerability Summary The Paymattic plugin (<= 4.6.19) contains a **Missing Authorization** vulnerability within its custom routing system. Specifically, certain administrative or dashboard-level actions—such as canceling subscriptions or syncing billing data—were accessible through unauthent…
Show full research plan
1. Vulnerability Summary
The Paymattic plugin (<= 4.6.19) contains a Missing Authorization vulnerability within its custom routing system. Specifically, certain administrative or dashboard-level actions—such as canceling subscriptions or syncing billing data—were accessible through unauthenticated AJAX requests.
The vulnerability stems from the plugin's use of a custom router (defined in app/Http/Routes/routes.php) which dispatches requests to controllers based on a route parameter. In versions up to 4.6.19, routes within the dashboard prefix (intended for customers) and potentially some under forms were not strictly enforced against unauthenticated access, or the FrontendUserPolicy failed to validate that the requester was the owner of the entry.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
wppayform_apiorwppayform_frontend_api(dispatched via theWPPayForm\App\Http\Router). - Vulnerable Route:
dashboard/form/{id}/entries/{entryId}/cancel-subscription - HTTP Method:
POST - Authentication: Unauthenticated (
nopriv). - Preconditions: An existing submission/subscription ID and form ID must be known (often sequential and easily guessable).
3. Code Flow
- The request hits `admin-ajax.
Summary
The Paymattic plugin for WordPress (<= 4.6.19) lacks proper authorization checks and nonce validation within its custom routing system and AJAX handlers. This allows unauthenticated attackers to perform unauthorized actions, such as canceling subscriptions, accessing sensitive billing data, or submitting forged form data by targeting predictable form and entry IDs.
Vulnerable Code
// app/Hooks/actions.php:145 add_action('wp_ajax_wpf_submit_form', array(new \WPPayForm\App\Hooks\Handlers\SubmissionHandler(), 'handleSubmission')); add_action('wp_ajax_nopriv_wpf_submit_form', array(new \WPPayForm\App\Hooks\Handlers\SubmissionHandler(), 'handleSubmission')); --- // app/Hooks/Handlers/SubmissionHandler.php:33 class SubmissionHandler { // ... (omitted properties) public function handleSubmission() { if (!isset($_REQUEST['form_data'])) { return; } parse_str($_REQUEST['form_data'], $form_data); // ... if(isset($_REQUEST['form_id'])){ $formId = absint(sanitize_text_field(wp_unslash($_REQUEST['form_id']))); } // No nonce verification or capability check performed before processing data --- // app/Http/Routes/routes.php:84 $router->prefix('dashboard')->withPolicy('FrontendUserPolicy')->group(function ($router) { $router->get('/forms/formatted', 'FormsController@formatted'); $router->prefix('/form/{id}/entries')->group(function ($router) { $router->prefix('/{entryId}')->group(function ($router) { $router->get('/', 'SubmissionController@getSubmission')->int('id', 'entryId'); $router->post('/cancel-subscription', 'SubmissionController@cancelSubscription')->int('id', 'entryId'); }); }); });
Security Fix
@@ -40,17 +40,20 @@ if (!isset($_REQUEST['form_data'])) { return; } - + if (!isset($_REQUEST['wpf_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['wpf_nonce'])), 'wpf_form_submission')) { + wp_send_json_error(array( + 'message' => __('Security verification failed. Please refresh and try again.', 'wp-payment-form'), + ), 403); + } + parse_str($_REQUEST['form_data'], $form_data); - $form_localize = Arr::get($_REQUEST['form_localize'], 'conditional_logic'); // Now Validate the form please if(isset($_REQUEST['form_id'])){ $formId = absint(sanitize_text_field(wp_unslash($_REQUEST['form_id']))); } $this->formID = $formId; - // Get Original Form Elements Now - $totalPayableAmount = intval(wp_unslash($_REQUEST['main_total'] ?? 0)); do_action('wppayform/form_submission_activity_start', $formId); $form = Form::getForm($formId); @@ -60,13 +63,11 @@ 'message' => __('Invalid request. Please try again', 'wp-payment-form'), ), 423); } - $formattedElements = Form::getFormattedElements($formId); $numericCalculation = []; $submittedNumericCalculation = apply_filters('wppayform/dynamic_payment_calculation', '', $numericCalculation, $formattedElements, $form_data); - - $this->validate($form_data, $formattedElements, $form, $form_localize); + $this->validate($form_data, $formattedElements, $form); $paymentMethod = apply_filters('wppayform/choose_payment_method_for_submission', '', $formattedElements['payment_method_element'], $formId, $form_data);
Exploit Outline
To exploit this vulnerability, an attacker targets the AJAX endpoint /wp-admin/admin-ajax.php or the custom routing mechanism used by Paymattic. For unauthorized subscription cancellation, an unauthenticated attacker crafts a POST request to the custom router endpoint (often wppayform_api) specifying the target route 'dashboard/form/{id}/entries/{entryId}/cancel-subscription'. Because the plugin fails to verify that the requester owns the entry or has sufficient capabilities, providing a valid form ID and entry ID (which are often sequential and guessable) allows the attacker to cancel any active subscription. Similarly, the form submission handler (wpf_submit_form) can be exploited via CSRF or unauthorized unauthenticated requests due to the lack of nonce verification.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.