My Calendar – Accessible Event Manager <= 3.7.3 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes
Description
The My Calendar – Accessible Event Manager plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the `template` attribute of the `[my_calendar_upcoming]` shortcode in all versions up to, and including, 3.7.3. This is due to the use of `stripcslashes()` on user-supplied shortcode attribute values in the `mc_draw_template()` function, which decodes C-style hex escape sequences (e.g., `\x3c` to `<`) at render time, bypassing WordPress's `wp_kses_post()` content sanitization that runs at save time. 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 v3.7.4
Source Code
WordPress.org SVNThis research plan targets **CVE-2026-2355**, a stored Cross-Site Scripting (XSS) vulnerability in the "My Calendar" plugin. The vulnerability arises because the plugin uses `stripcslashes()` on shortcode attributes during the rendering process, which allows an attacker to bypass WordPress's default…
Show full research plan
This research plan targets CVE-2026-2355, a stored Cross-Site Scripting (XSS) vulnerability in the "My Calendar" plugin. The vulnerability arises because the plugin uses stripcslashes() on shortcode attributes during the rendering process, which allows an attacker to bypass WordPress's default sanitization by using C-style hex or octal escape sequences.
1. Vulnerability Summary
- Vulnerability: Authenticated (Contributor+) Stored XSS.
- Vulnerable Component:
[my_calendar_upcoming]shortcode. - Affected Attribute:
template. - Sink Function:
stripcslashes()withinmc_draw_template()(inferred from description). - Root Cause: WordPress's
wp_kses_post()(run at save-time) does not identify\x3cas the start of an HTML tag (<). However, the plugin processes this string at render-time usingstripcslashes(), converting hex escapes back into functional HTML/JavaScript, which is then echoed to the page without further escaping.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/post-new.phpor/wp-admin/post.php. - Action: Creating or editing a post/page.
- Required Role: Contributor or higher (any role capable of using shortcodes).
- Payload Location: The
templateattribute of the[my_calendar_upcoming]shortcode. - Precondition: At least one future event must exist in My Calendar for the "upcoming events" shortcode to render the malicious template.
3. Code Flow
- Save Time: A Contributor saves a post containing:
[my_calendar_upcoming template="\x3cscript\x3ealert(document.domain)\x3c/script\x3e"]. - Sanitization: WordPress runs
wp_kses_post(). Since\x3cis just text, it is not stripped. - Render Time: A user views the post. WordPress calls
do_shortcode(). - Shortcode Handling: The plugin's handler for
my_calendar_upcomingis invoked. - Template Processing: The handler passes the
templateattribute value tomc_draw_template(). - Vulnerable Sink:
mc_draw_template()executes$template = stripcslashes( $template );. - Transformation:
\x3cscript\x3ebecomes<script>. - Output: The decoded HTML is echoed into the page content.
4. Nonce Acquisition Strategy
This vulnerability does not require a plugin-specific nonce for exploitation because it relies on the standard WordPress post-saving mechanism and shortcode rendering.
- Post Saving: Handled by the agent using standard WordPress authentication.
- Rendering: Shortcodes are processed automatically when a post is viewed; no nonce is required for
GETrequests to public posts.
Note: If testing the AJAX loading functionality of upcoming events (as seen in js/mcjs.js), the my_calendar.action and potential nonces can be found in the window.my_calendar object.
5. Exploitation Strategy
The goal is to inject a script that executes in the context of any user viewing the page.
Step 1: Create a placeholder event
The shortcode [my_calendar_upcoming] only renders its template if there are upcoming events.
- Use
wp_clito create a future event. - Command:
wp eval "my_calendar_save_event(array('event_title'=>'Test Event','event_desc'=>'Test','event_begin'=>'2029-01-01','event_end'=>'2029-01-01','event_time'=>'12:00:00','event_endtime'=>'13:00:00','event_author'=>1), 'add');"
Step 2: Create the malicious post
- As a Contributor, create a new post.
- Payload:
[my_calendar_upcoming template="\x3cimg src=x onerror=alert(origin)\x3e"] - Submit via
http_requesttowp-admin/post.php.
Step 3: Trigger the XSS
- Navigate to the permalink of the created post.
- The browser will process the shortcode,
stripcslashes()will decode the payload, and theonerrorevent will fire.
6. Test Data Setup
- Users:
contributor(to create the post).admin(to verify the stored XSS fires in a high-privilege context).
- Event: Create one event with a start date significantly in the future using the
wp_clicommand mentioned in Step 5.1. - Shortcode Usage:
[my_calendar_upcoming template="\x3cscript\x3econsole.log('CVE-2026-2355_Exploited')\x3c/script\x3e"]
7. Expected Results
- When saving the post, the response should be a successful
302redirect to the post editor. - When viewing the post frontend, the HTML source code should contain:
(Instead of the literal<img src=x onerror=alert(origin)>\x3cimg...) - The
alertorconsole.logshould execute in the browser.
8. Verification Steps
- Database Check: Verify the post content in the database still contains the backslashes (confirming bypass of save-time sanitization):
wp db query "SELECT post_content FROM wp_posts WHERE post_title='Exploit Post'" - DOM Inspection: Use
browser_evalto check if the injected element exists:browser_eval("document.querySelector('img[onerror*=\"alert\"]') !== null")
9. Alternative Approaches
If the template attribute is somehow restricted, explore other attributes of [my_calendar_upcoming] or [my_calendar] that might pass through the same mc_draw_template logic.
Payload variants:
- Octal escapes:
\134x3cscript\134x3ealert(1)\134x3c/script\134x3e(If double-stripping occurs). - Attribute Breakout: If the template is used inside an existing tag, use
\x22 onmouseover=\x22alert(1)\x22to break out of an attribute.
AJAX Vector:
If the plugin is configured for AJAX (my_calendar.ajax === 'true'), the loadUpcoming function in js/mcjs.js might be used to trigger the XSS.
- Navigate to a page with the shortcode.
- Trigger the AJAX call:
browser_click(".mc-loader"). - Observe if the
results.responseinjected viaparent.innerHTML += results.response;contains the decoded payload.
Summary
The My Calendar plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'template' attribute of the [my_calendar_upcoming] shortcode. Authenticated attackers with Contributor-level access or higher can bypass WordPress's default sanitization by using C-style hex escape sequences that the plugin decodes at render-time using stripcslashes().
Vulnerable Code
// In mc_draw_template() within the plugin's rendering logic // This function processes shortcode attribute values before outputting them to the page. function mc_draw_template( $data, $template ) { // ... $template = stripcslashes( $template ); // ... return $template; }
Security Fix
@@ -455,1 +455,1 @@ - $template = stripcslashes( $template ); + $template = $template;
Exploit Outline
The exploit requires an authenticated attacker with at least Contributor-level permissions (capable of using shortcodes). 1. Precondition: At least one upcoming event must be present in the My Calendar system to ensure the 'upcoming events' shortcode renders its template. 2. Payload Creation: The attacker creates a post or page and includes the shortcode [my_calendar_upcoming]. 3. Injection: In the 'template' attribute of the shortcode, the attacker provides a hex-encoded HTML payload, such as: [my_calendar_upcoming template="\x3cimg src=x onerror=alert(origin)\x3e"]. 4. Bypass: When the post is saved, WordPress's wp_kses_post() sanitization ignores the hex sequences (\x3c) as they do not look like HTML tags. 5. Execution: When any user views the post, the plugin's mc_draw_template() function executes stripcslashes() on the attribute value, converting '\x3c' back into '<' and rendering the malicious script directly into the page.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.