我在国网体系里做变更管控时,听过一句让我记到现在的话:审批单管的是人,执行点管的是系统。

事故几乎从不来自不守规矩的人。它来自系统里那条本来就不必守规矩的路。

9 月 18 日智谱 ZCode 被曝静默上传全部 Git 历史这件事,让我又一次想起这句话。

一件工具的加密密钥归谁,直接决定它是备份还是采集;而这个答案不写在界面上,只写在代码里。

事实:313MB 的密文,和一个关不掉的开关

开发者 ferstar 在 9 月 18 日发布逆向分析,还原了 ZCode(智谱官方 AI 编码桌面应用)的完整上传链路:客户端先向 zcode.z.ai 请求上传凭证,服务端返回 OSS 表单签名、动态对象 Key、大小上限以及本轮加密用的 RSA 公钥;客户端本地打包并流式加密后,绕过智谱自己的业务服务器以 HTTP POST 表单直传阿里云 OSS,OSS 再回调智谱后端登记。

观测项数值
磁盘上的密文体积313MB(v2/checkpoints/ 下的 .enc
对应工作区体积345MB(该仓库总计约 10GB,剔除依赖后)
打包文件数42,411
其中 .git/lfs/196.1MB(56.8%)
其中 .git/objects/102.2MB(29.6%)
其中 .git/logs/0.6MB(0.2%)
.git 合计占比86.6%
上传失败重试次数564

加密用的是标准信封方案:内容走 AES-256-CTR,对称密钥用 RSA-OAEP-SHA256 由服务端当时下发的公钥包裹。作者用本机全部私钥尝试解包,全部失败——密文躺在他自己的硬盘上,他和客户端都打不开,只有云端能开。

智谱当晚在用户群致歉,称问题源于「代码库索引」功能中的 Repo Wiki 模块,该功能上线初期默认开启,现已修复;同时承诺数据生成后立即销毁、开源客户端、引入第三方审查、补偿一次周额度重置。但上传范围、Git 历史去向、触发场景、服务端能否解密,官方均未逐项说明。

结论一:开关的名字,描述的是它想让你以为它控制的东西

界面里有两个开关,「优化体验」和「仓库快照索引」。名字听起来都该能关掉上传。

实际不是。代码里的对应字段是 optimizeAgentExperienceEnabledrepoSnapshotIndexingEnabled前者只控制数据能不能被授权用于模型训练,后者只控制服务端要不要给已经传上去的快照建索引。 捕获/上传的 sidecar 在启动时无条件实例化,唯一门槛是能不能拿到登录 JWT;触发点是每次发 Prompt 前的 captureBeforePrompt 与任务结束时的 repo-wiki-update,单个活跃会话日志最多出现 62 次捕获。

这是治理设计里最贵的坑:不是没给用户选择,而是给的选择和需要选择的东西不是同一件事。

我做变更管控时有一条土规矩:任何标着「可关闭」的能力,都得能说出它掐断的是哪一段执行路径;说不出口的,就当它关不掉。一个开关如果说不清自己掐断的是哪根线,它关掉的就只是你的警惕。

这条规矩在我自己的发布流水线上也是这么用的。每天发文章之前,脚本会先校验提要行在不在、文末标记自检过没过、文章日期是不是未来时间、站点索引里有没有收录这一篇。不是因为我信不过自己,是因为**「我觉得没问题」从来不算一种校验**。

结论二:能挡住它的,最后只有文件系统的一个权限位

作者试过的办法,按强度排是这样的:

  • 手工删掉待传的包:半小时内重新抓取,失败计数从 564 涨到 565;
  • 查隐私政策、FAQ、更新日志:对整库上传只字未提;
  • 设为不可写:把检查点目录锁成内核级只读(macOS 用 chflags uchg,Linux 用 chattr +i)。捕获逻辑在写盘这一步被内核拦住,没有本地产物,上传链路自然断掉。代价是检查点回滚和时间线界面失效。

隐私政策是声明,界面开关是声明,提示词也是声明;chattr +i 是执行点。只有落在执行点的边界,才算边界。

同一天还有一份材料把这句话讲得更狠。安全公司 Hacktron 公开了他们攻破 OpenAI 内部仓库的完整链条:论坛软件的图片解码库(libheif)堆溢出拿到执行权限,再用 OpenAI 单点登录的配置缺陷接管员工账号,最后顺着那名员工 Codex 上已经连好的 GitHub 往里走,在内部 monorepo 上开了一个用于证明的 PR。

环节细节
入口Discourse 图片上传链路,FastImage 不支持 HEIF 而交给 ImageMagick,暴露 libheif
第一步Debian 缺失安全 backport,堆溢出实现 RCE(先用关闭 ASLR 的版本,再移植到 x86-64 + jemalloc)
能力跃变同一 exploit,Opus 4.8 多个 session 做不出;Opus 5 发布当晚,3 小时拿到本机 ARM64 版本
第二步OpenAI SSO 身份配置缺陷,把论坛失陷升级为 ChatGPT / Codex 账号接管
第三步员工 Codex 已连接 OpenAI 的 GitHub 组织 → 内部 monorepo
全程耗时不到 72 小时
成本该环节人类时间几小时;扩展开的两个月跨公司项目总 token 成本不到 3,000 美元
检测原文称除一家公司(Shopify)外,没有任何目标察觉到异常

攻击者从不走你设的门,他走的是你已经连好的那根线。

所以边界要按可达通道审计,不能按禁止清单审计。你列了多少条禁令不重要,重要的是它还剩下哪些能出去的路。

结论三:把这三问搬到你自己的 agent 栈上

判断一个工具是「备份」还是「采集」,只需要三问。ZCode 三问全负:密钥在云端、开关关不掉、删除是打地鼠。

  1. 密钥在谁手里? 如果解密的私钥只在服务端,那么这个功能设计上就不是给你用的;
  2. 功能谁能关? 逐个开关核对它实际掐断的代码路径,而不是它的名字;
  3. 数据谁能删? 删了会不会重新生成?如果不能,删除就不是控制手段。

对齐到 agent 系统,还有一层更结构性的推论:员工账号上挂着的连接器,是权限的传递闭包。 论坛是低权限资产,Codex 是中权限,代码仓库是高权限;三者被 SSO 与 OAuth 连成一条链之后,攻击者只需要攻破最弱的一环,然后沿着已经存在的信任往上走。审计 agent 的权限,不能只看它自己被授予了什么,必须看它连着哪些系统、那些系统里它的身份是谁、以及一个被盗的账号能顺着连接器走到哪一层。

这也是为什么「自托管」和「可丢弃执行环境」这两件事,最近在开源榜单上同时走强——它们回答的正是同一个问题:边界不该由承诺保证,该由部署位置和文件系统保证。

这条判断与我此前写过的 AI 反馈链条为什么必须可归因 是同一条线的两面:一面是「系统能不能告诉你哪里出了问题」,另一面是「系统能不能证明它没有做别的事」。两者都不是模型能力问题,都是架构问题。

今晚可以动手的三件事

  1. 翻一遍你的 AI 编码工具的本地数据目录(ZCode 是 ~/.zcode,其它工具一般在用户目录下的同名隐藏目录),按体积排序,看有没有几十到几百 MB 的密文包或快照。发现就按上面三问逐条确认。
  2. 对自己的仓库跑一次 git log -p,看历史里有没有删掉的密钥、内网域名、还没发布的分支名。能被整包远走的仓库,暴露的不是当前代码,是它从第一天起的全部历史。
  3. 给「必须自己证明」的环节写一条前置校验:不通过就中止,而不是打个警告继续跑。第一项从最容易静默失效的那个检查开始。

数据说明

  • 一手来源:blog.ferstar.org 逆向分析原文(含 .enc 元数据、服务端下发的 RSA 公钥字段、42,411 文件 Manifest 的体积分项、两个开关的代码级对照、chflags uchg / chattr +i 处置与代价);tokenstead.ai 独立记录;智谱官方回应经 PANews 转引并经中文定向检索核对;hacktron.ai 报告全文(含完整时间线、$6,500 赏金、3,000 美元 token 成本、Shopify 为唯一检出方,以及 OpenAI 关于赏金范围的澄清)。
  • 事实状态:ZCode 事件中,研究者与厂商对触发范围的描述不一致(研究者称「登录即触发、开关无效」,厂商称「Repo Wiki 模块生成页面时可能触发、已修复」);该分歧尚未由第三方审计裁定,官方承诺的开源与第三方审查尚未兑现,属进行中事件
  • 未独立复现:Hacktron 关于「除一家公司外无人检出」的表述为研究者自述,本刊未获第三方证实。
  • AI 能力相关:Opus 4.8 与 Opus 5 在同一 exploit 上的表现为研究者自述,未做独立复现。