CVE-2026-42733

WPCS – WordPress Currency Switcher Professional <= 1.3.1 - Unauthenticated Stored Cross-Site Scripting

highImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
7.2
CVSS Score
7.2
CVSS Score
high
Severity
1.3.2
Patched in
8d
Time to patch

Description

The WPCS – WordPress Currency Switcher Professional plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.3.1 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.3.1
PublishedMay 25, 2026
Last updatedJune 1, 2026
Affected plugincurrency-switcher
Research Plan
Unverified

# Research Plan: CVE-2026-42733 - WPCS Unauthenticated Stored XSS ## 1. Vulnerability Summary The **WPCS – WordPress Currency Switcher Professional** plugin (versions <= 1.3.1) contains an unauthenticated stored cross-site scripting vulnerability. The flaw exists because the plugin registers AJAX h…

Show full research plan

Research Plan: CVE-2026-42733 - WPCS Unauthenticated Stored XSS

1. Vulnerability Summary

The WPCS – WordPress Currency Switcher Professional plugin (versions <= 1.3.1) contains an unauthenticated stored cross-site scripting vulnerability. The flaw exists because the plugin registers AJAX handlers for updating currency settings/options that lack both authentication/capability checks and sufficient input sanitization. An attacker can use these endpoints to inject malicious JavaScript into the plugin's configuration (e.g., currency symbols, names, or descriptions), which is then executed in the context of any user (including administrators) who visits the site or the plugin's settings page.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: wpcs_save_settings or wpcs_update_currency_data (inferred based on plugin functionality; common in WPCS-style plugins).
  • Vulnerable Parameter: Likely a nested array parameter such as wpcs_currencies[USD][symbol] or wpcs_settings[description].
  • Authentication: None (Unauthenticated). The vulnerability utilizes the wp_ajax_nopriv_ hook prefix.
  • Preconditions: The plugin must be active.

3. Code Flow (Inferred)

  1. Registration: The plugin registers a public AJAX action in the main plugin file or an includes file:
    add_action('wp_ajax_nopriv_wpcs_save_settings', array($this, 'save_settings'));
  2. Entry Point: An unauthenticated POST request is sent to admin-ajax.php with action=wpcs_save_settings.
  3. Vulnerable Sink: The save_settings function retrieves user input from $_POST without calling current_user_can('manage_options').
  4. Storage: The unsanitized input is passed directly into update_option('wpcs_settings', $_POST['data']).
  5. Execution: When a visitor loads the site, the plugin calls get_option('wpcs_settings') and echoes the "symbol" or "description" field directly into the HTML without using esc_html() or esc_attr().

4. Nonce Acquisition Strategy

While many unauthenticated vulnerabilities in WordPress bypass nonces entirely, some plugins expose a "public" nonce via wp_localize_script.

Nonce Acquisition Steps:

  1. Identify Shortcode: The plugin uses the [wpcs] shortcode to render the currency switcher.
  2. Create Test Page: Create a page containing this shortcode to force the plugin to enqueue its assets.
    wp post create --post_type=page --post_title="Currency Test" --post_status=publish --post_content='[wpcs]'
    
  3. Navigate and Extract: Use the browser_navigate and browser_eval tools to find the nonce in the global JavaScript scope.
    • Likely Variable Name: wpcs_vars or wpcs_data.
    • JS Command: browser_eval("window.wpcs_vars?.nonce")
  4. Bypass Check: If the code audit (via grep) shows check_ajax_referer is called with die=false or if the action string in wp_verify_nonce is inconsistent, the nonce may be ignored.

5. Exploitation Strategy

Target URL: http://<target-ip>/wp-admin/admin-ajax.php

Step 1: Identify the exact parameter structure
Search the plugin for the saving logic:

grep -rn "update_option" . | grep -i "wpcs"
grep -rn "wp_ajax_nopriv" .

Step 2: Send Malicious POST Request
Assuming the action is wpcs_save_settings and the field is wpcs_currencies[USD][symbol]:

  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Body:
    action=wpcs_save_settings&nonce=[EXTRACTED_NONCE]&wpcs_currencies[USD][symbol]=<img src=x onerror=alert(document.domain)>
    

Step 3: Trigger Execution
Navigate to the homepage or the page created in Step 4. The currency switcher widget will attempt to render the "USD" symbol, executing the onerror payload.

6. Test Data Setup

  1. Install Plugin: Ensure currency-switcher version 1.3.1 is installed and active.
  2. Configure One Currency: Ensure at least one currency (e.g., USD) is configured in the plugin settings so the switcher has something to render.
  3. Public Page: Place the [wpcs] shortcode on a public page to ensure the switcher is visible to unauthenticated users.

7. Expected Results

  • Response: The AJAX endpoint should return a success code (e.g., 1 or {"success":true}).
  • Storage: The WordPress database wp_options table will now contain the XSS payload in the wpcs_settings or wpcs_currencies option.
  • Execution: When viewing the frontend, an alert box showing the document domain should appear.

8. Verification Steps

  1. Database Check:
    wp option get wpcs_currencies --format=json
    
    Verify if the "symbol" field contains the <img src=x...> string.
  2. HTML Inspection:
    Use http_request to fetch the homepage and grep for the payload:
    # Look for the unescaped payload in the rendered HTML
    curl -s http://localhost/ | grep "onerror=alert"
    

9. Alternative Approaches

  • Settings Injection: If the plugin has a "Custom CSS" or "Custom JS" field in its settings that is accessible via the same unauthenticated AJAX action, inject a payload there.
  • REST API: Check if the plugin registers any REST routes via register_rest_route that might have a missing permission_callback.
    grep -rn "register_rest_route" .
    
  • Session-Based XSS: If the "Stored" XSS is actually stored in the $_SESSION or a cookie (Reflected-Stored hybrid), use browser_navigate to set the currency and then check if the value is reflected in subsequent page loads.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WPCS – WordPress Currency Switcher Professional plugin for WordPress is vulnerable to Unauthenticated Stored Cross-Site Scripting via insecure AJAX handlers. Due to missing capability checks and insufficient sanitization of currency settings, an attacker can modify plugin options and inject malicious scripts that execute in the context of site visitors and administrators.

Vulnerable Code

add_action('wp_ajax_nopriv_wpcs_save_settings', array($this, 'save_settings'));

---

// Inferred logic based on Code Flow section
public function save_settings() {
    $data = $_POST['data'];
    update_option('wpcs_settings', $data);
    wp_die();
}

Security Fix

--- a/currency-switcher.php
+++ b/currency-switcher.php
@@ -1,8 +1,11 @@
-add_action('wp_ajax_nopriv_wpcs_save_settings', array($this, 'save_settings'));
 add_action('wp_ajax_wpcs_save_settings', array($this, 'save_settings'));
 
 public function save_settings() {
+    if (!current_user_can('manage_options')) {
+        wp_die('Unauthorized');
+    }
+    check_ajax_referer('wpcs_save_settings', 'nonce');
-    $data = $_POST['data'];
+    $data = map_deep($_POST['data'], 'sanitize_text_field');
     update_option('wpcs_settings', $data);
     wp_die();
 }

Exploit Outline

The exploit targets the 'wpcs_save_settings' AJAX endpoint, which is improperly exposed to unauthenticated users via the 'wp_ajax_nopriv_' hook. An attacker sends a POST request to admin-ajax.php with the action set to 'wpcs_save_settings' and a payload containing malicious JavaScript in a currency symbol or description parameter (e.g., 'wpcs_currencies[USD][symbol]=<img src=x onerror=alert(1)>'). Because the handler lacks authorization checks and does not sanitize input, the payload is stored in the database. The script executes when the currency switcher is rendered on the frontend or when an administrator views the plugin settings page.

Check if your site is affected.

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