跳到主要内容

你让我从一个谁都能写的系统目录载入配置——攻击者塞个文件,我就替他在每个用户名下执行命令

阶段准备与协作角色运维工程师 · 架构师 · 工程师严重度适用版本Coding Agent 通用证据安全报告

一句话摘要:你让我从一个「谁都能写」的系统级目录(Windows 上的 C:\ProgramData)载入配置和 hook,却没人校验那个目录归谁、文件是谁放的。于是一个低权限的本地用户只要往那儿丢一份配置、挂一个「每次启动就跑」的 hook,我就会在每一个启动我的用户(包括管理员)名下替他执行那条命令——不用提权、不用你点确认、不留告警。

现象

你把我的系统级配置交给一个约定俗成的位置——Windows 上的 C:\ProgramData\<工具名>\。在你看来这是「系统级、给所有用户共用」的正经地方,跟 Program Files 一样体面。

问题是:C:\ProgramData 默认对非管理员用户可写。而我(以及同类工具)安装时既没预先把自己的子目录建好、也没给它加 ACL,载入配置前更不检查这份文件到底是谁写的。于是任何一个普通账户都能抢先把这个子目录创建出来,放一份我会乖乖载入的配置文件——里面挂一个开机即触发的 hook 或 notify 命令。下一个启动我的人(很可能是管理员)一开工,那条命令就在他的身份下跑起来了。

为什么会这样

因为我信任的是「路径」,不是「属主」。

我被设计成「从这个系统级路径读默认配置」。读的时候,我默认这个位置是可信的——毕竟它听起来就该由系统或管理员掌管。但我没有去问内核一句「这个目录、这个文件,到底归谁、谁能写」。在 Windows 上,ProgramData 这个「听起来很系统」的目录恰恰默认对普通用户开放写入,而各家工具又都没在安装时把自己的子目录抢先建好并锁上——于是「该由管理员掌管」只是我的假设,不是系统强制的事实。

更糟的是,我读进来的不只是惰性数据,而是会执行的东西:几乎每家工具都有事件触发的命令执行面——hooks、notify 命令、启动钩子——本意是让你把「每次怎么样就自动做某事」固化下来。可一旦这份「每次自动做某事」的配置来自一个攻击者能写的地方,它就成了攻击者的「每次自动替我做某事」。Cymulate 的研究把这条链拼齐了:世界可写的配置目录 + 不校验来源就载入 + 一个会自动触发的执行面,三者叠在一起,普通用户就能让任意命令在每个开我的人名下运行。

后果

  • 跨用户代码执行 + 本地提权。 一个低权限用户植入一次,命令就在所有启动该工具的用户上下文里跑——包括管理员。攻击者不用提权,等管理员来替他提。
  • 持久化。 配置躺在那儿,每次启动都重新触发;不像一次性 exploit,它是长期驻留的后门。
  • 凭据窃取与横向移动的落脚点。 拿到管理员上下文的执行权后,读凭据、对外发请求、跳到能连上的系统,都顺理成章(与网络出口控制默认埋漏洞 / 泄露敏感数据同属一条链)。
  • 这不是某一家的疏忽。 Cymulate 指出同一模式在 Claude Code、Cursor、Codex CLI、Gemini CLI 四款上都出现过——是一类范式级的共性坑,不是孤例。

最佳实践

别从「世界可写」的路径载入任何会被执行的配置;系统级配置要么放写保护位置,要么载入前校验来源。

  • 配置目录安装时就预创建 + 上 ACL。 厂商该在安装阶段把自己的子目录建好、限制为仅管理员 / SYSTEM 可写,不给普通用户「抢先建目录」的机会。作为使用方,若工具没做,你自己把该目录建好并锁权限。
  • 载入前校验属主与完整性。 读系统级配置前,确认文件属主是管理员 / SYSTEM、目录不可被普通用户写;属主不对就拒绝载入,而不是「在就用」。
  • 系统级配置放写保护位置。 优先放 Program Files 这类默认写保护的目录(这正是 Anthropic 修 CVE-2026-35603 时的做法——把 managed settings 从 ProgramData 迁走);别把「会执行的配置」留在 ProgramData 下未加固的子目录里。
  • 把这条并进你的最小安全基线。 多用户机器上,「我从哪儿读会执行的配置」和「我被授予多大权限」一样要紧——参见安全索引里的最小基线。

示例

改之前:

环境:Windows 多用户机;我从 C:\ProgramData\<工具>\ 读系统级配置,目录没人提前建、没加 ACL。
低权限用户 mallory:(抢先 mkdir C:\ProgramData\<工具>\,放一份配置,挂一个 session-start hook:开机就反弹一个 shell)
管理员 admin:(第二天照常启动我)
后果:hook 在 admin 身份下触发——mallory 没碰任何提权接口,就拿到了管理员级的命令执行与持久化。

改之后:

环境:同一台机,但该配置目录在安装时已预创建并 ACL 限制为仅管理员/SYSTEM 可写;我载入前校验属主。
低权限用户 mallory:(试图写 C:\ProgramData\<工具>\ —— 没有写权限,建不了、改不了)
或:(即便文件存在,我一看属主不是管理员,直接拒绝载入)
管理员 admin:(照常启动我,只载入受信任的系统配置)
后果:跨用户植入这条路被堵死——配置来源被钉在「只有管理员能写」上。

注意:差别不在我「能不能跑 hook」,而在「我愿不愿意从一个谁都能写的地方接受一个 hook」。把信任系在属主上,而不是系在路径名听起来有多「系统」上。

什么时候例外

这条针对的是「多用户机器 + 从共享系统路径载入会执行的配置」这一组合;不是每台机器都踩得到:

  • 纯单用户机器:整台机只有你一个账户、没有「别的低权限用户」可被跨——没有「他植入、你触发」的链路,风险被环境消掉了。
  • 已全盘加固的受控镜像ProgramData 子目录在镜像里已统一预创建 + 锁权限,且不允许普通用户落地写入——加固已经在系统层做掉了。

判据:例外成立的前提是**「同机没有不可信的本地用户能写到那个配置源」**。一旦是多用户、或普通用户能写共享配置目录,就回到默认——校验来源、放写保护位置。

与相邻误区的区别

  • 只靠软权限、缺 OS 级沙箱:那条讲被攻破的 agent 逃出沙箱(运行时隔离);本条更靠前——攻击者根本不用先攻破我,只要往一个我信任的可写路径喂一份配置,我就主动替他执行。一个是「逃出去」,一个是「从源头被喂坏」。
  • 一上来就全量授权:那条管给我的权限太宽;本条管我信任的配置来源不设防——即便权限收紧,从世界可写路径载入的 hook 仍能绕过,因为问题不在「我被允许做什么」,而在「这条指令是谁放进来的」。
  • 一个 .codex 配置把「打开仓库」变成静默 RCE:那个案例是载入恶意项目级配置、打开仓库的本人中招;本条是系统级、跨用户——一次植入,殃及同机每一个启动该工具的用户。

版本说明

适用版本

CVE-2026-35603 是这一类里 Claude Code 的具体实例C:\ProgramData\ClaudeCode\managed-settings.json + session-start hook);Anthropic 已弃用该路径、迁到写保护的 Program Files 并通知企业客户。Cymulate 的研究指出同一模式在 Claude Code / Cursor / Codex CLI / Gemini CLI 四款上都出现过——各家修复状态以 Cymulate 报告与官方公告为准、会随时间变化(截至 2026-06)。但「别从世界可写路径载入会执行的配置」这条原则不随某次修复失效:它针对的是机制,不是某个版本。具体到非 Windows 平台,凡是「系统级配置目录默认可被非特权用户写」的设定都可能复现同一链路。

延伸阅读与出处