CVE-2026-10029

Event Koi Lite <= 1.3.13.1 - Missing Authorization to Unauthenticated Sensitive Information Exposure via REST API Endpoints

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
1.3.14.0
Patched in
1d
Time to patch

Description

The Event Koi Lite – Events Calendar, Event Management, RSVP, and Tickets plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 1.3.13.1 via the get_events. This makes it possible for unauthenticated attackers to extract sensitive data including virtual meeting URLs, physical location data, latitude/longitude coordinates, Google Maps links, and RSVP configuration belonging to draft, pending, and private events that are otherwise inaccessible via public URLs.

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<=1.3.13.1
PublishedJune 17, 2026
Last updatedJune 18, 2026
Affected plugineventkoi-lite

What Changed in the Fix

Changes introduced in v1.3.14.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-10029 (Event Koi Lite) ## 1. Vulnerability Summary The **Event Koi Lite** plugin (versions <= 1.3.13.1) contains a **Missing Authorization** vulnerability within its REST API implementation. Specifically, the endpoints `/event` and `/get_event` are registered …

Show full research plan

Exploitation Research Plan: CVE-2026-10029 (Event Koi Lite)

1. Vulnerability Summary

The Event Koi Lite plugin (versions <= 1.3.13.1) contains a Missing Authorization vulnerability within its REST API implementation. Specifically, the endpoints /event and /get_event are registered with a permission_callback of __return_true, making them accessible to unauthenticated users. These endpoints retrieve event data by ID using get_post(), which retrieves the post object regardless of its status (draft, pending, or private). Because the plugin fails to verify the post status or the user's capability to read non-public posts, it exposes sensitive event metadata—including virtual meeting URLs and physical locations—that should be restricted.

2. Attack Vector Analysis

  • Endpoint: /wp-json/eventkoi/v1/event (and /wp-json/eventkoi/v1/get_event)
  • Method: GET
  • Vulnerable Parameter: id (integer)
  • Authentication: None (Unauthenticated)
  • Preconditions: An event of the custom post type eventkoi_event must exist with a non-public status (e.g., draft, private, pending).

3. Code Flow

  1. Registration: In includes/api/class-event.php, the init() method registers the REST routes:
    register_rest_route(
        EVENTKOI_API, // 'eventkoi/v1'
        '/event',
        array(
            'methods'             => 'GET',
            'callback'            => array( self::class, 'get_result' ),
            'permission_callback' => '__return_true', // <--- VULNERABILITY: No authorization check
        )
    );
    
  2. Retrieval: The get_result(WP_REST_Request $request) method extracts the id parameter:
    $event_id = absint( $request->get_param( 'id' ) );
    $event    = new SingleEvent( $event_id );
    $response = $event::get_meta();
    
  3. Instantiation: The EventKoi\Core\Event (SingleEvent) constructor in includes/core/class-event.php uses get_post($event):
    public function __construct( $event = null ) {
        if ( is_numeric( $event ) ) {
            $event = get_post( $event ); // <--- Fetches the post regardless of status
            // ...
        }
        self::$event    = $event;
        self::$event_id = ! empty( $event->ID ) ? $event->ID : 0;
    }
    
  4. Exposure: SingleEvent::get_meta() iterates through $meta_keys (defined in class-event.php), which includes sensitive keys like virtual_url, location, latitude, longitude, gmap_link, and rsvp_enabled. It returns these values to the API response.

4. Nonce Acquisition Strategy

This vulnerability does not require a WordPress nonce.

  • The permission_callback is explicitly set to __return_true.
  • Standard WordPress REST API requests usually require a _wpnonce for authenticated state changes (POST/PUT/DELETE), but for GET requests with __return_true, no nonce is validated by the REST server.

5. Exploitation Strategy

The goal is to demonstrate that an unauthenticated user can read sensitive data from a draft event.

  1. Enumerate IDs: An attacker would typically iterate through potential post IDs (e.g., 1 to 2000).
  2. Request Event Data: Send a GET request to the vulnerable endpoint for each ID.
  3. Identify Sensitive Data: Parse the JSON response for sensitive fields.

Actionable Request

Using the http_request tool:

  • URL: http://<target>/wp-json/eventkoi/v1/event?id=<EVENT_ID>
  • Method: GET
  • Headers: Content-Type: application/json

6. Test Data Setup

To verify the vulnerability in a controlled environment:

  1. Create a Draft Event: Use WP-CLI to create an event with sensitive information that is not published.
    # Create the event
    EVENT_ID=$(wp post create --post_type=eventkoi_event --post_status=draft --post_title="Secret Board Meeting" --porcelain)
    
    # Add sensitive metadata
    wp post meta set $EVENT_ID virtual_url "https://zoom.us/j/999888777666"
    wp post meta set $EVENT_ID location "Secret Underground Bunker"
    wp post meta set $EVENT_ID latitude "37.2431"
    wp post meta set $EVENT_ID longitude "-115.793"
    
  2. Verify Status: Ensure the post is in draft status (wp post get $EVENT_ID --field=post_status).

7. Expected Results

A successful exploit will return a 200 OK response with a JSON body containing the sensitive fields:

{
    "id": 123,
    "title": "Secret Board Meeting",
    "virtual_url": "https://zoom.us/j/999888777666",
    "location": "Secret Underground Bunker",
    "latitude": "37.2431",
    "longitude": "-115.793",
    "wp_status": "draft"
}

An unauthenticated request should normally result in a 401 Unauthorized or 404 Not Found (to prevent ID enumeration) for non-public content.

8. Verification Steps

  1. Confirm Unauthenticated Access: Verify that the request was made without any Cookie or Authorization headers.
  2. Check Sensitivity: Use WP-CLI to confirm the retrieved data matches the draft event's meta:
    wp post meta get <EVENT_ID> virtual_url
    
  3. Status Check: Confirm the event is indeed a draft and not publicly accessible via the standard frontend URL (wp post get <EVENT_ID> --field=guid).

9. Alternative Approaches

If the /event endpoint is patched or behaves differently, test the secondary endpoint:

  • Endpoint: /wp-json/eventkoi/v1/get_event?id=<ID>
  • Logic: This calls eventkoi_get_event($event_id), which likely uses similar internal logic via SingleEvent.

If the plugin implements a collection endpoint (referred to as get_events in the description but not present in the snippets), try:

  • Endpoint: /wp-json/eventkoi/v1/events (inferred)
  • Logic: This would likely leak multiple non-public events in a single response, significantly increasing the impact.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Event Koi Lite plugin for WordPress is vulnerable to sensitive information exposure via its REST API due to missing authorization checks on several endpoints. Unauthenticated attackers can exploit this to retrieve sensitive metadata—including virtual meeting URLs, precise physical coordinates, and RSVP settings—belonging to non-public event statuses such as drafts, private, and pending posts.

Vulnerable Code

// includes/api/class-event.php
	public static function init() {
		register_rest_route(
			EVENTKOI_API,
			'/event',
			array(
				'methods'             => 'GET',
				'callback'            => array( self::class, 'get_result' ),
				'permission_callback' => '__return_true',
			)
		);

		register_rest_route(
			EVENTKOI_API,
			'/get_event',
			array(
				'methods'             => 'GET',
				'callback'            => array( self::class, 'get_event' ),
				'permission_callback' => '__return_true',
			)
		);
---
// includes/api/class-event.php
	public static function get_result( WP_REST_Request $request ) {
		$event_id = absint( $request->get_param( 'id' ) );

		if ( empty( $event_id ) ) {
			return new WP_Error( 'eventkoi_invalid_id', __( 'Invalid event ID.', 'eventkoi-lite' ), array( 'status' => 400 ) );
		}

		$event    = new SingleEvent( $event_id );
		$response = $event::get_meta();
		$response = self::attach_rendered_event_fields( $response, $event_id );

		return rest_ensure_response( $response );
	}
---
// includes/core/class-event.php
	public function __construct( $event = null ) {

		if ( is_numeric( $event ) ) {
			$event = get_post( $event );
			if ( ! empty( $event->post_type ) && 'eventkoi_event' !== $event->post_type ) {
				$event = array();
			}
		}

		self::$event    = $event;
		self::$event_id = ! empty( $event->ID ) ? $event->ID : 0;
	}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/eventkoi-lite/1.3.13.1/includes/api/class-event.php /home/deploy/wp-safety.org/data/plugin-versions/eventkoi-lite/1.3.14.0/includes/api/class-event.php
--- /home/deploy/wp-safety.org/data/plugin-versions/eventkoi-lite/1.3.13.1/includes/api/class-event.php	2026-05-21 20:42:00.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/eventkoi-lite/1.3.14.0/includes/api/class-event.php	2026-06-04 18:20:14.000000000 +0000
@@ -147,6 +147,17 @@
 			return new WP_Error( 'eventkoi_invalid_id', __( 'Invalid event ID.', 'eventkoi-lite' ), array( 'status' => 400 ) );
 		}
 
+		// Non-published events (draft, pending, future, private) are only
+		// readable by users who can edit them. This prevents unauthenticated
+		// disclosure of unpublished event data (CVE-2026-10029).
+		$post = get_post( $event_id );
+		if ( ! $post || 'eventkoi_event' !== $post->post_type ) {
+			return new WP_Error( 'eventkoi_not_found', __( 'Event not found.', 'eventkoi-lite' ), array( 'status' => 404 ) );
+		}
+		if ( 'publish' !== $post->post_status && ! current_user_can( 'edit_post', $event_id ) ) {
+			return new WP_Error( 'eventkoi_not_found', __( 'Event not found.', 'eventkoi-lite' ), array( 'status' => 404 ) );
+		}
+
 		$event    = new SingleEvent( $event_id );
 		$response = $event::get_meta();
 		$response = self::attach_rendered_event_fields( $response, $event_id );
@@ -364,6 +375,15 @@
 			return new \WP_Error( 'eventkoi_missing_param', __( 'Missing event ID or timestamp.', 'eventkoi-lite' ) );
 		}
 
+		// Gate unpublished instance data behind edit capability (CVE-2026-10029).
+		$post = get_post( $event_id );
+		if ( ! $post || 'eventkoi_event' !== $post->post_type ) {
+			return new \WP_Error( 'eventkoi_not_found', __( 'Event not found.', 'eventkoi-lite' ), array( 'status' => 404 ) );
+		}
+		if ( 'publish' !== $post->post_status && ! current_user_can( 'edit_post', $event_id ) ) {
+			return new \WP_Error( 'eventkoi_not_found', __( 'Event not found.', 'eventkoi-lite' ), array( 'status' => 404 ) );
+		}
+
 		$event = new \EventKoi\Core\Event( $event_id );
 		$raw   = $event::get_meta();

Exploit Outline

The exploit targets the `/wp-json/eventkoi/v1/event` and `/wp-json/eventkoi/v1/get_event` endpoints, which are configured with `permission_callback` set to `__return_true`. 1. An unauthenticated attacker identifies the REST API endpoint base: `https://<target>/wp-json/eventkoi/v1/event`. 2. The attacker performs an ID enumeration attack by iterating through integer values for the `id` parameter (e.g., `?id=1`, `?id=2`, etc.). 3. Because the internal logic uses `get_post()` without verifying the post status, the API returns a full metadata object for any event ID, regardless of whether it is public (`publish`) or restricted (`draft`, `pending`, `private`). 4. The resulting JSON response exposes sensitive fields such as `virtual_url` (meeting links), `location`, `latitude`, `longitude`, and `rsvp_enabled` status for internal or unpublished events.

Check if your site is affected.

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