My Calendar <= 3.7.14 - Insecure Direct Object Reference to Unauthenticated Sensitive Information Disclosure via 'vcal' Parameter
Description
The My Calendar – Accessible Event Manager plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 3.7.14 via the 'vcal' parameter due to missing validation on a user controlled key. This makes it possible for unauthenticated attackers to enumerate occurrence IDs and access the full iCalendar export of non-public, draft, trashed, and personal calendar events, disclosing sensitive event metadata including titles, descriptions, dates, locations, organizer and host details, permalinks, and related calendar metadata.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NTechnical Details
What Changed in the Fix
Changes introduced in v3.7.15
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-11896 (My Calendar IDOR) ## 1. Vulnerability Summary The **My Calendar** plugin for WordPress (versions <= 3.7.14) contains an Insecure Direct Object Reference (IDOR) vulnerability. The plugin exposes an iCalendar (.ics) export feature triggered by the `vcal` …
Show full research plan
Exploitation Research Plan: CVE-2026-11896 (My Calendar IDOR)
1. Vulnerability Summary
The My Calendar plugin for WordPress (versions <= 3.7.14) contains an Insecure Direct Object Reference (IDOR) vulnerability. The plugin exposes an iCalendar (.ics) export feature triggered by the vcal parameter. This feature fails to perform authorization checks on the event ID (occurrence ID) provided by the user. Consequently, unauthenticated attackers can access sensitive metadata for events that should be restricted, including Drafts, Trashed events, and Private events, by simply knowing or enumerating the ID.
2. Attack Vector Analysis
- Endpoint: The main WordPress frontend (
/orindex.php). - HTTP Parameter:
vcal(GET parameter). - Value: An integer representing the
occurrence_idof a calendar event. - Authentication: None (Unauthenticated).
- Preconditions: The plugin must be active, and at least one non-public event (Draft, Private, or Trashed) must exist in the database.
3. Code Flow (Inferred)
Since the specific export handler file was not provided in the source dump but is referenced in the vulnerability description, the logic flow is as follows:
- Entry Point: The plugin likely hooks into
initorwp_loadedto monitor for thevcalparameter. - Handler: A function (likely
my_calendar_vcal()or similar) identifies the presence of$_GET['vcal']. - Data Retrieval: The code takes the integer from
vcaland queries thewp_my_calendar(orwp_my_calendar_event) table to retrieve the event details. - Vulnerability: The code proceeds to generate the ICS output headers (
text/calendar) and event body without calling authorization checks likemc_event_is_hidden()ormc_event_published()(found inincludes/conditionals.php). - Sink: The event metadata is echoed to the output buffer, ending execution with
exit;ordie;.
4. Nonce Acquisition Strategy
According to the vulnerability description and standard behavior of iCal export links in this plugin, no nonce is required for the vcal parameter. These links are typically intended to be shareable/publicly accessible for calendar synchronization.
If a nonce were required, it would be found in the frontend script localization or a hidden field. To verify:
- Navigate to a public calendar page.
- Use
browser_evalto search formc_prefixed variables:browser_eval("window.my_calendar_vars || window.mc_data")
However, for this specific CVE (IDOR to sensitive info), the exploit is expected to be a direct GET request.
5. Exploitation Strategy
The goal is to demonstrate that an unauthenticated request can retrieve an event that is NOT in a 'Public' state.
Step-by-Step Plan:
- Identify Occurrence ID: Use WP-CLI to find the ID of a "Draft" or "Private" event.
- Request Export: Send a GET request to the homepage with the
vcalparameter set to that ID. - Verify Content: Check if the response contains the
BEGIN:VCALENDARheader and the private event's title/description.
Request Details:
- URL:
http://localhost:8080/index.php?vcal=[ID] - Method:
GET - Headers:
Accept: text/calendar(Optional, as the plugin forces the header).
6. Test Data Setup
The PoC requires events with different statuses to prove the bypass.
# 1. Create a Public event (to find the baseline ID range)
wp eval "mc_insert_event(array('event_title'=>'Public Event','event_desc'=>'Visible','event_begin'=>'2026-01-01','event_time'=>'12:00:00','event_end'=>'2026-01-01','event_endtime'=>'13:00:00','event_approved'=>1));"
# 2. Create a Draft event (event_approved = 0)
wp eval "mc_insert_event(array('event_title'=>'Secret Draft','event_desc'=>'Confidential Info','event_begin'=>'2026-02-01','event_time'=>'12:00:00','event_end'=>'2026-02-01','event_endtime'=>'13:00:00','event_approved'=>0));"
# 3. Create a Trashed event (event_approved = 2)
wp eval "mc_insert_event(array('event_title'=>'Trashed Event','event_desc'=>'Should be gone','event_begin'=>'2026-03-01','event_time'=>'12:00:00','event_end'=>'2026-03-01','event_endtime'=>'13:00:00','event_approved'=>2));"
# 4. Identify the occurrence IDs
# My Calendar stores occurrences in a separate table. We need the 'occur_id' from wp_my_calendar.
wp db query "SELECT occur_id, event_title, event_approved FROM wp_my_calendar INNER JOIN wp_my_calendar_events ON wp_my_calendar.occur_event_id = wp_my_calendar_events.event_id"
7. Expected Results
- Unauthenticated Request: A request to
?vcal=[DRAFT_ID]should return an HTTP 200 response withContent-Type: text/calendar. - Information Disclosure: The body of the response should contain:
SUMMARY:Secret DraftDESCRIPTION:Confidential Info
- Baseline Comparison: A properly secured application would either return a 404, a 403, or a generic calendar file with no event data for a draft ID when requested by an unauthenticated user.
8. Verification Steps
- Check Response Headers:
http_requestshould showContent-Type: text/calendar; charset=UTF-8. - Verify Occurrence Mapping: Use WP-CLI to confirm the
occur_idused in the request corresponds to the draft event:wp db query "SELECT event_approved FROM wp_my_calendar_events WHERE event_id = (SELECT occur_event_id FROM wp_my_calendar WHERE occur_id = [ID])" - Confirm Lack of Authentication: Ensure the
http_requestwas made without anyCookieheaders representing a logged-in user.
9. Alternative Approaches
If the vcal parameter expects an event_id rather than an occur_id, or if the parameter is handled differently:
- Try
?vcal=1(standard IDOR enumeration). - Search the source for
add_action( 'init'oradd_action( 'wp_loaded'to find the exact function handling thevcalkey. - If
vcalis used in a redirect context (e.g., generating a file then redirecting), check if the file path is predictable inwp-content/uploads/. (Unlikely given the "sensitive information disclosure" via parameter description).
Summary
The My Calendar plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) flaw in the iCalendar export functionality. Unauthenticated attackers can disclose sensitive event details—including those of draft, private, or trashed events—by supplying a specific occurrence ID to the 'vcal' parameter, as the plugin lacks authorization checks on the event's visibility status.
Security Fix
@@ -218,6 +218,10 @@ color: var(--primary-dark); } +.mc_edit_links { + clear: both; +} + .mc_edit_links ul { display: flex; gap: 10px; @@ -553,6 +557,7 @@ font-style: italic; } +.single-mc-event .mc-image, .mc-main .details .mc-image { max-width: 100%; height: auto; ... (truncated)
Exploit Outline
The exploit targets the iCalendar export feature triggered by the 'vcal' GET parameter. 1. Endpoint: The attacker targets the WordPress root (index.php) or any page where the My Calendar plugin is active. 2. Methodology: An unauthenticated attacker sends a GET request with the 'vcal' parameter set to an integer representing an event's occurrence ID (e.g., /?vcal=123). 3. Authentication: No authentication or specific nonces are required to access this endpoint. 4. Payload/Bypass: By enumerating occurrence IDs, the attacker can bypass visibility restrictions. The plugin fails to verify if the event status is 'Public' or if the user has permission to view the event, causing the server to return the full event metadata (including sensitive summaries and descriptions) in a 'text/calendar' (ICS) file format even for Drafts or Private events.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.