Stylish Cost Calculator < 8.3.1 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The Stylish Cost Calculator plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and excluding, 8.3.1 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with contributor-level access and above, 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:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<8.3.1# Vulnerability Research Plan: CVE-2026-24630 - Stylish Cost Calculator Stored XSS ## 1. Vulnerability Summary The **Stylish Cost Calculator** plugin (versions <= 8.1.9) contains a stored Cross-Site Scripting (XSS) vulnerability. The flaw exists because the plugin fails to properly sanitize and esc…
Show full research plan
Vulnerability Research Plan: CVE-2026-24630 - Stylish Cost Calculator Stored XSS
1. Vulnerability Summary
The Stylish Cost Calculator plugin (versions <= 8.1.9) contains a stored Cross-Site Scripting (XSS) vulnerability. The flaw exists because the plugin fails to properly sanitize and escape user-supplied data when saving calculator configurations and subsequently rendering them. Authenticated users with Contributor-level permissions or higher can inject arbitrary JavaScript into calculator settings (e.g., element labels, custom HTML fields, or calculator names). This script executes when an administrator views the calculator in the backend or when any user views a page where the malicious calculator is embedded via shortcode.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
scc_save_calculator(inferred based on plugin architecture) or a similar AJAX action registered for saving calculator metadata. - Vulnerable Parameter: Likely a field within the
calculator_dataorform_dataPOST parameters (e.g.,element_label,section_title, orcustom_html). - Authentication Level: Authenticated (Contributor+). Contributors in WordPress can typically access the plugin's calculator editor if the plugin doesn't strictly enforce
manage_optionscapabilities for all views. - Precondition: The plugin must be active, and the attacker must have a valid login for a Contributor-level account.
3. Code Flow (Inferred)
- Registration: The plugin registers an AJAX handler via
add_action('wp_ajax_scc_save_calculator', ...). - Input: The handler function (likely in
admin/class-admin.phpor a dedicated AJAX controller) retrieves data from$_POST['calculator_json']or$_POST['settings']. - Persistence: The data is saved to the database using
update_option()orupdate_post_meta()without undergoingwp_kses()orsanitize_text_field()on the specific sub-fields containing the payload. - Output (Frontend): When a user visits a page with the shortcode
[stylish-cost-calculator id="XX"], the plugin retrieves the settings and echoes them. - Sink: The vulnerable data is printed using
echoorprintfwithout escaping functions likeesc_html()oresc_attr().
4. Nonce Acquisition Strategy
To exploit the AJAX endpoint, a valid WordPress nonce associated with the plugin's admin actions is required.
- Preparation: Create a page with the plugin's primary shortcode to ensure all scripts and localized variables are loaded.
wp post create --post_type=page --post_title="XSS Loader" --post_status=publish --post_content='[stylish-cost-calculator id="1"]'
- Navigation: Use
browser_navigateto go to the WordPress login page, authenticate as a Contributor, and then navigate to the "XSS Loader" page or the Plugin's management page in/wp-admin/. - Extraction: Stylish Cost Calculator typically localizes its settings in a global JS object. Use
browser_evalto extract the nonce:- Check for:
window.scc_admin_params?.nonce - Check for:
window.scc_vars?.ajax_nonce - Check for:
window.stylish_cost_calculator_params?.nonce
- Check for:
- Action String: The nonce is likely generated for the action
scc_nonceorstylish_cost_calculator_save.
5. Exploitation Strategy
Step 1: Authentication
Authenticate the http_request tool by providing the Contributor's session cookies.
Step 2: Inject Payload
Send a POST request to admin-ajax.php to update an existing calculator or create a new one with a payload.
Example Request:
- URL:
https://<target>/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
(Note: The exact structure ofaction=scc_save_calculator& nonce=[EXTRACTED_NONCE]& calculator_id=1& calculator_data={"elements":[{"type":"heading","label":"<script>alert(document.domain)</script>"}]}calculator_datamust match the plugin's expected JSON schema, often found inassets/js/admin.js).
Step 3: Trigger Execution
- Navigate to the frontend page containing the shortcode:
https://<target>/xss-loader/. - Verify if the script executes in the browser context.
6. Test Data Setup
- User Creation:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123
- Initial Calculator: Create at least one calculator so an ID exists.
wp stylish-cost-calculator create --name="Test Calc"(if CLI exists) or manually via the UI.
- Page Creation:
wp post create --post_type=page --post_title="XSS Test" --post_content='[stylish-cost-calculator id="1"]' --post_status=publish
7. Expected Results
- The
scc_save_calculatorrequest should return a successful JSON response (e.g.,{"success":true}). - When viewing the page
XSS Test, a JavaScript alert box displaying the domain name should appear. - In a real-world scenario, the payload would be replaced with an admin-cookie exfiltration script or a CSRF script to create a new administrator.
8. Verification Steps
- Check Database: Verify the payload is stored raw in the
wp_optionsorwp_postmetatable.wp db query "SELECT option_value FROM wp_options WHERE option_name LIKE '%scc_calculator_%'"
- Source Code Inspection: Fetch the frontend page and check for the unescaped script tag.
- Use
http_request(GET) and search the body for<script>alert(document.domain)</script>.
- Use
9. Alternative Approaches
- Settings XSS: If
scc_save_calculatoris restricted, check forwp_ajax_scc_save_settings. Many plugins allow lower-privileged users to modify "Global Settings" that are reflected on all calculators. - Attribute Breakout: If
<script>is filtered, attempt attribute injection in a label:- Payload:
"><img src=x onerror=alert(1)>
- Payload:
- Import/Export: SCC often has an import feature. Attempt to upload a malicious
.jsonor.txtconfiguration file containing the XSS payload. This often bypasses standard AJAX sanitization.
Summary
The Stylish Cost Calculator plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) in versions up to 8.1.9. Authenticated attackers with Contributor-level access or higher can inject malicious JavaScript into calculator configurations via the `scc_save_calculator` AJAX action, which executes when the calculator is viewed by other users.
Vulnerable Code
// Inferred from Research Plan: Input handled without sanitization in AJAX callback // File: admin/class-admin.php or similar add_action('wp_ajax_scc_save_calculator', 'scc_save_calculator_callback'); function scc_save_calculator_callback() { // ... check_ajax_referer ... $id = $_POST['calculator_id']; $calculator_data = $_POST['calculator_data']; // Vulnerable: Input is not sanitized update_option('scc_calculator_' . $id, $calculator_data); wp_send_json_success(); } --- // Inferred from Research Plan: Output rendered without escaping in shortcode handler // File: public/class-public.php or similar public function render_calculator($atts) { $data = get_option('scc_calculator_' . $atts['id']); foreach ($data['elements'] as $element) { echo $element['label']; // Vulnerable: Output is not escaped } }
Security Fix
@@ -10,7 +10,7 @@ public function scc_save_calculator() { check_ajax_referer('scc_nonce', 'nonce'); $id = intval($_POST['calculator_id']); - $data = $_POST['calculator_data']; + $data = wp_kses_post($_POST['calculator_data']); update_option('scc_calculator_' . $id, $data); wp_send_json_success(); } @@ -25,7 +25,7 @@ public function render_calculator($atts) { $data = get_option('scc_calculator_' . $atts['id']); foreach ($data['elements'] as $element) { - echo $element['label']; + echo esc_html($element['label']); } }
Exploit Outline
The exploit is carried out by an authenticated user with at least Contributor-level permissions. First, the attacker navigates to the WordPress admin area or a page with a calculator shortcode to extract the necessary AJAX nonce (likely `scc_nonce` or similar) from localized JavaScript variables like `window.scc_vars`. Then, the attacker sends an authenticated POST request to `/wp-admin/admin-ajax.php` with the action `scc_save_calculator`. The payload is embedded within the `calculator_data` JSON parameter, placing a malicious script (e.g., `<script>alert(document.domain)</script>`) into a field such as an element label or section title. The stored script will execute whenever an administrator views the calculator in the plugin settings or any user visits a frontend page where the specific calculator is embedded via shortcode.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.