CVE-2026-1379

HTTP Headers <= 1.19.2 - Authenticated (Administrator+) Stored Cross-Site Scripting via 'Custom Headers' Plugin Setting

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
4.4
CVSS Score
4.4
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The HTTP Headers plugin for WordPress is vulnerable to Stored Cross-Site Scripting via admin settings in all versions up to, and including, 1.19.2 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with administrator-level permissions and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page. This only affects multi-site installations and installations where unfiltered_html has been disabled.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.19.2
PublishedApril 21, 2026
Last updatedMay 4, 2026
Affected pluginhttp-headers
Research Plan
Unverified

# Research Plan: CVE-2026-1379 - Authenticated Stored XSS in HTTP Headers ## 1. Vulnerability Summary The **HTTP Headers** plugin (<= 1.19.2) is vulnerable to **Stored Cross-Site Scripting (XSS)** via the "Custom Headers" setting. The plugin allows administrators to define custom HTTP headers to be…

Show full research plan

Research Plan: CVE-2026-1379 - Authenticated Stored XSS in HTTP Headers

1. Vulnerability Summary

The HTTP Headers plugin (<= 1.19.2) is vulnerable to Stored Cross-Site Scripting (XSS) via the "Custom Headers" setting. The plugin allows administrators to define custom HTTP headers to be sent by the server. However, it fails to sufficiently sanitize the input when saving these headers and fails to escape the output when displaying them back in the administrative interface.

In a standard WordPress installation, Administrators have the unfiltered_html capability, making this "intended functionality." However, in Multi-site environments or installations where DISALLOW_UNFILTERED_HTML is defined as true, this vulnerability allows an Administrator to bypass those restrictions and inject malicious scripts that execute in the context of other users (including Super Admins) who visit the plugin's settings page.

2. Attack Vector Analysis

  • Endpoint: WordPress Admin Settings page for the plugin, typically wp-admin/options-general.php?page=http-headers or a specific sub-page for "Custom".
  • Vulnerable Parameter: Likely a POST parameter named custom_headers or similar, nested within the plugin's options array.
  • Authentication: Authenticated, Administrator+ level permissions.
  • Preconditions:
    1. The plugin http-headers (<= 1.19.2) must be active.
    2. unfiltered_html must be disabled (common in Multi-site or via define('DISALLOW_UNFILTERED_HTML', true); in wp-config.php).

3. Code Flow (Inferred)

  1. Entry Point: The Administrator navigates to the plugin settings and submits a form to save custom headers.
  2. Processing: The request hits wp-admin/options.php (if using the Settings API) or a custom admin_init hook. The plugin receives the array of custom headers.
  3. Storage: The plugin calls update_option() to save the custom headers into the database (likely under an option name like http_headers_settings or http_headers_custom).
  4. Sink (Output): When any administrator visits the "Custom Headers" configuration tab, the plugin retrieves the saved option and echoes it into the HTML value attribute of an input field or within a table row without using esc_attr() or esc_html().
  5. Execution: The injected script (e.g., "><script>alert(1)</script>) breaks out of the HTML attribute and executes in the browser.

4. Nonce Acquisition Strategy

The plugin likely uses the standard WordPress Settings API or a custom form on an admin page.

  1. Identify the Page: Use wp-cli to find the settings page slug:
    wp admin-menu list | grep "HTTP Headers"
  2. Navigate and Extract:
    • Use browser_navigate to go to the identified settings page (e.g., /wp-admin/options-general.php?page=http-headers).
    • The form will contain a hidden _wpnonce field.
    • Action String: If the plugin uses settings_fields('http_headers_group'), the nonce action will be http_headers_group-options.
    • JS Extraction:
      browser_eval("document.querySelector('input[name=\"_wpnonce\"]')?.value")
      

5. Exploitation Strategy

  1. Setup Environment: Ensure DISALLOW_UNFILTERED_HTML is set to true in wp-config.php.

  2. Identify Parameter Name: Inspect the settings page to find the exact name of the custom header input field. (Inferred: http_headers_custom[0][value] or similar).

  3. Payload Construction:

    • Simple: "><script>alert(document.domain)</script>
    • Breaking out of an input: "><img src=x onerror=alert(1)>
  4. HTTP Request (Playwright):
    Submit a POST request to wp-admin/options.php (if Settings API) or the plugin's own admin page.

    Example Request:

    POST /wp-admin/options.php HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    
    option_page=http_headers_settings_group&
    action=update&
    _wpnonce=[EXTRACTED_NONCE]&
    http_headers_settings[custom_headers][0][name]=X-XSS-Test&
    http_headers_settings[custom_headers][0][value]=%22%3E%3Cscript%3Ealert(1)%3C%2Fscript%3E
    
  5. Trigger: Navigate back to the "Custom Headers" settings page to trigger the stored payload.

6. Test Data Setup

  1. Plugin Installation: wp plugin install http-headers --version=1.19.2 --activate
  2. Config Change: Add define( 'DISALLOW_UNFILTERED_HTML', true ); to wp-config.php using a file write tool or sed.
  3. User Creation: Ensure an administrator user exists (e.g., admin / password).
  4. Page Visit: Visit the settings page once to ensure default options are initialized.

7. Expected Results

  • The POST request should return a 302 Redirect back to the settings page with settings-updated=true.
  • Upon navigating to the "Custom Headers" tab, the browser should execute the alert(1) script.
  • The HTML source of the page should show the unescaped payload: <input ... value=""><script>alert(1)</script>">.

8. Verification Steps

  1. Database Check: Use wp-cli to verify the payload is stored in the database:
    wp option get http_headers_settings --format=json (or the specific option name found during research).
  2. DOM Check: Use browser_eval to check for the presence of the injected script:
    browser_eval("!!document.querySelector('script')")
  3. Visual Confirmation: Capture a screenshot of the alert/injected element using the screenshot tool.

9. Alternative Approaches

  • AJAX Entry Point: If the plugin doesn't use options.php, check for wp_ajax_save_http_headers hooks in the source.
  • Header Injection (Frontend): If the plugin also renders these headers in the site frontend (as actual HTTP headers), test if it's possible to inject a Set-Cookie or Refresh header to perform session fixation or redirects, though the primary vulnerability is XSS in the admin UI.
  • Attribute Breakout: If the input is inside a complex JavaScript structure, use a JSON-breaking payload: \";alert(1)//.
Research Findings
Static analysis — not yet PoC-verified

Summary

The HTTP Headers plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'Custom Headers' setting in versions up to 1.19.2. This occurs because the plugin fails to sanitize user-provided header values before storage and fails to escape them when displaying them back in the administrative interface, allowing attackers to bypass 'unfiltered_html' restrictions in Multi-site environments.

Vulnerable Code

// File: http-headers/pages/custom.php (Inferred based on plugin structure and research plan)
// The plugin iterates through custom headers and echoes the 'value' key directly into an input attribute without escaping.

for ($i = 0; $i < count($http_headers_custom); $i++) {
    echo '<tr>';
    echo '<td><input name="http_headers_custom[' . $i . '][name]" type="text" value="' . $http_headers_custom[$i]['name'] . '" /></td>';
    echo '<td><input name="http_headers_custom[' . $i . '][value]" type="text" value="' . $http_headers_custom[$i]['value'] . '" /></td>';
    echo '</tr>';
}

Security Fix

--- a/http-headers/pages/custom.php
+++ b/http-headers/pages/custom.php
@@ -3,6 +3,6 @@
 for ($i = 0; $i < count($http_headers_custom); $i++) {
     echo '<tr>';
-    echo '<td><input name="http_headers_custom[' . $i . '][name]" type="text" value="' . $http_headers_custom[$i]['name'] . '" /></td>';
-    echo '<td><input name="http_headers_custom[' . $i . '][value]" type="text" value="' . $http_headers_custom[$i]['value'] . '" /></td>';
+    echo '<td><input name="http_headers_custom[' . $i . '][name]" type="text" value="' . esc_attr($http_headers_custom[$i]['name']) . '" /></td>';
+    echo '<td><input name="http_headers_custom[' . $i . '][value]" type="text" value="' . esc_attr($http_headers_custom[$i]['value']) . '" /></td>';
     echo '</tr>';
 }

Exploit Outline

1. Authentication: Log in to the WordPress dashboard as an Administrator. This exploit is particularly relevant on Multi-site installations or where 'unfiltered_html' is disabled. 2. Nonce Acquisition: Navigate to the HTTP Headers settings page (Settings > HTTP Headers) and click the 'Custom' tab. Extract the security nonce from the form (e.g., using `document.querySelector('input[name="_wpnonce"]').value`). 3. Payload Injection: Construct a POST request to `wp-admin/options.php` that includes the extracted nonce and an XSS payload (e.g., `"><script>alert(document.domain)</script>`) in the `http_headers_custom[0][value]` parameter. 4. Storage: The WordPress Settings API saves the malicious payload into the `http_headers_custom` option in the database. 5. Execution: The payload is triggered whenever an administrator visits the 'Custom' headers configuration sub-page, as the plugin echoes the stored value directly into the HTML without sanitization or attribute escaping.

Check if your site is affected.

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