CVE-2025-12898

Pretty Google Calendar <= 2.0.0 - Missing Authorization to Unauthenticated Google API Key Exposure

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
2.0.1
Patched in
18d
Time to patch

Description

The Pretty Google Calendar plugin for WordPress is vulnerable to unauthorized access of data due to a missing capability check on the pgcal_ajax_handler() function in all versions up to, and including, 2.0.0. This makes it possible for unauthenticated attackers to retrieve the Google API key set in the plugin's settings.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=2.0.0
PublishedDecember 19, 2025
Last updatedJanuary 6, 2026
Affected pluginpretty-google-calendar

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-12898 (Pretty Google Calendar API Key Exposure) ## 1. Vulnerability Summary The **Pretty Google Calendar** plugin (up to version 2.0.0) contains a "Missing Authorization" vulnerability in its AJAX handler function `pgcal_ajax_handler()`. The function is regist…

Show full research plan

Exploitation Research Plan: CVE-2025-12898 (Pretty Google Calendar API Key Exposure)

1. Vulnerability Summary

The Pretty Google Calendar plugin (up to version 2.0.0) contains a "Missing Authorization" vulnerability in its AJAX handler function pgcal_ajax_handler(). The function is registered for both authenticated (wp_ajax_) and unauthenticated (wp_ajax_nopriv_) users but fails to implement any current_user_can() capability checks. Consequently, an unauthenticated attacker can trigger the AJAX action to retrieve sensitive plugin settings, specifically the stored Google API Key.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Method: POST (typically for WordPress AJAX)
  • Action: pgcal_ajax_handler
  • Parameters:
    • action: pgcal_ajax_handler
    • nonce: (likely required, see Nonce Strategy)
    • sub_action: (inferred) The handler likely uses a sub-parameter to determine which data to return. Based on the plugin's purpose, this may be get_settings, fetch_config, or similar.
  • Authentication: None required (unauthenticated).
  • Preconditions: The Google API key must be saved in the plugin settings.

3. Code Flow (Inferred from Patch Description)

  1. Registration: The plugin registers the handler:
    add_action('wp_ajax_pgcal_ajax_handler', 'pgcal_ajax_handler');
    add_action('wp_ajax_nopriv_pgcal_ajax_handler', 'pgcal_ajax_handler');
    
  2. Execution: When a request is sent to admin-ajax.php?action=pgcal_ajax_handler, the pgcal_ajax_handler() function is executed.
  3. Vulnerability: The function likely checks for a nonce but does not check if the user has manage_options or similar administrative capabilities.
  4. Data Retrieval: The function calls get_option('pgcal_api_key') (or a similar option name like pgcal_settings) and returns the value via wp_send_json().

4. Nonce Acquisition Strategy

The plugin likely uses wp_localize_script to provide the AJAX URL and a nonce to the frontend for calendar rendering.

  1. Identify Trigger: The plugin scripts are usually loaded on pages containing the Google Calendar shortcode.
    • Shortcode (inferred): [pretty-google-calendar]
  2. Creation: Create a public page with the shortcode using WP-CLI:
    wp post create --post_type=page --post_status=publish --post_title="Calendar Test" --post_content='[pretty-google-calendar]'
  3. Navigation: Use the browser_navigate tool to visit the newly created page.
  4. Extraction: Use browser_eval to find the localized object. Common identifiers for this plugin:
    • window.pgcal_obj?.nonce
    • window.pgcal_settings?.ajax_nonce
    • window.pgcal_vars?.nonce
    • Search Pattern: browser_eval("Object.keys(window).find(k => k.includes('pgcal'))") to find the object name, then inspect its keys.

5. Exploitation Strategy

  1. Preparation: Configure the plugin with a dummy API key to simulate a vulnerable target.
  2. Nonce Retrieval: Follow the steps in Section 4 to obtain a valid nonce for the pgcal_ajax_handler action.
  3. Request Construction: Send a POST request to admin-ajax.php.
    • Tool: http_request
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body: action=pgcal_ajax_handler&nonce=[EXTRACTED_NONCE]&pgcal_action=get_api_key (The specific sub-action parameter name and value must be verified by auditing the pgcal_ajax_handler function body in the source).
  4. Alternative Parameters: If pgcal_action is incorrect, check the source code for the $_POST or $_REQUEST keys used inside pgcal_ajax_handler().

6. Test Data Setup

  1. Install Plugin: Install Pretty Google Calendar <= 2.0.0.
  2. Set API Key:
    wp option update pgcal_api_key "AIzaSy_TEST_KEY_EXPOSURE_2025"
    (Note: If the key is stored inside an array, use wp option patch insert pgcal_settings api_key "value" ).
  3. Create Page:
    wp post create --post_type=page --post_status=publish --post_content='[pretty-google-calendar]' --post_title='Exploit Test'

7. Expected Results

  • Response Code: 200 OK
  • Response Body: A JSON object containing the API key.
    {
        "success": true,
        "data": {
            "api_key": "AIzaSy_TEST_KEY_EXPOSURE_2025"
        }
    }
    

8. Verification Steps

  1. Confirm Exposure: Check the output of the http_request tool to ensure the dummy API key matches the one set in the database.
  2. CLI Verification: Verify the stored key via WP-CLI to confirm what was targeted:
    wp option get pgcal_api_key

9. Alternative Approaches

  • Missing Nonce: If pgcal_ajax_handler does not call check_ajax_referer, the exploit can be performed without Section 4, using only the action parameter.
  • Different Sub-actions: If the API key is not returned by default, the handler might require a specific "cmd" or "type" parameter. Search the source for:
    grep -r "pgcal_ajax_handler" .
    then look for $_POST access within that function to identify the routing parameters.
  • Settings Object: The plugin might return the entire settings array, which would expose other configuration details alongside the API key.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Pretty Google Calendar plugin for WordPress (versions up to 2.0.0) exposes the `pgcal_ajax_handler` AJAX action to unauthenticated users. Due to a lack of authorization checks (capability checks) within this handler, an attacker can retrieve sensitive configuration data, including the Google API Key.

Vulnerable Code

// File: pretty-google-calendar.php (or associated AJAX handler file)
add_action('wp_ajax_pgcal_ajax_handler', 'pgcal_ajax_handler');
add_action('wp_ajax_nopriv_pgcal_ajax_handler', 'pgcal_ajax_handler');

function pgcal_ajax_handler() {
    // Check for nonce usually exists, but capability check is missing
    check_ajax_referer('pgcal_ajax_nonce', 'nonce');

    // Vulnerability: No current_user_can('manage_options') or similar check
    $api_key = get_option('pgcal_api_key');
    $settings = get_option('pgcal_settings');

    wp_send_json_success([
        'api_key' => $api_key,
        'settings' => $settings
    ]);
    wp_die();
}

Security Fix

--- a/pretty-google-calendar.php
+++ b/pretty-google-calendar.php
@@ -10,7 +10,6 @@
 
 // The handler is no longer exposed to unauthenticated users for administrative data
 add_action('wp_ajax_pgcal_ajax_handler', 'pgcal_ajax_handler');
-add_action('wp_ajax_nopriv_pgcal_ajax_handler', 'pgcal_ajax_handler');
 
 function pgcal_ajax_handler() {
     check_ajax_referer('pgcal_ajax_nonce', 'nonce');
+
+    if ( ! current_user_can( 'manage_options' ) ) {
+        wp_send_json_error( 'Unauthorized', 403 );
+    }
+
     $api_key = get_option('pgcal_api_key');

Exploit Outline

The exploit targets the `/wp-admin/admin-ajax.php` endpoint. An unauthenticated attacker first visits a public-facing page where the Google Calendar shortcode [pretty-google-calendar] is present. From the page source, they extract the AJAX nonce, typically found in a localized JavaScript object (e.g., `pgcal_obj.nonce`). The attacker then sends a POST request to the AJAX endpoint with the `action` set to `pgcal_ajax_handler` and the extracted `nonce`. Because the server-side function fails to verify if the requester has administrative privileges, it returns the plugin's internal settings, including the Google API key, in the JSON response.

Check if your site is affected.

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