CRLF Injection

A simple line break seems harmless when thinking about a web application. However, if poorly managed, it can open the door to serious attacks.

This is precisely the case with CRLF injections, an often underestimated vulnerability that involves inserting end-of-line control characters into requests or responses.

Behind this technical mechanism lie concrete exploitation scenarios that can range from the leakage of sensitive information to the compromise of a system’s reliability.

This article takes a detailed look at what a CRLF injection is, how it is exploited, and best practices for protecting against it.

Comprehensive Guide to CRLF Injections

What is a CRLF injection?

CRLF injection is a vulnerability that relies on the misuse of two special characters: Carriage Return (CR, represented by ‘\r’) and Line Feed (LF, represented by ‘\n’).

Together, they indicate to many computer systems that a new line should begin. This is particularly the case for web servers, which interpret the ‘CRLF’ sequence as a line break.

It should be noted that this convention is not universal. For example, in Unix environments, the single LF character is sufficient to signal a new line. But in web applications, the CRLF combination is frequently used and therefore constitutes a potential attack surface.

A CRLF injection occurs when these characters are introduced into a part of the system that does not expect them. If the application does not validate or encode them correctly, an attacker can take advantage of this to alter the normal functioning of a request or response. In practice, this amounts to ‘breaking’ the structure intended by the server and inserting unwanted content into an unintended context.

This principle may seem abstract at first glance. That is why we will illustrate it through several concrete scenarios of web application exploitation, in order to better understand the scope and risks of this vulnerability.

Exploiting CRLF Injection Vulnerabilities and Security Best Practices

One of the most common CRLF injection scenarios involves the SMTP protocol, which is used for sending and receiving emails.

How does an SMTP header injection work?

In the context of a web application, this vulnerability typically manifests itself in features that automatically send emails to users, such as password resets.

When a user requests a password reset, the application takes the address provided, forges an SMTP message, and sends it to the mail server. Under normal conditions, this process remains transparent to the user. For example, imagine an SMTP exchange between a client sending an email:

telnet smtp.company.com 25
Connected to smtp.company.com.
220 smtp.company.com SMTP Ready
HELO client
250-smtp.company.com
250-PIPELINING
250 8BITMIME       
MAIL FROM: <[email protected]>
250 Sender ok
RCPT TO: [email protected]
250 Recipient ok.
DATA
354 Enter mail, end with "." on a line by itself
Subject: Contact

Corps du texte
.
250 Ok
QUIT
221 Closing connection
Connection closed by foreign host.

In the HTTP request, the user will have control over the ‘RCPT TO’ field.

The legitimate request will be as follows:

POST /api/authentication/forgot-password HTTP/2
Host: vulnerable.vaadata.com

{"login":"[email protected]"}

This request is processed correctly. The SMTP server constructs the email and sends it only to the specified address ([email protected]) with the password reset link.

Exploiting the vulnerability

The problem arises if CRLF characters are not filtered. An attacker can then inject a new header into the SMTP stream. For example:

POST /api/authentication/forgot-password HTTP/2
Host: vulnerable.vaadata.com

{"login":"[email protected]\r\nCc: [email protected]"}

In this case, the generated headers are no longer limited to the original recipient. The result is:

From: [email protected]
Subject: Change your password
To: [email protected]
Cc: [email protected]

Through this manipulation, the attacker copies himself into the message (with the Cc header) and also receives the password reset link. The exploitation can be even more discreet by injecting a Bcc field, which remains invisible to the legitimate recipient.

This vulnerability often originates in SMTP management libraries used by applications. Whether third-party or developed in-house, the risk is the same: the lack of validation and encoding of user input.

How to prevent SMTP header injections?

The correction therefore involves several levels of defence:

  • Strictly validate the input: the email address must correspond to a valid format, without tolerating the characters ‘\r’ and ‘\n’.
  • Encode the control characters: if they appear despite everything, they must be transformed so that they are not interpreted by the SMTP server.
  • Correct at the application level: if the library used does not include a fix, the application or API must implement its own protection mechanisms.

In summary, this scenario shows that a simple lack of control over line breaks can be enough to hijack a critical feature such as password reset.

CRLF injections are not limited to SMTP exchanges. They can also affect the logging system of a web application.

What is log injection?

Most frameworks offer built-in logging mechanisms, and it is strongly recommended to use them, particularly for detecting intrusions or tracking application usage. However, if the data is not properly checked before being recorded, these logs can become a target.

Let’s take the example of an application that records user actions in a file, using a simple format:

HTTP verb   HTTP status   Endpoint     Client-IP       X-User-Id
GET         200           /home        5.50.81.190     123-456
PATCH       200           /user        5.50.81.190     123-456
GET         200           /login       5.45.20.32      Unauthenticated

Each authenticated request contains an X-User-Id header, automatically added by the browser. In the log above, we can follow the path of the user with ID 123-456, who visited the home page and then edited his profile.

To prevent abuse, the developers made sure that it is impossible to send a request without this header (except on public routes). But they overlooked one detail: the data in this header is recorded as is, without being cleaned. This is where a CRLF injection can come into play.

Example of a CRLF attack in the logs

A malicious user who is already authenticated could, for example, send the following request:

GET /search HTTP/2
Host: vulnerable.vaadata.com
Authorization: Bearer <...>
X-User-Id: 789-123\r\nGET 200 /admin 5.48.16.120 Unauthenticated

The generated log file would then look like this:

HTTP verb   HTTP status   Endpoint     Client-IP       X-User-Id
GET         200           /home        5.50.81.190     123-456
PATCH       200           /user        5.50.81.190     123-456
GET         200           /login       5.45.20.32      Unauthenticated
GET         200           /search      5.50.81.190     123-456
GET         200           /admin       5.48.16.120     Unauthenticated

The injected line makes it appear as though an IP address has accessed the administration page, when in fact it has not. The system administrator may be misled, wasting time investigating and, more broadly, losing confidence in the integrity of the logs.

Even if this attack does not directly compromise the data or the server, it undermines the reliability of the logging system, which then becomes unusable as a security tool.

Preventing log injections

The solution is to prevent special characters from being interpreted. To do this, CR (\r) and LF (\n) characters in entries must be encoded (e.g. using URL encoding) before being stored. This prevents them from breaking the log file structure.

How does a CRLF injection lead to a reflected XSS?

Another scenario for exploiting CRLF injections involves reflected XSS vulnerabilities.

This type of vulnerability occurs when the application takes data from the HTTP request and reflects it directly in its response, without filtering or encoding it.

Example of CRLF injection leading to XSS

Let us imagine a multi-tenant application in which a user can belong to multiple organisations. To determine which environment to display, the browser sends an X-Organisation-Id parameter.

A legitimate request/response might look like this:

GET /home?X-Organization-Id=1234 HTTP/2
Host: vulnerable.vaadata.com
Authorization: Bearer <...>

HTTP/2 200 OK
Date: Fri, 08 Aug 2025 08:11:55 GMT
Content-Type: application/json
X-Organization-Id: 1234

[… HTML CONTENT …]

We can see that the parameter is reproduced as is in the server’s response. This opens the way for a CRLF injection, for example by encoding the special characters %0d%0a (which correspond to \r\n):

GET /home?X-Organization-Id=1234%0d%0a%0d%0a<html><script>alert(1)</script></html> HTTP/2
Host: vulnerable.vaadata.com
Authorization: Bearer <...>

HTTP/2 200 OK
Date: Fri, 08 Aug 2025 08:11:55 GMT
Content-Type: application/json
X-Organization-Id: 1234

<html><script>alert(1)</script></html>
[… HTML CONTENT …]

With this injection of two line breaks, the attacker manages to escape the context of the headers and insert HTML/JavaScript code directly into the body of the response.

The browser then executes this code, causing an XSS. In its simplest form, this results in an alert on the screen. But the impact can be much more serious if the application uses a cache system: in this case, a Cache Poisoning attack could infect all users in an organisation, or even the entire platform.

How to protect yourself from an XSS attack via CRLF injection?

This example illustrates that, although less well known than other injections, CRLF vulnerabilities can have serious consequences.

The remediation remains the same regardless of the vector: ensure that CRLF characters (\r and \n) are correctly encoded so that they are never interpreted as line breaks by the server.

Conclusion

CRLF injections perfectly illustrate how something as mundane as a line break can become a real security flaw. Behind this technical detail lie a variety of concrete scenarios: theft of password reset links via SMTP, falsification of application logs, and even triggering of XSS attacks.

While their impact varies depending on the context, all these exploitations rely on the same weakness: the lack of control or encoding of carriage return and line feed characters. That is why the best defence remains twofold: strictly validate user input and correctly encode sensitive characters before they are interpreted by a server or stored by the application.

Although often overlooked, these vulnerabilities deserve the full attention of development and security teams. By incorporating these best practices from the design phase onwards, it is possible to significantly reduce risks and preserve the integrity of web systems in the face of these discreet but highly effective attacks.

Author: Julien BRACON – Pentester @Vaadata