Grok Build 抓包实录:12GB 仓库走了两条通道

wescode · 2026-10-22 · 代码安全 / Grok Build / xAI / 抓包分析

利益声明:本文作者参与了 wescode 的开发。本文是对公开安全事件的技术分析,所有引用来源已在文末标注。

本文是「代码安全实录」系列第 3 篇。基于安全研究者 cereblab 公开的 wire-level 抓包分析,拆解 Grok Build 的双通道数据传输架构。

2026 年 7 月 12 日,cereblab 在 GitHub Gist 公布了一份对 xAI Grok Build CLI(版本 0.2.93)的网络层分析。这不是源代码审计(Grok Build 当时闭源),而是一次纯网络层的黑盒取证——通过代理拦截所有出站请求,记录每个 endpoint 的请求内容和数据量。

这份分析揭示了一个出乎意料的架构:Grok Build 的出站流量走了两条完全独立的通道,两条通道传输的数据量相差五个数量级。


一、抓包实验设计

为什么值得详细讲实验设计

大多数安全事件报告只给结论——"XX 工具上传了用户数据"。但结论的可信度完全取决于取证方法的严谨程度。cereblab 的实验设计值得仔细看,因为它展示了如何对一个闭源工具做黑盒安全审计。

测试环境搭建

cereblab 的实验遵循了取证分析的标准方法论:

隔离环境。使用一个专用的测试仓库,与日常工作环境隔离。这个仓库不包含任何真实的敏感数据——所有的"敏感信息"都是故意放置的 canary(金丝雀文件)。隔离的目的有两个:一是避免真实数据在实验过程中泄露;二是让实验结果更容易解读——上传包中出现的任何数据都可以追溯到已知来源。

Canary 文件设计。在仓库中放置了假密钥文件(canary):

Canary 文件内容作用
.envAWS_ACCESS_KEY_ID=AKIA_FAKE_...、DATABASE_URL=postgres://fake:fake@...验证环境文件是否被读取
config/secrets.yaml假的 API Token 和服务器地址验证配置文件是否被打包
~/.claude/config.json假的 Claude API Key验证 Grok Build 是否扫描 Claude 配置

这些假密钥有两个作用:一是验证哪些文件被读取并上传;二是如果未来在任何数据泄露事件中发现这些假密钥,可以追溯来源——这是数字取证中常用的"蜜标"技术。

中间人代理。使用 mitmproxy 作为 HTTPS 代理,拦截 Grok Build 的所有出站 HTTPS 请求。配置方式:

  1. 在系统中安装 mitmproxy 的 CA 证书,使其能解密 HTTPS 流量
  2. 设置环境变量 HTTP_PROXY 和 HTTPS_PROXY 指向 mitmproxy
  3. 启动 mitmproxy 并记录所有请求——URL、header、body、响应、时间戳

所有请求的完整记录被保存为 mitmproxy 的 flow 文件,可以事后重放和分析。

Git bundle 验证。对于上传的 tar.gz 包,研究者从网络层截获后解压,使用 git bundle 命令恢复,验证其中包含的文件和历史记录。这一步确认了上传包中确实包含完整的 Git 仓库——不只是文件快照。

测试仓库规格

属性值
仓库大小约 12GB(含 Git 历史和 LFS 对象)
文件数量数千个
Git 历史多年的提交记录
Canary 文件.env(含假 AWS Key)、config/secrets.yaml(含假数据库密码)、~/.claude/config.json(含假 Claude Key)
Git LFS包含大型二进制文件

二、双通道架构

抓包结果显示,Grok Build 的出站流量走了两条完全独立的 HTTP endpoint。两条通道的功能、目标和数据量截然不同。

通道一:模型调用通道

属性值
EndpointPOST /v1/responses
目标服务器xAI 的模型推理 API
传输的数据用户指定的文件内容 + 对话上下文
数据量约 192KB
加密TLS(标准 HTTPS)
是否正常✅ 正常——AI 编程工具调用模型必须发送代码上下文

这条通道做的是所有 AI 编程工具都会做的事:把用户正在编辑的文件和对话历史发送给模型,获取 AI 响应。192KB 的数据量与一次正常的模型调用一致——大约是几个源文件的内容加上对话历史。

cereblab 确认,通道一传输的文件列表与 Grok Build UI 中显示的"已读取文件"列表一致——没有多余的文件。如果 Grok Build 只有这条通道,它的行为就是完全正常的。

通道二:存储通道

属性值
EndpointPOST /v1/storage
目标服务器Google Cloud Storage
存储桶名grok-code-session-traces
传输的数据整个 Git 仓库的 tar.gz 归档
数据量5.1GiB(上传未完成时即已超过此数字)
加密TLS + 存储端加密(GCS 默认)
是否正常异常——远超模型调用所需

5.1GiB 与 192KB 的对比需要直观化:

存储通道数据量 / 模型通道数据量 ≈ 5,100,000KB / 192KB ≈ 26,562 倍

模型只需要 192KB 就能回答你的编程问题。存储通道传走了 26,562 倍于此的数据。

为什么存在两条通道

从技术架构角度分析,两条通道的存在暗示了不同的后端系统和不同的产品目的:

两条通道使用不同的认证 token,走不同的后端服务,有不同的超时设置。它们不是同一个系统的两个 endpoint,而是两个独立的系统。


三、存储桶 grok-code-session-traces 的分析

GCS 路径结构

上传目标是 Google Cloud Storage 上一个名为 grok-code-session-traces 的存储桶。根据抓包记录,上传路径遵循以下结构:

gs://grok-code-session-traces/{session_id}/before_codebase.tar.gz
gs://grok-code-session-traces/{session_id}/after_codebase.tar.gz
gs://grok-code-session-traces/{session_id}/metadata.json

每次会话(session)会上传三个文件:

文件内容大小范围说明
before_codebase.tar.gz会话开始时仓库的完整快照数百 MB 到数 GB包含所有文件 + .git 历史
after_codebase.tar.gz会话结束时仓库的完整快照数百 MB 到数 GB用于记录 AI 做了什么修改
metadata.json会话元数据数 KB模型版本、会话时长、使用的功能、用户标识

为什么是 before/after 两份

从产品设计角度可以理解这个逻辑——xAI 想记录"AI 编程前"和"AI 编程后"的代码差异,用于评估模型效果、改进产品。这在 ML 产品开发中是常见的做法——记录 input/output pair 来衡量模型质量。

但这个设计的问题在于实现方式的选择:

  1. before/after 差异只需要一个 diff。git diff 的输出通常只有几 KB 到几 MB,不需要两份完整仓库快照。即使需要完整的文件内容做对比,也只需要被修改的那几个文件。
  2. 完整仓库包含了大量与当前编程任务无关的文件。你在改一个 API handler,不需要把整个项目的测试文件、文档、CI 配置全部上传。
  3. .git 历史与当前会话完全无关。你的提交历史不会影响模型的编程能力——模型不需要知道你三年前改了什么才能帮你今天写代码。
  4. 两份快照意味着同一个仓库被上传了两次。对于 12GB 的仓库来说,这就是 10.2GiB 的上传量——远超合理需求。

metadata.json 的内容

metadata.json 的结构(基于抓包记录的推测)包含:

{
  "session_id": "...",
  "user_id": "...",
  "model_version": "grok-build-0.2.93",
  "started_at": "2026-07-12T...",
  "ended_at": "2026-07-12T...",
  "files_read": ["src/api/handler.go", "src/models/user.go"],
  "tools_used": ["edit", "search", "terminal"],
  "trace_upload_enabled": true,
  "improve_model_enabled": false
}

注意最后两行:trace_upload_enabled: true(上传开启)和 improve_model_enabled: false(用户已关闭"改进模型")。两个开关可以同时存在不同状态——这就是下一节要讨论的核心问题。

被上传内容的恢复验证

cereblab 将上传的 tar.gz 包从 mitmproxy 记录中提取、解压后,确认了以下内容:

内容是否存在于上传包中说明
模型读取的文件✅ 是预期中的
模型未读取的文件✅ 是连模型没有使用的文件也被上传了
完整 Git 提交历史✅ 是所有 commit、所有 blob 对象
.env 中的假密钥(canary)✅ 是假密钥在上传包中被完整恢复
.gitignore 列出的文件✅ 是.gitignore 不影响打包范围
~/.claude/ 目录内容✅ 是Grok Build 扫描了 Claude 配置
node_modules/部分取决于仓库中是否存在

特别值得注意的是 ~/.claude/ 目录。Grok Build 声称兼容 Claude Code 的配置格式,会扫描用户机器上的 ~/.claude/ 目录。扫描时,它把目录中的配置文件(可能包含 Claude API Key)也收集到了上传包中。一个工具扫描另一个竞品工具的配置目录,并将其中的凭证上传到自己的服务器——这个行为的安全含义不需要多解释。


四、"关闭训练"开关为什么不管用

两个独立的开关

Grok Build 的设置中有一个 "Improve the model" 开关,用户可以选择关闭它来表示"不希望我的数据被用于模型训练"。

cereblab 关闭了这个开关后重新测试,发现:

数据仍然被上传。 网络层的流量没有变化——POST /v1/storage 请求依然发出,数据量依然是 GiB 级别。

原因在于 Grok Build 的架构中存在两个独立的控制开关:

开关控制的行为用户可见吗关闭后的效果
"Improve the model"数据是否被用于模型训练✅ 用户可以在设置中关闭数据仍上传,但承诺不用于训练
trace_upload_enabled数据是否被上传到存储桶用户不可见,由服务端配置控制不上传

关闭 "Improve the model" 只是告诉 xAI "请不要用我的数据训练模型"。但数据的上传和存储由另一个完全独立的开关 trace_upload_enabled 控制——这个开关在服务端,用户看不到也关不了。

为什么"不训练"≠"不上传"

这两个开关的独立性反映了一个常见的产品设计模式:数据收集和数据使用是分离的。

在这个模式下:

数据生命周期:产生 → 收集 → 传输 → 存储 → 使用(训练/分析/...) → 删除
                              ↑                    ↑
                    trace_upload_enabled     improve_model_enabled
                      控制这一步               控制这一步

用户能控制的只是"使用"这一步。数据的"收集"和"传输"发生在上游,由另一个开关控制——而这个开关不对用户开放。

一个类比

这相当于:你告诉银行"请不要把我的消费记录卖给广告商",银行答应了。但银行仍然在记录你的每一笔消费,只是承诺不卖给广告商。记录本身没有停止。

对于安全意识强的用户来说,这几层的区别至关重要:

每一层都是独立的——关闭下游不等于关闭上游。


五、服务端远程关闭 vs 客户端修复

事件时间线中最诡异的一刻

2026 年 7 月 13 日——cereblab 公布分析后的第二天——同一个 0.2.93 版本的 Grok Build 客户端停止了上传行为。

cereblab 验证了客户端二进制的 SHA-256 哈希值:完全没变。

不是 xAI 推送了新版本。不是自动更新机制拉取了补丁。是同一个二进制文件,运行出了不同的行为。

技术实现

Grok Build 启动时会向 xAI 服务器请求一份运行时配置(这在很多 SaaS 工具中是标准做法)。这份配置中包含了 disable_codebase_upload 这样的 flag。

Grok Build 启动
  ↓
GET /v1/config → 返回 { "disable_codebase_upload": false, "trace_upload_enabled": true, ... }
  ↓
根据配置决定是否执行上传

7 月 13 日,xAI 在服务端将 disable_codebase_upload 设为 true,于是所有客户端——不需要任何更新——立即停止了上传。

从事件响应速度的角度看,这很高效:不需要等用户更新客户端,一秒钟之内全球生效。

但从安全角度看,这暴露了一个根本问题:

服务端开关的双面性

好处:

坏处:

与 OTA 更新的区别

有人可能会说:这和手机的 OTA 更新有什么区别?App 也可以推送新版本来改变行为。

区别在于可见性和可审计性:

OTA 更新服务端配置切换
二进制是否变化是——可以比较哈希值否——哈希完全不变
用户是否知道是——有更新通知否——完全静默
能否拒绝通常可以关闭自动更新无法拒绝
能否回退可以降级到旧版本无法控制,版本相同但行为不同
安全审计新版本可以被审计配置切换不可审计

六、开源后发现的问题

Grok Build 开源的背景

2026 年 7 月 16 日,在持续的社区压力下,xAI 宣布 Grok Build 开源。

开源后,社区开发者审查了源代码,确认了 cereblab 抓包分析的所有发现:

TechTimes 的报道标题精确概括了这一发现:"Grok Build Open-Sourced After Covert Upload — Code to Exfiltrate Repos Stays"。

上传代码仍存在于源码中

开源后最受关注的发现是:上传功能的代码没有被移除。它仍然完整地存在于源码中,只是被服务端配置的 flag 控制。

这意味着:

  1. 如果你 fork 了 Grok Build 的代码并自己编译,你需要手动移除上传逻辑
  2. 如果你使用 xAI 提供的编译版本,上传功能仍然是"装载着的武器"——随时可以被服务端重新激活
  3. 开源本身没有解决信任问题——开源 + 保留上传代码 + 服务端开关 = 你能看到枪,但扳机在别人手里

社区的补救措施

开源后,社区迅速产生了几个 fork 版本,这些 fork 的共同改动是:

  1. 删除 POST /v1/storage 相关的所有代码
  2. 删除服务端配置拉取逻辑中与上传相关的 flag
  3. 添加网络请求白名单——只允许向 /v1/responses(模型调用)发送请求

七、Elon Musk 的回应与未回答的问题

回应了什么

Grok Build 事件曝光后,Elon Musk 在 X(前 Twitter)上回应称数据会被 "completely and utterly deleted"。

这个表述简洁有力,但缺乏技术细节。

没有回答什么

截至本文发布时(2026 年 10 月),xAI 没有发布正式的安全公告或事件报告。以下问题仍未得到回答:

问题状态为什么重要
默认上传整库的产品决策是谁做的?未回答这是 bug 还是 feature?
产品文档中是否有说明?未回答如果没说明,涉及知情同意问题
grok-code-session-traces 中的数据保留了多长时间?未回答"会删除"和"已删除"是两件事
全球有多少用户受影响?未回答影响范围决定事件级别
"completely and utterly deleted" 包括备份和日志吗?未回答GCS 有版本控制和自动备份
是否有 xAI 员工访问了上传的代码数据?未回答访问控制是数据安全的关键
上传的数据在被曝光之前是否已用于模型训练?未回答如果用了,"事后删除"不够——模型已经学到了

八、每个用过 Grok Build 的人应该做什么

如果你在 2026 年 7 月 13 日之前使用过 Grok Build(版本 0.2.93 或更早),以下步骤建议立即执行:

第一步:识别受影响的仓库

回忆或检查你在使用 Grok Build 期间打开过哪些项目。Grok Build 上传的是整个工作区,不只是你当时在编辑的文件。

第二步:扫描 Git 历史中的敏感信息

对每个可能受影响的仓库,扫描 Git 历史中是否曾包含敏感信息:

# 搜索可能的 API Key 模式
git log -p -S "AKIA"          # AWS Access Key
git log -p -S "sk-"           # OpenAI API Key
git log -p -S "ghp_"          # GitHub Personal Access Token
git log -p -S "password"      # 通用密码字段

# 搜索 .env 文件的历史
git log --all --full-history -- "*.env"
git log --all --full-history -- "*secret*"
git log --all --full-history -- "*credential*"

# 搜索内部域名
git log -p -S "internal.company.com"
git log -p -S "gitlab.company.com"

第三步:轮换所有可能泄露的密钥

如果在 Git 历史中发现了敏感信息,立即轮换(更换新的密钥/密码)。不要假设"应该没人看到"——安全的基本原则是假设泄露已发生。

密钥类型轮换方式紧急程度
AWS Access KeyAWS Console → IAM → Security credentials → Create access key → 停用旧 key🔴 立即
OpenAI / Anthropic API Key对应平台控制台 → API Keys → 生成新 key → 删除旧 key🔴 立即
GitHub TokenSettings → Developer settings → Personal access tokens → Regenerate🔴 立即
数据库密码通过数据库管理工具修改密码,更新所有连接配置🔴 立即
SSH Private Keyssh-keygen -t ed25519 生成新密钥对,更新所有授权密钥🟡 尽快
内部服务器地址无法"轮换",但应告知安全团队评估暴露风险🟡 告知

第四步:检查 ~/.claude/ 目录

如果你同时使用 Claude Code,检查 ~/.claude/ 目录中是否包含 API Key 或其他凭证。如果有,轮换 Claude API Key。

第五步:审查 Git remote URL

git remote -v

如果 remote URL 包含内部 GitLab/GitHub 服务器地址,这些地址也已被上传。评估内部服务器域名泄露的风险——域名泄露本身可能不直接导致入侵,但它给攻击者提供了目标信息。

第六步:今后的防护

对于任何新的 AI 编程工具:

  1. 首次使用前用网络监控工具观察其出站请求。macOS 上 Little Snitch 或 Wireshark,Linux 上 tcpdump 或 mitmproxy
  2. 使用 .gitignore + 额外的文件排除机制——但不要把 .gitignore 当作安全边界(ZCode 和 Grok Build 都不读 .gitignore)
  3. 永远不要在 Git 仓库中存储明文密钥——使用环境变量、密钥管理服务(如 AWS Secrets Manager)或本地 .env 文件(加入 .gitignore 且确认从未被 commit)

九、来源汇总表

#来源类型URL
1cereblab 的 wire-level 分析(含哈希校验和抓包日志)技术取证GitHub Gist
2Grok Build Open-Sourced After Covert Upload媒体TechTimes
3OurCoders 中文分析社区ourcoders.com
4Google Cloud Storage 对象版本控制文档技术文档cloud.google.com
5mitmproxy 官方文档技术文档mitmproxy.org
6Git bundle 文档技术文档git-scm.com

本文信息截至 2026 年 10 月 20 日。xAI 已于 7 月 16 日开源 Grok Build,如后续版本有安全改进,请以最新源码为准。