CVE-2026-2717

HTTP Headers <= 1.19.2 - Authenticated (Administrator+) CRLF Injection via Custom Header Values

mediumImproper Neutralization of CRLF Sequences ('CRLF Injection')
5.5
CVSS Score
5.5
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The HTTP Headers plugin for WordPress is vulnerable to CRLF Injection in all versions up to, and including, 1.19.2. This is due to insufficient sanitization of custom header name and value fields before writing them to the Apache .htaccess file via `insert_with_markers()`. This makes it possible for authenticated attackers, with Administrator-level access and above, to inject arbitrary newline characters and additional Apache directives into the .htaccess configuration file via the 'Custom Headers' settings, leading to Apache configuration parse errors and potential site-wide denial of service.

CVSS Vector Breakdown

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

Technical Details

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

# Exploitation Research Plan: CVE-2026-2717 (CRLF Injection in HTTP Headers) ## 1. Vulnerability Summary The **HTTP Headers** plugin (<= 1.19.2) is vulnerable to **CRLF Injection** via the "Custom Headers" configuration. The plugin allows administrators to define arbitrary HTTP headers which are th…

Show full research plan

Exploitation Research Plan: CVE-2026-2717 (CRLF Injection in HTTP Headers)

1. Vulnerability Summary

The HTTP Headers plugin (<= 1.19.2) is vulnerable to CRLF Injection via the "Custom Headers" configuration. The plugin allows administrators to define arbitrary HTTP headers which are then written to the site's .htaccess file using the WordPress core function insert_with_markers().

The vulnerability exists because the plugin fails to sanitize or validate the input fields for custom header names and values against newline characters (\n, \r). An attacker with Administrator privileges can inject CRLF sequences followed by arbitrary Apache directives. When these are written to .htaccess, Apache fails to parse the configuration, resulting in a site-wide 500 Internal Server Error (Denial of Service).

2. Attack Vector Analysis

  • Vulnerable Endpoint: WordPress Admin Dashboard Settings Page (typically options-general.php?page=http-headers-custom-headers or similar).
  • Vulnerable Parameter: Input fields for custom header names/values (e.g., http_headers_custom[name][] and http_headers_custom[value][]).
  • Authentication: Authenticated, Administrator role required.
  • Preconditions:
    • The site must be running on an Apache web server.
    • The .htaccess file must be writable by the web server/WordPress.
    • The "Custom Headers" feature must be active or accessible.

3. Code Flow

  1. Entry Point: The administrator navigates to the plugin settings and submits the "Custom Headers" form.
  2. Form Submission: The data is sent to options.php (standard Settings API) or a custom POST handler in the plugin.
  3. Processing: The plugin retrieves the array of custom headers.
  4. Generation: A string of Apache directives is built, typically using the format: Header set [NAME] "[VALUE]" (inferred).
  5. Sink: The plugin calls insert_with_markers( get_home_path() . '.htaccess', 'HTTP Headers', $rules ).
  6. Vulnerability: Because $rules contains unsanitized user input with \n, the generated .htaccess block breaks out of the intended Header directive.

4. Nonce Acquisition Strategy

This exploit requires an authenticated Administrator session. Since it involves modifying plugin settings, a standard WordPress settings nonce is required.

  1. Login: Authenticate as an Administrator using wp_cli or http_request.
  2. Navigate: Access the Custom Headers settings page.
  3. Identify Variable: Look for the _wpnonce field in the form or the localized JS variable if the plugin uses AJAX.
  4. Extraction:
    • Navigate to: /wp-admin/options-general.php?page=http-headers (exact slug to be verified).
    • Use browser_eval to get the nonce:
      // Search for the nonce in the settings form
      document.querySelector('input[name="_wpnonce"]')?.value;
      

5. Exploitation Strategy

The goal is to inject a malformed Apache directive into .htaccess to cause a 500 error.

Step 1: Discover Setting Parameters

Access the settings page and identify the name of the option and the field names.

  • Inferred URL: /wp-admin/options-general.php?page=http-headers
  • Inferred Field Name: http_headers_custom_headers[0][value]

Step 2: Prepare the Payload

The payload will use a newline to break the Header directive and insert a syntax error.

  • Payload Name: X-Safe-Header
  • Payload Value: SafeValue" \n Malformed-Directive-Here
  • Resulting .htaccess logic:
    # BEGIN HTTP Headers
    Header set X-Safe-Header "SafeValue"
    Malformed-Directive-Here
    # END HTTP Headers
    

Step 3: Execute Request

Submit the settings form via http_request.

Request Details:

  • Method: POST
  • URL: https://[target]/wp-admin/options.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    option_page=http_headers_settings_group&
    action=update&
    _wpnonce=[NONCE]&
    http_headers_custom_headers[0][name]=X-EVIL&
    http_headers_custom_headers[0][value]=Injected"\nUnrecognizedDirective&
    submit=Save+Changes
    

6. Test Data Setup

  1. Requirement: WordPress installed on Apache.
  2. User: wp user create attacker admin@example.com --role=administrator --user_pass=password
  3. Plugin Configuration:
    • Ensure the "HTTP Headers" plugin is active.
    • Check if a "Custom Headers" sub-tab exists within the plugin settings.

7. Expected Results

  • Immediate Effect: After the POST request, the application should return a 500 Internal Server Error (or the next request to any page will).
  • Root Cause: The .htaccess file now contains:
    Header set X-EVIL "Injected"
    UnrecognizedDirective
    
  • Response: HTTP 500.

8. Verification Steps

After attempting the exploit, use the terminal/WP-CLI to verify the file corruption:

  1. Check .htaccess: cat /var/www/html/.htaccess
    • Verify the content between # BEGIN HTTP Headers and # END HTTP Headers contains the injected newline and the UnrecognizedDirective.
  2. Confirm Site Status: curl -I http://localhost:8080/
    • Expect HTTP/1.1 500 Internal Server Error.
  3. Cleanup: wp plugin deactivate http-headers (if possible) or manually edit .htaccess to remove the broken block.

9. Alternative Approaches

  • Blind Injection: If the goal is not DoS, but bypass, try injecting \nHeader set Access-Control-Allow-Origin "*" to modify security policies.
  • Comment Injection: If a directive name is required, use \n# Injected Comment to verify injection without crashing the site initially.
  • Different Marker: Check if the plugin uses a different marker name in insert_with_markers (e.g., insert_with_markers( ..., 'HTTP_HEADERS', ... )).
  • Field Targeting: If the value field is escaped but the name field is not, move the payload to the header name field: X-Header"\nMalformedDirective: .

Check if your site is affected.

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