Event Koi Lite <= 1.3.13.1 - Missing Authorization to Unauthenticated Sensitive Information Exposure via REST API Endpoints
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:NTechnical Details
<=1.3.13.1What Changed in the Fix
Changes introduced in v1.3.14.0
Source Code
WordPress.org SVN# 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_eventmust exist with a non-public status (e.g.,draft,private,pending).
3. Code Flow
- Registration: In
includes/api/class-event.php, theinit()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 ) ); - Retrieval: The
get_result(WP_REST_Request $request)method extracts theidparameter:$event_id = absint( $request->get_param( 'id' ) ); $event = new SingleEvent( $event_id ); $response = $event::get_meta(); - Instantiation: The
EventKoi\Core\Event(SingleEvent) constructor inincludes/core/class-event.phpusesget_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; } - Exposure:
SingleEvent::get_meta()iterates through$meta_keys(defined inclass-event.php), which includes sensitive keys likevirtual_url,location,latitude,longitude,gmap_link, andrsvp_enabled. It returns these values to the API response.
4. Nonce Acquisition Strategy
This vulnerability does not require a WordPress nonce.
- The
permission_callbackis explicitly set to__return_true. - Standard WordPress REST API requests usually require a
_wpnoncefor authenticated state changes (POST/PUT/DELETE), but forGETrequests 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.
- Enumerate IDs: An attacker would typically iterate through potential post IDs (e.g., 1 to 2000).
- Request Event Data: Send a
GETrequest to the vulnerable endpoint for each ID. - 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:
- 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" - Verify Status: Ensure the post is in
draftstatus (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
- Confirm Unauthenticated Access: Verify that the request was made without any
CookieorAuthorizationheaders. - Check Sensitivity: Use WP-CLI to confirm the retrieved data matches the
draftevent's meta:wp post meta get <EVENT_ID> virtual_url - 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 viaSingleEvent.
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.
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
@@ -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.