跳转至

解析 Windows .cmd 垫片以绕过 cmd.exe

Node 拒绝在没有 shell 的情况下启动 .cmd/.bat 目标(CVE-2024-27980 缓解措施),因此 resolveSpawn 必须让 cmd.exe 成为程序,并向其传递 /d /v:off /s /c "<caret-escaped argv>"。对于 agent CLI,这意味着一条很长的 cmd.exe /c 命令行,其中经过脱字符转义的负载是自然语言 Prompt 文本——而 Microsoft Defender for Endpoint 的命令行模型会将其评分为混淆。codex.cmd 正是因为这个原因出现在针对 Orca 的 MDE 事件的 spawn 集群中。

src/shared/child-process/windows-cmd-shim-resolution.ts 绕开了这一点。npm 的 cmd-shim 和 pnpm 的 @zkochan/cmd-shim 生成的文件,其整个内容都是 “查找一个 Node 解释器并运行这个脚本”。读取其中一个文件后,resolveSpawn 可以直接启动 node.exe <script> <args…>:进程树中没有 cmd.exe,也完全不需要 脱字符转义。

解析会改变什么

仅涉及 runProcess / spawnProcess 调用方。有两件事人们期望它覆盖,但它并不覆盖:

  • 交互式终端。 src/main/daemon/pty-subprocess/native-pty-spawn.ts 直接调用 pty.spawn,因此在 Orca 终端中输入 codex 完全不受影响。
  • Orca 自身的 hook 包装器(codex-hook.cmd 等)。这些是 Orca 编写的批处理 文件,不匹配任何生成器形态,因此它们仍走 cmd.exe 路径。它们是可以处理的——我们生成了它们——但不是由这个 模块处理。

添加一种形态

识别四种形态,每种形态都从真实安装中逐字转录到 src/shared/child-process/__fixtures__/windows-cmd-shim-bodies.ts。如果你添加第 五种,也请在那里添加它的真实内容。从文档中猜测出来的形态不算证据。

解析器的规则是全有或全无:整个规范化后的内容必须从头到尾匹配,任何无法识别的内容都返回 null 并保留 cmd.exe 路径。错误的解析会静默地运行错误的程序或丢弃参数,这比 EDR 告警严重得多——有疑问时,拒绝。

解析还会拒绝以下情况:捕获到的路径是绝对路径、驱动器相对路径 (D:evil.js —— win32.isAbsolute 返回 false,但 win32.resolve 会留在 垫片目录中),或包含 % ^ & | < > " : 或换行符;脚本或目标不在磁盘上;没有解释器的目标不是 .exe/.com; 以及程序路径不是绝对路径。

拒绝所有 : 不会导致误拒绝。Windows 在路径段内保留该字符,因此相对路径不能包含它——唯一可能包含它的写法是带驱动器限定、备用数据流(a.js:zone) 或 \\?\ 设备路径,而最后一种已经作为绝对路径被拒绝。

紧急开关

在子进程启动时所用的环境中,将 ORCA_DISABLE_CMD_SHIM_RESOLUTION 设置为任何非空值,所有 .cmd 都会原样改回通过 cmd.exe /c 处理。它从 spawn 自身的环境中读取,因此在启动 Orca 之前导出它会在整个进程中禁用解析。

用它来确认疑似的错误解析:在有和没有该变量的情况下运行失败的操作。行为相同意味着解析不是原因。如果确实是, 请报告垫片的内容——解析器只允许识别我们真实见过的形态。

有意改变的行为

已解析的垫片不仅仅是 cmd.exe 路径的更安静写法。cmd.exe 的两个限制随之消失:

  • 包含 \r/\n 的参数会被直接拒绝,因为 cmd 会在原始换行符处结束命令,无论引号状态如何。多行 agent Prompt 现在可以工作。
  • 超过 8191 个字符的命令行会返回 The command line is too long. 长 Prompt 现在可以工作。

两者都是改进,但它们是行为变化:未解析的垫片仍会触发这两个限制,因此调用方不能假设每个 .cmd 都接受它们。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 orca48/orca