Improper Neutralization of CRLF Sequences ('CRLF Injection')

Description

CRLF Injection is a vulnerability that occurs when software constructs output records using externally-influenced input but does not neutralize or incorrectly neutralizes Carriage Return (CR, \r, %0d) and Line Feed (LF, \n, %0a) characters. In HTTP and many text-based protocols, CRLF sequences delimit headers and separate headers from body content. Attackers exploit this vulnerability to inject arbitrary headers, split HTTP responses, manipulate log files, or alter the structure of protocol messages. HTTP Response Splitting is a particularly dangerous form of CRLF injection that enables cache poisoning, cross-site scripting, and session hijacking attacks.

Risk

CRLF injection enables powerful attacks depending on the context. In HTTP responses, attackers can inject additional headers including Set-Cookie for session fixation, Location for redirect attacks, or security headers to disable protections. HTTP Response Splitting allows injecting complete fake responses that may be cached by proxies, enabling widespread attacks against other users. In email headers, CRLF injection enables email header injection for spam relay or spoofing. Log injection can corrupt log files, inject false entries to cover tracks, or exploit log viewers vulnerable to terminal escape sequences. The simplicity of exploitation combined with potentially severe impact makes this a high-priority vulnerability class.

Solution

Strip or encode all CR and LF characters from user input before including it in HTTP headers, log messages, email headers, or other line-delimited contexts. Use framework-provided functions for setting HTTP headers that automatically handle encoding. Validate that user input does not contain unexpected line terminators. For URLs and redirect destinations, validate against allowlists of permitted domains and paths. Implement proper output encoding for the specific context (URL encoding for URL parameters, header encoding for HTTP headers). Use modern web frameworks that prevent CRLF injection by default in header-setting functions.

Common Consequences

ImpactDetails
IntegrityScope: Integrity

HTTP Response Splitting enables injection of arbitrary content including malicious JavaScript, fake login pages, or misleading information.
Access ControlScope: Access Control

Session fixation through injected Set-Cookie headers can enable account hijacking and unauthorized access.
ConfidentialityScope: Confidentiality

Cache poisoning through response splitting can expose other users' data or redirect them to malicious sites.

Example Code + Solution Code

Vulnerable Code

from flask import Flask, request, redirect, make_response

app = Flask(__name__)

@app.route('/redirect')
def vulnerable_redirect():
    # VULNERABLE: User input directly in Location header
    # Attack: url=http://example.com%0d%0aSet-Cookie:%20session=malicious
    url = request.args.get('url')
    response = make_response('Redirecting...')
    response.headers['Location'] = url  # CRLF injection possible
    response.status_code = 302
    return response

@app.route('/setlang')
def set_language():
    # VULNERABLE: User input in cookie without sanitization
    # Attack: lang=en%0d%0aSet-Cookie:%20admin=true
    lang = request.args.get('lang')
    response = make_response('Language set')
    response.headers['Set-Cookie'] = f'language={lang}'
    return response

Fixed Code

from flask import Flask, request, redirect, make_response, abort
import re
from urllib.parse import urlparse

app = Flask(__name__)

def sanitize_header_value(value):
    """Remove CRLF characters from header values"""
    if value is None:
        return None
    # Remove CR, LF, and null bytes
    return re.sub(r'[\r\n\x00]', '', value)

def validate_redirect_url(url):
    """Validate redirect URL against allowlist"""
    allowed_domains = ['example.com', 'trusted.com']

    try:
        parsed = urlparse(url)
        if parsed.scheme not in ['http', 'https']:
            return None
        if parsed.netloc not in allowed_domains:
            return None
        return url
    except:
        return None

@app.route('/redirect')
def safe_redirect():
    url = request.args.get('url', '')

    # Sanitize CRLF characters
    url = sanitize_header_value(url)

    # Validate against allowlist
    safe_url = validate_redirect_url(url)
    if not safe_url:
        abort(400, 'Invalid redirect URL')

    # Use framework's redirect function (adds protection)
    return redirect(safe_url)

@app.route('/setlang')
def safe_set_language():
    lang = request.args.get('lang', 'en')

    # Validate against allowlist
    allowed_languages = ['en', 'de', 'fr', 'es', 'ja']
    if lang not in allowed_languages:
        lang = 'en'

    # Use response.set_cookie() instead of manual header
    response = make_response('Language set')
    response.set_cookie('language', lang, httponly=True, secure=True)
    return response

Exploited in the Wild

HTTP Response Splitting Attacks (Web Applications, Historical)

CRLF injection enabling HTTP Response Splitting has been exploited against various web applications to perform cache poisoning attacks, where malicious responses are cached by proxy servers and served to other users.


Tools to test/exploit

  • Burp Suite — web security testing platform with CRLF injection detection in headers and responses.

  • CRLFuzz — automated CRLF injection vulnerability scanner.


CVE Examples


References

  1. MITRE. "CWE-93: Improper Neutralization of CRLF Sequences." https://cwe.mitre.org/data/definitions/93.html

  2. OWASP. "HTTP Response Splitting." https://owasp.org/www-community/attacks/HTTP_Response_Splitting