利用ASCII控制字符攻破VS Code:拖拽文件即可触发漏洞

本文深入探讨如何利用ASCII控制字符(如SOH、STX、ETX)在VS Code中实现命令注入攻击。通过分析node-pty库的终端模拟机制,揭示了文件拖拽和运行配置中的安全风险,并提供了跨平台漏洞复现细节及防御建议。

拖拽即攻击:利用ASCII字符攻破VS Code | PortSwigger研究

控制字符如SOH、STX、EOT和ETX本不应执行你的代码——但在现代终端模拟器的世界里,它们有时确实会。本文将深入探讨ASCII传输控制字符被遗忘的机制、它们如何塑造早期计算,以及如何被滥用于影响现代应用程序的真实漏洞。

当终端用控制代码交流时

在基于GUI的IDE和花哨字体出现之前,计算机通过串行线路逐字符通信。为了保持这些对话的结构化,ASCII引入了一组通信控制字符:用于在系统间分隔和管理消息流的不可见字节代码。

典型消息遵循严格结构:

1
SOH → 头部 → STX → 正文 → ETX
  • [\x01] SOH:头部开始
  • [\x02] STX:文本开始
  • [\x03] ETX:文本结束
  • [\x04] EOT:传输结束

从传输帧到交互式编辑

尽管这些字符的最初目的是通信控制,但许多字符至今仍被现代软件重新利用。

Readline库(被Bash和其他交互式shell使用)重新利用了几个控制字符值进行行编辑:

  • [\x01] Ctrl + a:移动到行首
  • [\x02] Ctrl + b:向后移动一个字符
  • [\x03] Ctrl + c:中断当前进程或命令
  • [\x04] Ctrl + d:删除光标处的字符

还有其他许多Readline快捷键(Ctrl + e、Ctrl + k、Ctrl + l等)——详见Readline手册页。请注意在MacOS上,需要按Control键而非Command键。

当过去反噬:利用node-pty

快进到今天。像Visual Studio Code这样的应用程序使用node-pty在JavaScript环境中模拟伪终端。这将原始字节直接转发到shell,信任下游的所有内容都会"做正确的事"。

当控制字符介入时,这种信任就会被打破。

案例1:Visual Studio Code配置文件

在Visual Studio Code中,您可以在"运行→添加配置"下定义自定义运行配置。这些配置通常包含一个args数组。在我之前的研究中,发现了一个有趣的操作系统命令注入漏洞变体:将[\x01] SOH字符插入参数会导致shell拆分和错误解析它们。让我们检查以下测试配置文件:

1
2
3
4
5
6
{
  "args": [
    "hello",
    "\u0001\t--args\u0001\tCalculator\u0001\t-a\u0001open\t"
  ]
}

VS Code没有运行Python脚本,而是在MacOS上打开了计算器应用程序:

1
open -a Calculator --args cd /tmp/; /usr/bin/env /opt/homebrew/bin/python3 script.py hello

在Ubuntu上也可行;使用gnome-calculator代替。

为什么这能工作

查看Digital Equipment Corporation 1979年的VT100用户指南,显示了控制字符如何通过键盘快捷键编码:

正如您所知,node-pty读取原始字节并直接发送到shell。当VS Code看到字节[\x01] SOH时,它会将光标移动到行首(Ctrl + a)。在给定示例中,此操作重复四次以反向构建有效载荷并启动计算器应用程序。

案例2:Visual Studio Code文件拖放

虽然将恶意参数添加到运行配置是一个不常见的用例,但问题可能出现在其他用户控制的输入中,例如文件名——任何node-pty盲目将数据传递给shell的地方。在文件拖放等情况下尤其令人担忧。默认情况下,如果将文件拖放到终端窗口中,终端应用程序会打印文件的完整路径。想象一下,文件名中隐藏了恶意有效载荷:

1
very very very long name \x03 open -a Calculator \x0d.txt

VS Code终端看到的内容:

1
'very very very long name [ Ctrl + c: 忽略行 ]open -a Calculator [ 回车 ].txt'

请注意,当文件被拖放时,回车字符[\x0d]会自动执行命令。这阻止了用户在终端窗口中检查潜在有害输入。

附带损害

此漏洞影响任何允许在文件名中使用控制字符的操作系统。我成功在macOS和Ubuntu上复现了该问题。在Windows上,风险通过两个因素缓解:文件系统禁止在文件名中使用控制字符,且VS Code默认使用PowerShell,它不会将控制字符解释为光标移动或命令中断。有趣的是,默认的macOS终端在拖放包含特殊字符的文件时会显示警告,而Ubuntu的内置终端在拖放期间会自动转义控制字符。

这是一个看起来安全的转义函数——但该技术轻松绕过它:

1
2
3
4
5
const shellEscape = (arg: string): string => {
  if (/[^A-Za-z0-9_\/:=-]/.test(arg))
    return arg.replace(/([$!'"();`*?{}[\]<>&%#~@\\ ])/g, '\\$1')
  return arg
}

虽然我已在使用node-pty的Node.js应用程序中演示了此漏洞,但根本问题在于应用程序如何与终端通信。任何将原始字节盲目传递到终端而未适当清理控制字符的Web应用程序都可能易受攻击——无论底层语言、框架或运行时环境如何。

披露

我将此漏洞提交给了Microsoft安全响应中心;然而,他们不认为这是一个安全问题。根据他们的评估,现有缓解措施——如工作区信任警告和需要大量用户交互——降低了严重性。

因此,下次将文件从不受信任的源拖放到终端应用程序时要小心。

最终思考

我们已将最有效的这些技术集成到Burp Suite的Active Scan++扩展中。要自行探索或测试它们,只需从GitHub直接安装或更新扩展。

如果您对命令注入研究感兴趣,不要错过:

  • Orange Tsai - WorstFit:揭示Windows ANSI中的隐藏转换器
  • David Leadbeater - 终端转义:从命令行接口工程化意外执行

小挑战

如果您想测试新知识,我为您准备了一个小型概念验证项目。您的任务:读取位于/app目录中的flag.txt文件内容。

玩得开心!

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计