Visualizer: Tables and Charts Manager for WordPress < 4.0.0 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The Visualizer: Tables and Charts Manager for WordPress plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to 4.0.0 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
What Changed in the Fix
Changes introduced in v4.0.0
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-24573 (Visualizer Stored XSS) ## 1. Vulnerability Summary **CVE-2026-24573** is a Stored Cross-Site Scripting (XSS) vulnerability in the **Visualizer: Tables and Charts Manager for WordPress** plugin (< 4.0.0). The vulnerability stems from insufficient sanitiz…
Show full research plan
Exploitation Research Plan: CVE-2026-24573 (Visualizer Stored XSS)
1. Vulnerability Summary
CVE-2026-24573 is a Stored Cross-Site Scripting (XSS) vulnerability in the Visualizer: Tables and Charts Manager for WordPress plugin (< 4.0.0). The vulnerability stems from insufficient sanitization of chart configuration or data (labels, tooltips, or manual data entries) when saved by an authenticated user with Contributor+ permissions. These payloads are subsequently executed in the context of any user (including Administrators) viewing the page where the chart is rendered.
2. Attack Vector Analysis
- Endpoint: WordPress REST API or AJAX. Based on
Visualizer_Gutenberg_Block, the plugin registers REST endpoints viaregister_rest_endpoints. - Action: Likely a
POSTorPUTrequest to update chart settings or data. - Vulnerable Parameter: Chart titles, legend labels, or manual data cells.
- Authentication: Authenticated, Contributor-level or higher.
- Preconditions: The plugin must be active, and a contributor must have the ability to create or edit a chart (enabled by default in Gutenberg).
3. Code Flow
- Entry Point:
Visualizer_Gutenberg_Block::register_rest_endpoints(registered in__constructviarest_api_init). - Data Handling: User-provided chart data (manual edit feature added in 3.11.0) or settings are sent via the block editor.
- Storage: Data is stored as a Custom Post Type (
visualizer) or in post meta. - Sink:
Visualizer_Gutenberg_Block::gutenberg_block_callback(and the underlying rendering engine) retrieves the chart configuration. - Rendering: The frontend script (likely using Google Charts or ChartJS libraries as seen in
block.js) renders the labels. If labels are not escaped before being passed to the JS library or if the library renders them as HTML, the XSS triggers.
4. Nonce Acquisition Strategy
The Gutenberg block editor enqueues scripts using visualizerLocalize. For REST API requests, WordPress uses the standard wp-api nonce.
- Identify Block Script: The script
visualizer-gutenberg-blockis localized withvisualizerLocalize. - Setup Page: Create a page as a Contributor containing the
visualizer/chartblock. - Extraction:
- Navigate to the WordPress dashboard as a Contributor.
- Use
browser_evalto extract the REST nonce provided by WordPress core for Gutenberg:window.wpApiSettings?.nonce - Additionally, check
visualizerLocalizefor specific URLs:window.visualizerLocalize?.chartEditUrl
5. Exploitation Strategy
The goal is to inject a script into a chart's configuration that executes when viewed.
Step 1: Create a Chart (Inferred REST/AJAX)
Visualizer typically uses a Custom Post Type. We will attempt to create a chart via the REST API.
- URL:
/wp-json/visualizer/v1/charts(inferred) - Method:
POST - Headers:
X-WP-Nonce: [NONCE] - Payload (JSON):
{ "title": "Test Chart <img src=x onerror=alert('XSS_TITLE')>", "type": "line", "data": [ ["Label", "Value"], ["<img src=x onerror=alert('XSS_DATA')>", 10] ], "settings": { "legend": {"position": "top"} } }
Step 2: Alternative (AJAX)
If the REST API is restricted, use the AJAX endpoint identified in Visualizer_Gutenberg_Block.
- URL:
/wp-admin/admin-ajax.php - Action:
visualizer-create-chart(found increateChartlocalized variable). - Parameters:
action=visualizer-create-chart&...
Step 3: Trigger
- Embed the created chart ID into a public post using the shortcode:
[visualizer id="[CHART_ID]"]. - Navigate to the post URL as an unauthenticated user or Admin.
6. Test Data Setup
- User: Create a user with the
contributorrole. - Chart Creation: The contributor creates a new "Visualizer" chart.
- Shortcode Placement: Contributor creates a post/page and adds the Visualizer block or shortcode:
wp post create --post_type=page --post_status=publish --post_content='[visualizer id="[ID]"]'
7. Expected Results
- The chart configuration is saved successfully containing the HTML tags.
- When the page is viewed, the browser attempts to render the malformed label/title.
- An alert box with
XSS_TITLEorXSS_DATAappears, confirming script execution.
8. Verification Steps
- Database Check: Verify the raw payload in the database.
wp db query "SELECT post_title, post_content FROM wp_posts WHERE post_type='visualizer' ORDER BY ID DESC LIMIT 1;" - HTML Inspection: Check the frontend source for the unescaped payload.
http_request GET "http://localhost:8080/?p=[POST_ID]" # Look for: <img src=x onerror=...
9. Alternative Approaches
- JSON Import: If manual editing is filtered, try the "Import from JSON" feature (common in Visualizer). The vulnerability might exist in the parser for imported files.
- ChartJS vs. Google Charts: The plugin supports multiple libraries. If Google Charts escapes by default, try switching the library to
chartjs(mentioned inCHANGELOG.mdversion 3.11.0) via thesettingspayload. - Attribute Injection: Instead of a full
<script>tag, try breaking out of a JSON string in a script block if the plugin reflects settings into avar visualizerConfig = {...}block.
Summary
The Visualizer plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) in versions up to 4.0.0. Authenticated attackers with Contributor-level access or higher can inject malicious scripts into chart settings or manual data entries, which are then executed in the context of any user viewing the chart.
Vulnerable Code
// classes/Visualizer/Gutenberg/Block.php:285 register_rest_route( 'visualizer/v' . VISUALIZER_REST_VERSION, '/update-chart', array( 'methods' => 'POST', 'callback' => array( $this, 'update_chart_data' ), 'args' => array( 'id' => array( 'sanitize_callback' => 'absint', ), ), 'permission_callback' => function () { return current_user_can( 'edit_posts' ); }, ) ); --- // classes/Visualizer/Gutenberg/Block.php:655 public function update_chart_data( $data ) { if ( ! current_user_can( 'edit_posts' ) ) { return false; } if ( $data['id'] && ! is_wp_error( $data['id'] ) ) { if ( get_post_type( $data['id'] ) !== Visualizer_Plugin::CPT_VISUALIZER ) { return new WP_Error( 'invalid_post_type', 'Invalid post type.' ); } $chart_type = sanitize_text_field( $data['visualizer-chart-type'] ); $source_type = sanitize_text_field( $data['visualizer-source'] ); $default_data = (int) $data['visualizer-default-data']; $series_data = map_deep( $data['visualizer-series'], array( $this, 'sanitize_value' ) ); $settings_data = map_deep( $data['visualizer-settings'], array( $this, 'sanitize_value' ) ); update_post_meta( $data['id'], Visualizer_Plugin::CF_CHART_TYPE, $chart_type ); update_post_meta( $data['id'], Visualizer_Plugin::CF_SOURCE, $source_type ); update_post_meta( $data['id'], Visualizer_Plugin::CF_DEFAULT_DATA, $default_data ); update_post_meta( $data['id'], Visualizer_Plugin::CF_SERIES, $series_data ); update_post_meta( $data['id'], Visualizer_Plugin::CF_SETTINGS, $settings_data );
Security Fix
@@ -284,117 +284,6 @@ register_rest_route( 'visualizer/v' . VISUALIZER_REST_VERSION, - '/update-chart', - array( - 'methods' => 'POST', - 'callback' => array( $this, 'update_chart_data' ), - 'args' => array( - 'id' => array( - 'sanitize_callback' => 'absint', - ), - ), - 'permission_callback' => function () { - return current_user_can( 'edit_posts' ); - }, - ) - ); -
Exploit Outline
An attacker with Contributor-level access or higher can exploit this vulnerability by submitting a malicious payload to the `/wp-json/visualizer/v1/update-chart` REST API endpoint. The payload should target chart metadata or data fields, such as `visualizer-settings` or `visualizer-series`. For example, a POST request with the chart ID and a JSON payload containing `{"visualizer-settings": {"title": "<script>alert('XSS')</script>"}}` can be sent using a valid WordPress REST API nonce. Once saved, the script will execute whenever an administrator or another user views the post or page where the specific chart is embedded.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.