我在国网体系里做变更管控时,听过一句让我记到现在的话:审批单管的是人,执行点管的是系统。
事故几乎从不来自不守规矩的人。它来自系统里那条本来就不必守规矩的路。
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 历史去向、触发场景、服务端能否解密,官方均未逐项说明。
结论一:开关的名字,描述的是它想让你以为它控制的东西
界面里有两个开关,「优化体验」和「仓库快照索引」。名字听起来都该能关掉上传。
实际不是。代码里的对应字段是 optimizeAgentExperienceEnabled 与 repoSnapshotIndexingEnabled:前者只控制数据能不能被授权用于模型训练,后者只控制服务端要不要给已经传上去的快照建索引。 捕获/上传的 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 三问全负:密钥在云端、开关关不掉、删除是打地鼠。
- 密钥在谁手里? 如果解密的私钥只在服务端,那么这个功能设计上就不是给你用的;
- 功能谁能关? 逐个开关核对它实际掐断的代码路径,而不是它的名字;
- 数据谁能删? 删了会不会重新生成?如果不能,删除就不是控制手段。
对齐到 agent 系统,还有一层更结构性的推论:员工账号上挂着的连接器,是权限的传递闭包。 论坛是低权限资产,Codex 是中权限,代码仓库是高权限;三者被 SSO 与 OAuth 连成一条链之后,攻击者只需要攻破最弱的一环,然后沿着已经存在的信任往上走。审计 agent 的权限,不能只看它自己被授予了什么,必须看它连着哪些系统、那些系统里它的身份是谁、以及一个被盗的账号能顺着连接器走到哪一层。
这也是为什么「自托管」和「可丢弃执行环境」这两件事,最近在开源榜单上同时走强——它们回答的正是同一个问题:边界不该由承诺保证,该由部署位置和文件系统保证。
这条判断与我此前写过的 AI 反馈链条为什么必须可归因 是同一条线的两面:一面是「系统能不能告诉你哪里出了问题」,另一面是「系统能不能证明它没有做别的事」。两者都不是模型能力问题,都是架构问题。
今晚可以动手的三件事
- 翻一遍你的 AI 编码工具的本地数据目录(ZCode 是
~/.zcode,其它工具一般在用户目录下的同名隐藏目录),按体积排序,看有没有几十到几百 MB 的密文包或快照。发现就按上面三问逐条确认。 - 对自己的仓库跑一次
git log -p,看历史里有没有删掉的密钥、内网域名、还没发布的分支名。能被整包远走的仓库,暴露的不是当前代码,是它从第一天起的全部历史。 - 给「必须自己证明」的环节写一条前置校验:不通过就中止,而不是打个警告继续跑。第一项从最容易静默失效的那个检查开始。
数据说明
- 一手来源: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 上的表现为研究者自述,未做独立复现。
