My Calendar <= 3.7.9 - Authenticated (Custom+) Missing Authorization to Unauthorized Event Publication via 'event_approved' Parameter
Description
The My Calendar – Accessible Event Manager plugin for WordPress is vulnerable to authorization bypass in all versions up to, and including, 3.7.9. This is due to the plugin not properly verifying that a user is authorized to perform an action. This makes it possible for authenticated attackers, with custom-level access and above, to bypass the moderation and approval workflow by tampering with the POST body to publish events or set other unauthorized statuses such as cancelled or private, in ways their role does not permit. While the UI correctly restricts low-privilege users to a draft-only submit button, this restriction is enforced only client-side, making it trivially bypassable by directly manipulating the POST request.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v3.7.10
Source Code
WordPress.org SVN# Vulnerability Research Plan: CVE-2026-7525 ## 1. Vulnerability Summary The **My Calendar** plugin for WordPress is vulnerable to a missing authorization check in versions up to and including **3.7.9**. While the administrative UI restricts low-privilege users (those with "Custom" access like `mc_…
Show full research plan
Vulnerability Research Plan: CVE-2026-7525
1. Vulnerability Summary
The My Calendar plugin for WordPress is vulnerable to a missing authorization check in versions up to and including 3.7.9. While the administrative UI restricts low-privilege users (those with "Custom" access like mc_add_events) to only submitting drafts for review, the backend fails to validate if the user has the authority to set the event_approved status. By intercepting and modifying the POST request during event creation or editing, an authenticated attacker can bypass the moderation workflow to directly publish events (event_approved=1) or set other unauthorized statuses.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin.php?page=my-calendar(Administrative event management page) - Vulnerable Parameter:
event_approved - Required Authentication: Authenticated user with at least
mc_add_eventscapability (Custom level). - Payload Type: Form-data (POST body)
- Preconditions: The user must be logged in and have permission to access the "Add Event" or "Edit Event" page.
3. Code Flow
- Entry Point: A user accesses the "Add Event" page (
admin.php?page=my-calendar&mode=add). - Submission: The user submits the event form. The browser sends a POST request to the same URL or the management handler.
- Processing (Inferred): The plugin processes the submission (likely in a function handling
mode=addormode=edit). - Missing Check: The code extracts
$_POST['event_approved']. While the UI only provides a button that sendsevent_approved=0(Draft) for low-privilege users, the backend code fails to callcurrent_user_can( 'mc_approve_events' )before assigning theevent_approvedvalue to the event object being saved to the database. - Sink: The
my_calendar_event_tableis updated with the user-suppliedevent_approvedvalue.
4. Nonce Acquisition Strategy
My Calendar uses standard WordPress nonces for its admin forms. To obtain a valid nonce for the "Add Event" action:
- Login: Use the security agent to log in as a user with the
mc_add_eventscapability. - Navigate: Navigate to the "Add Event" page:
/wp-admin/admin.php?page=my-calendar&mode=add. - Extract: Use
browser_evalto extract the nonce and standard form values.- The nonce is typically in a hidden input named
_wpnonce. - Command:
browser_eval("document.querySelector('input[name=\"_wpnonce\"]').value")
- The nonce is typically in a hidden input named
5. Exploitation Strategy
The goal is to create a published event as a user who should only be allowed to create drafts.
- Setup User: Create a user with the
Subscriberrole and grant them themc_add_eventscapability using WP-CLI. - Capture Form Data:
- Navigate to
/wp-admin/admin.php?page=my-calendar&mode=add. - Extract the
_wpnonce. - Identify other required fields:
event_title,event_begin,event_time,event_end,event_endtime.
- Navigate to
- Craft Malicious POST:
- Submit a POST request to
/wp-admin/admin.php?page=my-calendar. - Include
mode=add. - Include
event_title=Malicious Published Event. - Set
event_approved=1(The bypass). - Include required dates/times (e.g.,
event_begin=2025-10-10,event_time=12:00:00).
- Submit a POST request to
- Execute Request: Use the
http_requesttool to send the authenticated POST request.
Example Payload (POST Body):
_wpnonce=[NONCE]&mode=add&event_title=Bypassed+Event&event_desc=This+event+should+be+a+draft&event_begin=2025-01-01&event_time=12%3A00%3A00&event_end=2025-01-01&event_endtime=13%3A00%3A00&event_approved=1&save=Add+Event
6. Test Data Setup
- Plugin Activation: Ensure "My Calendar" is active.
- Create Attacker User:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password - Grant Permissions: Grant the specific My Calendar capability. My Calendar maps its internal capabilities to WordPress roles/caps.
# Granting the capability required to add events wp user add-cap attacker mc_add_events - Confirm Permissions: Ensure the user can reach the "Add Event" page but cannot "Approve" (published status).
7. Expected Results
- The server should respond with a
302 Redirect(standard WordPress admin behavior) or a success message indicating the event was added. - The event should be visible on the public calendar immediately without administrative approval.
- Checking the database will show
event_approved = 1.
8. Verification Steps
- Check Database:
wp db query "SELECT event_title, event_approved FROM $(wp db prefix)my_calendar WHERE event_title = 'Bypassed Event'"- Success Criteria:
event_approvedis1.
- Success Criteria:
- Check Visibility:
- Use
mc_event_publishedlogic (fromincludes/conditionals.php) or simply check the front-end calendar page if it exists. - Navigate to the homepage/calendar page and verify the "Bypassed Event" title appears.
- Use
9. Alternative Approaches
- Edit Existing Draft: If "Add Event" is hardened, attempt to edit an existing draft event (
mode=edit&event_id=X) and changeevent_approvedfrom0to1. - Unauthorized Statuses: Instead of
1(Published), attempt to setevent_approvedto2(Draft/Pending),3(Cancelled), or4(Hidden/Private) to disrupt site operations. - AJAX Endpoint: Check if
admin-ajax.phphandlers exist for event saving that might also be missing authorization checks for theevent_approvedfield. (Checkincludes/class-mc-ajax.phpif present in full source).
Summary
The My Calendar plugin for WordPress is vulnerable to an authorization bypass where authenticated users with event creation permissions can directly publish events by tampering with the 'event_approved' POST parameter. Because the plugin only enforces the moderation workflow on the client-side (via UI buttons) and lacks a server-side capability check for event approval, users can bypass administrative review to publish, cancel, or hide events.
Security Fix
@@ -306,9 +306,13 @@ #event_date_error.visible { opacity: 1; + display: flex; + align-items: center; } -#event_date_error .dashicons { +#event_date_error.visible::before { + content: "\f158"; + font-family: dashicons; color: #a00; } @@ -316,6 +320,11 @@ opacity: 0; } +#my-calendar .duet-date__input { + border-radius: 0 !important; + padding: 11px 60px 10px 14px; +} + #e_schedule .columns label:not(.duet-date__mobile-heading), .recurrences .columns label:not(.duet-date__mobile-heading), .recurring .columns label:not(.duet-date__mobile-heading) { @@ -323,6 +332,12 @@ max-width: fit-content; } +.recurrences-disabled { + display: flex; + align-items: center; + gap: .5rem; +} + #e_schedule .columns .checkboxes label { display: inline; } @@ -345,18 +360,6 @@ outline-offset: 2px; } -.recurrences input[type=time], -#e_schedule input[type=time], -.my-calendar-admin input[type=date], -.recurring select { - padding: 8px 24px 8px 8px; -} - -.recurring input[type=number] { - padding: 8px; - width: 5em; -} - #event1 .new-field { display: none; } @@ -367,11 +370,9 @@ } p.event_span.checkboxes { - border: 1px solid #c3c4c7; - border-radius: 3px; - padding: 1em 1em .5em; - background: #f0f0f1; - margin-bottom: 1em; + padding: 1em; + background: rgba( 0, 0, 0, .04 ); + align-items: center; } ol.mc-repeat-events { @@ -1614,10 +1615,6 @@ line-height: normal; } -select[name="event_recur"] { - vertical-align: top; -} - .my-calendar-admin li.events_access_notes { padding: 0px 1px 1px 5px; gap: 5px; ... (truncated)
Exploit Outline
To exploit this vulnerability, an attacker must have an account with permissions to add events (e.g., the 'mc_add_events' capability). The attacker first logs into the WordPress admin panel and navigates to the 'Add Event' page (`admin.php?page=my-calendar&mode=add`) to retrieve a valid security nonce (`_wpnonce`). They then craft a POST request to the same endpoint, providing all necessary event details (title, description, dates, times). By adding the parameter `event_approved=1` to the POST body, the attacker overrides the default draft status. Upon submission, the plugin accepts the user-provided approval status without verifying if the user has the 'mc_approve_events' capability, resulting in the event being published immediately on the public-facing calendar.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.