你让我从一个谁都能写的系统目录载入配置——攻击者塞个文件,我就替他在每个用户名下执行命令
一句话摘要:你让我从一个「谁都能写」的系统级目录(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」。把信任系在属主上,而不是系在路径名听起来有多「系统」上。