CVE-2026-25419

UpsellWP <= 2.2.3 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The UpsellWP plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 2.2.3. This makes it possible for authenticated attackers, with subscriber-level access and above, to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=2.2.3
PublishedJanuary 28, 2026
Last updatedFebruary 26, 2026
Research Plan
Unverified

Since the source code for version 2.2.3 is not provided, this research plan is based on the vulnerability description, the plugin's known architecture (slug: `checkout-upsell-and-order-bumps`), and common "Missing Authorization" patterns in WooCommerce-related plugins. ### 1. Vulnerability Summary …

Show full research plan

Since the source code for version 2.2.3 is not provided, this research plan is based on the vulnerability description, the plugin's known architecture (slug: checkout-upsell-and-order-bumps), and common "Missing Authorization" patterns in WooCommerce-related plugins.

1. Vulnerability Summary

The UpsellWP plugin (<= 2.2.3) fails to implement proper capability checks on one or more of its AJAX or admin-side handlers. While these handlers are intended for administrative use (managing upsells and order bumps), they are registered in a way that allows any authenticated user—including those with Subscriber roles—to trigger them. This allows an attacker to modify plugin settings, create malicious offers, or disrupt the store's checkout flow.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action: Likely upsell_wp_save_settings, upsell_wp_update_offer, or upsell_wp_toggle_offer_status (inferred).
  • Authentication: Authenticated (Subscriber or higher).
  • Payload Parameter: action, nonce (if checked), and data parameters (e.g., settings_data or offer_id).
  • Preconditions: The attacker must have a valid Subscriber-level session cookie.

3. Code Flow (Inferred)

  1. Entry Point: The plugin registers an AJAX action in its main class or an admin-specific class:
    add_action( 'wp_ajax_upsell_wp_save_settings', [ $this, 'save_settings_callback' ] );
    
  2. Missing Check: Inside save_settings_callback, the code likely performs a nonce check but fails to call current_user_can( 'manage_options' ).
  3. Sink: The function proceeds to update the database using update_option() or $wpdb->update() based on values provided in $_POST.

4. Nonce Acquisition Strategy

Many WooCommerce plugins localize nonces for the admin dashboard. Since Subscribers can access wp-admin/profile.php, we must check if the plugin enqueues its scripts globally in the admin area.

  1. Identify Nonce Action: Grep for wp_create_nonce in the plugin folder to find the action string (e.g., upsell-wp-nonce).
  2. Identify JS Variable: Grep for wp_localize_script to find the object name (e.g., upsell_wp_vars).
  3. Extraction:
    • Create a Subscriber user and log in.
    • Navigate to wp-admin/profile.php.
    • Use browser_eval to extract the nonce:
      browser_eval("window.upsell_wp_vars?.nonce") (inferred name).
  4. Bypass Check: If check_ajax_referer uses a generic action like -1 or is missing entirely, the nonce may be unnecessary or easily obtained.

5. Exploitation Strategy

We will attempt to disable an existing order bump or change a global setting to demonstrate unauthorized modification.

Step 1: Identify Target Setting
Use WP-CLI to find current settings:
wp option get upsell_wp_settings (inferred name).

Step 2: Prepare Payload
We will target an action like saving settings.

  • Request Method: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Content-Type: application/x-www-form-urlencoded
  • Parameters:
    • action: upsell_wp_save_settings (inferred)
    • upsell_wp_nonce: [EXTRACTED_NONCE]
    • settings: {"enable_upsells":"0"} (example payload to disable the plugin's main feature)

Step 3: Execute via http_request
Using the Subscriber's cookies.

6. Test Data Setup

  1. Plugin Setup: Install UpsellWP 2.2.3 and WooCommerce.
  2. Configuration: Create at least one "Order Bump" or "Checkout Upsell" via the admin UI.
  3. Attacker User:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
    
  4. Confirm Target State:
    wp option get upsell_wp_settings
    

7. Expected Results

  • HTTP Response: The server should return a 200 OK with a success indicator (e.g., {"success":true} or 1).
  • Impact: The targeted setting (e.g., enable_upsells) should be modified in the database despite the request coming from a Subscriber.

8. Verification Steps

After sending the HTTP request, verify the change using WP-CLI:

# Verify the option value has changed
wp option get upsell_wp_settings

# Or verify via database directly if options are serialized
wp db query "SELECT option_value FROM wp_options WHERE option_name = 'upsell_wp_settings'"

9. Alternative Approaches

If upsell_wp_save_settings is not the vulnerable action, the researcher should use the following commands to find other candidates:

  1. List all AJAX actions registered by the plugin:
    grep -r "wp_ajax_" wp-content/plugins/checkout-upsell-and-order-bumps/
    
  2. Analyze the callbacks for those actions:
    Search for callbacks that DO NOT contain the string current_user_can.
    # Example: Check if the 'save_offer' function has auth checks
    grep -A 15 "function save_offer" wp-content/plugins/checkout-upsell-and-order-bumps/path/to/file.php
    
  3. Check admin_init hooks:
    Some plugins handle form submissions via admin_init. Since admin_init runs for all authenticated users (including subscribers) when they access admin-ajax.php or admin-post.php, handlers registered here without capability checks are highly vulnerable.
    grep -r "add_action.*admin_init" wp-content/plugins/checkout-upsell-and-order-bumps/
    
Research Findings
Static analysis — not yet PoC-verified

Summary

The UpsellWP plugin for WordPress fails to perform capability checks on its AJAX handlers in versions up to 2.2.3. This allows authenticated users with Subscriber-level permissions to modify plugin settings or manipulate WooCommerce upsell offers by sending crafted requests to the WordPress AJAX endpoint.

Vulnerable Code

// Inferred code structure based on common patterns in the affected plugin version
// Path: wp-content/plugins/checkout-upsell-and-order-bumps/includes/admin/class-upsell-wp-admin.php

public function save_settings_callback() {
    // Nonce check might exist, but capability check is missing
    check_ajax_referer('upsell-wp-nonce', 'nonce');

    // Vulnerability: No call to current_user_can('manage_options') before processing data
    $settings = isset($_POST['settings']) ? $_POST['settings'] : array();
    update_option('upsell_wp_settings', $settings);
    
    wp_send_json_success();
}

Security Fix

--- wp-content/plugins/checkout-upsell-and-order-bumps/includes/admin/class-upsell-wp-admin.php
+++ wp-content/plugins/checkout-upsell-and-order-bumps/includes/admin/class-upsell-wp-admin.php
@@ -10,6 +10,10 @@
 public function save_settings_callback() {
     check_ajax_referer('upsell-wp-nonce', 'nonce');
 
+    if (!current_user_can('manage_options')) {
+        wp_send_json_error('Unauthorized', 403);
+    }
+
     $settings = isset($_POST['settings']) ? $_POST['settings'] : array();
     update_option('upsell_wp_settings', $settings);
     wp_send_json_success();

Exploit Outline

1. Login to the WordPress site as a user with Subscriber-level permissions. 2. Access the WordPress admin dashboard (e.g., /wp-admin/profile.php) and inspect the page source or use browser console to extract the AJAX nonce, typically localized by the plugin (e.g., window.upsell_wp_vars.nonce). 3. Construct a POST request to /wp-admin/admin-ajax.php with the 'action' parameter set to the vulnerable handler (e.g., 'upsell_wp_save_settings'). 4. Include the extracted 'nonce' and a 'settings' payload designed to disable features or modify offer parameters. 5. Send the request and verify the modification by checking the plugin's configuration via the UI or database (e.g., 'wp option get upsell_wp_settings').

Check if your site is affected.

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