HTTP Headers <= 1.19.2 - Authenticated (Administrator+) CRLF Injection via Custom Header Values
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:HTechnical Details
# 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-headersor similar). - Vulnerable Parameter: Input fields for custom header names/values (e.g.,
http_headers_custom[name][]andhttp_headers_custom[value][]). - Authentication: Authenticated, Administrator role required.
- Preconditions:
- The site must be running on an Apache web server.
- The
.htaccessfile must be writable by the web server/WordPress. - The "Custom Headers" feature must be active or accessible.
3. Code Flow
- Entry Point: The administrator navigates to the plugin settings and submits the "Custom Headers" form.
- Form Submission: The data is sent to
options.php(standard Settings API) or a custom POST handler in the plugin. - Processing: The plugin retrieves the array of custom headers.
- Generation: A string of Apache directives is built, typically using the format:
Header set [NAME] "[VALUE]"(inferred). - Sink: The plugin calls
insert_with_markers( get_home_path() . '.htaccess', 'HTTP Headers', $rules ). - Vulnerability: Because
$rulescontains unsanitized user input with\n, the generated.htaccessblock breaks out of the intendedHeaderdirective.
4. Nonce Acquisition Strategy
This exploit requires an authenticated Administrator session. Since it involves modifying plugin settings, a standard WordPress settings nonce is required.
- Login: Authenticate as an Administrator using
wp_cliorhttp_request. - Navigate: Access the Custom Headers settings page.
- Identify Variable: Look for the
_wpnoncefield in the form or the localized JS variable if the plugin uses AJAX. - Extraction:
- Navigate to:
/wp-admin/options-general.php?page=http-headers(exact slug to be verified). - Use
browser_evalto get the nonce:// Search for the nonce in the settings form document.querySelector('input[name="_wpnonce"]')?.value;
- Navigate to:
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
.htaccesslogic:# 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
- Requirement: WordPress installed on Apache.
- User:
wp user create attacker admin@example.com --role=administrator --user_pass=password - 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
POSTrequest, the application should return a 500 Internal Server Error (or the next request to any page will). - Root Cause: The
.htaccessfile 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:
- Check .htaccess:
cat /var/www/html/.htaccess- Verify the content between
# BEGIN HTTP Headersand# END HTTP Headerscontains the injected newline and theUnrecognizedDirective.
- Verify the content between
- Confirm Site Status:
curl -I http://localhost:8080/- Expect
HTTP/1.1 500 Internal Server Error.
- Expect
- Cleanup:
wp plugin deactivate http-headers(if possible) or manually edit.htaccessto 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 Commentto 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
valuefield is escaped but thenamefield 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.