CVE-2026-3574

Experto Dashboard for WooCommerce <= 1.0.4 - Authenticated (Administrator+) Stored Cross-Site Scripting via 'Navigation Font Size' Setting

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
4.4
CVSS Score
4.4
CVSS Score
medium
Severity
1.0.5
Patched in
1d
Time to patch

Description

The Experto Dashboard for WooCommerce plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's settings fields (including 'Navigation Font Size', 'Navigation Font Weight', 'Heading Font Size', 'Heading Font Weight', 'Text Font Size', and 'Text Font Weight') in all versions up to and including 1.0.4. This is due to insufficient input sanitization (no sanitize callback in register_setting()) and missing output escaping (no esc_attr() in the field_callback() printf output) on user-supplied values. This makes it possible for authenticated attackers, with Administrator-level access and above, to inject arbitrary web scripts in the plugin settings page that will execute whenever a user accesses the settings page. This only affects multi-site installations and installations where unfiltered_html has been disabled.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.0.4
PublishedApril 8, 2026
Last updatedApril 9, 2026
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-3574 ## 1. Vulnerability Summary The **Experto Dashboard for WooCommerce** plugin (up to version 1.0.4) is vulnerable to **Stored Cross-Site Scripting (XSS)** via its configuration settings. Specifically, several font-related settings (e.g., 'Navigation Font S…

Show full research plan

Exploitation Research Plan: CVE-2026-3574

1. Vulnerability Summary

The Experto Dashboard for WooCommerce plugin (up to version 1.0.4) is vulnerable to Stored Cross-Site Scripting (XSS) via its configuration settings. Specifically, several font-related settings (e.g., 'Navigation Font Size', 'Navigation Font Weight') are registered using register_setting() without a sanitize_callback. Furthermore, when these settings are rendered on the plugin's administration page, the output is generated using a callback function (e.g., field_callback()) that uses printf() to echo the current values without context-appropriate escaping (such as esc_attr()).

In WordPress environments where unfiltered_html is disabled (like Multi-site or specific security hardened sites), an Administrator can inject malicious scripts into these fields. These scripts execute whenever an administrator visits the plugin's settings page, potentially allowing for session hijacking or unauthorized administrative actions.

2. Attack Vector Analysis

  • Target Endpoint: wp-admin/options.php (Standard handler for the WordPress Settings API).
  • Vulnerable Parameters:
    • experto_dashboard_nav_font_size (inferred)
    • experto_dashboard_nav_font_weight (inferred)
    • experto_dashboard_heading_font_size (inferred)
    • experto_dashboard_heading_font_weight (inferred)
    • experto_dashboard_text_font_size (inferred)
    • experto_dashboard_text_font_weight (inferred)
  • Authentication Required: Administrator level or higher.
  • Preconditions: The plugin must be active. The exploit is most relevant in environments where DISALLOW_UNFILTERED_HTML is set to true or in a Multi-site installation where the user is a Site Admin but not a Network Super Admin.

3. Code Flow (Inferred)

  1. Registration: The plugin uses the admin_init hook to call register_setting().
  2. Missing Sanitization: register_setting( 'experto_dashboard_settings_group', 'experto_dashboard_nav_font_size', array() ) is called. The third parameter (args) lacks the sanitize_callback key.
  3. Field Definition: add_settings_field() is called to add the input to the UI, specifying a callback (e.g., field_callback).
  4. Vulnerable Rendering:
    // Example of vulnerable code in the plugin
    function field_callback($args) {
        $value = get_option('experto_dashboard_nav_font_size');
        // VULNERABILITY: No esc_attr() used here
        printf('<input type="text" name="experto_dashboard_nav_font_size" value="%s" />', $value);
    }
    
  5. Storage: When the admin submits the form, options.php saves the raw input into the wp_options table.
  6. Execution: When the settings page is reloaded, the raw payload is echoed into the value attribute, breaking out of it to execute JavaScript.

4. Nonce Acquisition Strategy

To update settings via options.php, two hidden fields are required: option_page and _wpnonce.

  1. Navigate to Settings Page: The settings page is likely located at wp-admin/admin.php?page=experto-dashboard-settings (slug inferred from plugin name).
  2. Identify Option Group: Look for the hidden input name="option_page".
  3. Extract Nonce: Use browser_eval to extract the nonce generated for that specific option group.

Execution Command:

// Within the settings page context
const optionPage = document.querySelector('input[name="option_page"]')?.value;
const nonce = document.querySelector('input[name="_wpnonce"]')?.value;
console.log({optionPage, nonce});

5. Exploitation Strategy

  1. Preparation: Log in to the WordPress instance as an Administrator.
  2. Discovery: Navigate to the Experto Dashboard settings page. Identify the exact field names for the font size settings and the option_page name.
  3. Payload Crafting:
    • Target field: experto_dashboard_nav_font_size
    • Payload: "><script>alert(document.domain)</script>
  4. Submission: Use the http_request tool to send a POST request to wp-admin/options.php.
    • Method: POST
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body:
      option_page=[EXTRACTED_OPTION_PAGE]&action=update&_wpnonce=[EXTRACTED_NONCE]&experto_dashboard_nav_font_size="><script>alert(document.domain)</script>
  5. Trigger: Navigate back to the plugin's settings page.

6. Test Data Setup

  1. Install and activate Experto Dashboard for WooCommerce (experto-custom-dashboard) <= 1.0.4.
  2. Ensure WooCommerce is installed and active (as the plugin depends on it).
  3. Create an Administrator user.
  4. (Optional) Add define( 'DISALLOW_UNFILTERED_HTML', true ); to wp-config.php to simulate the restricted environment where this vulnerability is most critical.

7. Expected Results

  • The options.php request should return a 302 Found redirect back to the settings page with a settings-updated=true parameter.
  • Upon navigating to the settings page, the browser should execute the alert(document.domain) script.
  • The HTML source of the page should show: <input ... value=""><script>alert(document.domain)</script>" />.

8. Verification Steps

  1. Database Check: Use WP-CLI to verify the payload is stored in the database.
    wp option get experto_dashboard_nav_font_size
    
    Result should match the injected payload.
  2. UI Check: Navigate to the settings page and check for the presence of the payload in the raw HTML response.
    # After navigating via browser_navigate
    browser_eval("document.body.innerHTML.includes('<script>alert(document.domain)</script>')")
    

9. Alternative Approaches

  • Attribute Breakout: If the input is placed within a different HTML tag or context (e.g., inside a style tag), use appropriate payloads like 12px; </style><script>alert(1)</script>.
  • Event Handlers: If <script> tags are filtered by an intermediary WAF but the plugin remains vulnerable, try event handlers:
    " onmouseover="alert(1) or " autofocus onfocus="alert(1)
  • Other Fields: The vulnerability is reported in multiple font size and weight fields. If nav_font_size is sanitized, attempt the exploit on heading_font_weight or text_font_size.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Experto Dashboard for WooCommerce plugin is vulnerable to Stored Cross-Site Scripting via several font-related configuration settings. Because the plugin fails to sanitize user input on registration and fails to escape output in the administrative UI, an authenticated attacker with administrative privileges can inject malicious scripts that execute whenever an administrator visits the settings page.

Vulnerable Code

// Within the plugin's administration logic (e.g., admin/class-experto-dashboard-admin.php or similar)

// 1. Missing sanitization in registration
register_setting('experto_dashboard_settings_group', 'experto_dashboard_nav_font_size');
register_setting('experto_dashboard_settings_group', 'experto_dashboard_nav_font_weight');

---

// 2. Missing output escaping in field callbacks
function field_callback($args) {
    $value = get_option('experto_dashboard_nav_font_size');
    // VULNERABILITY: The value is echoed directly into the attribute without esc_attr()
    printf('<input type="text" name="experto_dashboard_nav_font_size" value="%s" />', $value);
}

Security Fix

--- a/experto-custom-dashboard.php
+++ b/experto-custom-dashboard.php
@@ -20,7 +20,7 @@
-    register_setting( 'experto_dashboard_settings_group', 'experto_dashboard_nav_font_size' );
+    register_setting( 'experto_dashboard_settings_group', 'experto_dashboard_nav_font_size', 'sanitize_text_field' );
@@ -45,5 +45,5 @@
 function field_callback($args) {
     $value = get_option('experto_dashboard_nav_font_size');
-    printf('<input type="text" name="experto_dashboard_nav_font_size" value="%s" />', $value);
+    printf('<input type="text" name="experto_dashboard_nav_font_size" value="%s" />', esc_attr($value));
 }

Exploit Outline

The exploit requires an attacker with Administrator access to bypass unfiltered_html restrictions (common in multisite or hardened WordPress environments). 1. The attacker navigates to the Experto Dashboard settings page within the WordPress admin dashboard. 2. The attacker identifies one of the vulnerable font setting fields, such as 'Navigation Font Size' or 'Navigation Font Weight'. 3. The attacker crafts a payload that breaks out of the HTML input attribute: `"><script>alert(document.domain)</script>`. 4. The attacker submits the settings form. This request is sent to `wp-admin/options.php` with the appropriate `option_page` and `_wpnonce` values. 5. Because the plugin lacks a `sanitize_callback`, the raw script is saved to the `wp_options` table. 6. Every time an administrator visits the plugin's settings page, the `get_option()` call retrieves the payload and `printf()` renders it directly into the HTML without escaping, triggering the execution of the script in the context of the user's browser session.

Check if your site is affected.

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