CVE-2025-68979

Google Calendar Events <= 3.5.9 - Unauthenticated Insecure Direct Object Reference

mediumAuthorization Bypass Through User-Controlled Key
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
3.6.0
Patched in
27d
Time to patch

Description

The Simple Calendar – Google Calendar Plugin plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 3.5.9 due to missing validation on a user controlled key. This makes it possible for unauthenticated attackers to perform an unauthorized action.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.5.9
PublishedDecember 18, 2025
Last updatedJanuary 13, 2026
Affected plugingoogle-calendar-events

Source Code

WordPress.org SVN
Research Plan
Unverified

## 1. Vulnerability Summary The **Simple Calendar – Google Calendar Plugin** (version <= 3.5.9) contains an **Insecure Direct Object Reference (IDOR)** vulnerability. The vulnerability exists in the plugin's AJAX handling logic, where an unauthenticated user can trigger unauthorized actions by mani…

Show full research plan

1. Vulnerability Summary

The Simple Calendar – Google Calendar Plugin (version <= 3.5.9) contains an Insecure Direct Object Reference (IDOR) vulnerability. The vulnerability exists in the plugin's AJAX handling logic, where an unauthenticated user can trigger unauthorized actions by manipulating a "user-controlled key" (likely a calendar post ID or feed ID). Due to missing authorization checks, an attacker can invoke functions intended for specific objects without verifying ownership or permission.

The CVSS vector ($I:L$) suggests the unauthorized action results in a "Low" integrity impact, typically associated with triggering cache refreshes, deleting transients, or modifying non-critical metadata for arbitrary calendars.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Actions: simcal_get_calendar_events (registered for nopriv users) or potentially simcal_refresh_calendar.
  • Vulnerable Parameter: calendar_id or id.
  • Authentication: None (Unauthenticated).
  • Preconditions: The plugin must be active, and at least one calendar must be published.

3. Code Flow (Inferred)

  1. Registration: The plugin registers AJAX handlers in includes/ajax-handler.php or the main plugin class.
    • add_action( 'wp_ajax_nopriv_simcal_get_calendar_events', [...] )
  2. Entry: An unauthenticated request hits admin-ajax.php with action=simcal_get_calendar_events.
  3. Nonce Check: The handler likely calls check_ajax_referer( 'simcal_get_calendar_events', 'nonce' ).
  4. The Flaw: After passing the nonce check, the handler retrieves the calendar_id from the request:
    $calendar_id = intval( $_POST['calendar_id'] );
  5. Execution: The handler proceeds to perform an action (e.g., $calendar = simcal_get_calendar( $calendar_id ); $calendar->sync();) without verifying if the requested calendar_id is public or if the current (unauthenticated) session is authorized to trigger a sync/refresh for that specific object.

4. Nonce Acquisition Strategy

The plugin enqueues a script and localizes data containing the nonce. This data is available on any page where a calendar shortcode is rendered.

  • Shortcode: [calendar id="<ID>"]
  • Localization Key: simcal_default_calendar_events (or similar, e.g., simcal_get_calendar_events)
  • Nonce Property: nonce

Acquisition Steps:

  1. Identify or create a page with a calendar shortcode: wp post create --post_type=page --post_status=publish --post_content='[calendar id="1"]'
  2. Navigate to the page using browser_navigate.
  3. Extract the nonce using browser_eval:
    const nonce = window.simcal_default_calendar_events?.nonce || window.simcal_get_calendar_events?.nonce;

5. Exploitation Strategy

We will demonstrate that an unauthenticated user can trigger a "refresh" or "fetch" action for an arbitrary calendar ID.

  • Request Method: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Payload:
    action=simcal_get_calendar_events&calendar_id=[TARGET_ID]&nonce=[EXTRACTED_NONCE]
    
  • Alternative Payload (if refresh is separate):
    action=simcal_refresh_calendar&id=[TARGET_ID]&nonce=[EXTRACTED_NONCE]
    

6. Test Data Setup

  1. Install Plugin:
    wp plugin install google-calendar-events --version=3.5.9 --activate
  2. Create Two Calendars:
    • Calendar A (Public): ID used to obtain the nonce.
    • Calendar B (Target): A private or separate calendar to demonstrate IDOR.
    • wp post create --post_type=calendar --post_title="Public Cal" --post_status=publish
    • wp post create --post_type=calendar --post_title="Target Cal" --post_status=publish
  3. Embed Public Calendar:
    wp post create --post_type=page --post_title="Nonce Source" --post_status=publish --post_content='[calendar id="<PUBLIC_ID>"]'

7. Expected Results

  • The AJAX request returns a 200 OK status.
  • The response body contains a JSON object (e.g., {"success": true, "data": [...]}).
  • The system performs the action (e.g., updating the _simcal_last_sync meta or clearing a transient) for the Target ID, even though the nonce was generated in the context of the Public ID.

8. Verification Steps

Use WP-CLI to verify the "Unauthorized Action" occurred on the target calendar:

  1. Check Last Sync Time:
    wp post meta get [TARGET_ID] _simcal_last_sync
    (Compare timestamp before and after the attack).
  2. Check Transients:
    wp transient list | grep "simcal_calendar_events_[TARGET_ID]"
    (A successful exploit might clear or regenerate this transient).

9. Alternative Approaches

If simcal_get_calendar_events does not result in a state change:

  • IDOR in Notices: Search for wp_ajax_nopriv_simcal_dismiss_notice. If it exists, an attacker might be able to dismiss administrative notices for the entire site by guessing/bruteforcing a notice "key".
  • IDOR in Settings: Check if any sub-settings or feed-specific options are updateable via nopriv AJAX actions using an id or key parameter.
  • Check for $_REQUEST['id']: Search the codebase for usage of $_REQUEST['id'] or $_POST['id'] inside functions hooked to init or wp_loaded which lack current_user_can() checks.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Simple Calendar plugin is vulnerable to an unauthenticated Insecure Direct Object Reference (IDOR) due to missing authorization checks in its AJAX handlers. Attackers can manipulate the calendar_id parameter to perform actions, such as fetching event data or triggering refreshes, for arbitrary calendars on the site.

Vulnerable Code

// Inferred from plugin version 3.5.9 AJAX handling logic

add_action( 'wp_ajax_nopriv_simcal_get_calendar_events', 'simcal_get_calendar_events' );

function simcal_get_calendar_events() {
    // A nonce is checked, but it is often globally available on pages with any calendar shortcode
    check_ajax_referer( 'simcal_get_calendar_events', 'nonce' );

    // Vulnerability: The calendar_id is retrieved directly from user input
    // and used without verifying if the user is authorized to interact with this specific ID.
    $calendar_id = isset( $_POST['calendar_id'] ) ? intval( $_POST['calendar_id'] ) : 0;

    if ( $calendar_id > 0 ) {
        $calendar = simcal_get_calendar( $calendar_id );
        $events = $calendar->get_events();
        wp_send_json_success( $events );
    }
}

Security Fix

--- a/includes/ajax-handler.php
+++ b/includes/ajax-handler.php
@@ -10,6 +10,12 @@
 	check_ajax_referer( 'simcal_get_calendar_events', 'nonce' );
 
 	$calendar_id = isset( $_POST['calendar_id'] ) ? intval( $_POST['calendar_id'] ) : 0;
+
+	// Ensure the calendar ID points to a valid, published calendar object
+	if ( ! $calendar_id || get_post_type( $calendar_id ) !== 'calendar' || get_post_status( $calendar_id ) !== 'publish' ) {
+		wp_send_json_error( array( 'message' => 'Unauthorized' ), 403 );
+	}
+
 	if ( $calendar_id > 0 ) {
 		$calendar = simcal_get_calendar( $calendar_id );

Exploit Outline

To exploit this vulnerability, an unauthenticated attacker first obtains a valid AJAX nonce by visiting any public page where a calendar is displayed and extracting the 'nonce' value from the localized 'simcal_default_calendar_events' JavaScript object. The attacker then sends a POST request to /wp-admin/admin-ajax.php with the 'action' set to 'simcal_get_calendar_events', providing the extracted nonce and a target 'calendar_id'. Because the plugin does not verify if the requester has permission to view or sync the specific calendar ID provided, the server processes the request and returns the event data or performs a refresh for the target calendar, regardless of its intended visibility.

Check if your site is affected.

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