欢迎来到 侠客岛

一入江湖岁月催,代码人生共举杯

焚决 Claude — 一个油猴脚本如何撬开 Anthropic 的支付大门

前言


最近圈子里流传着一个叫「焚决」的东西,名字取得很玄,搞得好像什么了不得的秘术。拿到手一看——是一段不到 200 行的 Tampermonkey 油猴脚本。

但别小看这 200 行代码。它干了一件非常漂亮的事情:在你打开 Claude 网页版准备付费订阅的瞬间,在浏览器内部悄无声息地篡改了一个 API 响应,把你看到的支付页面从信用卡表单换成了 SEPA 银行转账。然后你去 randomiban.com 随便生成一个德国银行账号,填进去,提交——Claude Max 到手,扣款金额 0 元。

这玩意在外面传得沸沸扬扬,各路卡网卖的廉价 Claude 会员,相当一部分就是靠这个批量生产的。

今天咱们把它彻底拆开。不光讲它怎么工作,更要讲为什么能成、它利用了支付系统的哪个结构性缺陷、以及这种攻击模式怎么迁移到其他平台。


一、攻击全景:一个 API 字段改写引发的连锁反应

正常的 Claude 订阅流程

打开 claude.ai,点击升级订阅,前端第一件事不是弹出支付表单——它先问后端一个问题:

GET /api/organizations/{org_id}/subscription/checkout_capabilities

这个接口的职责很简单:告诉前端,当前这个用户应该走哪条支付通道。后端会综合 IP 地理位置、账号注册区域、浏览器 Accept-Language 等信号,返回一个支付流标识:

{
  "checkout_flow": "stripe"
}

前端拿到 stripe,渲染信用卡表单——卡号、有效期、CVV、3DS 二次验证,一套标准流程。信用卡支付是同步授权的,银行实时校验,过不了就是过不了。

劫持后的流程

脚本做的事情只有一件:把所有命中 checkout_capabilities 的响应体替换为:

{
  "checkout_flow": "cassia"
}

cassia 是 Anthropic 内部用于欧洲 SEPA 银行转账的支付流标识。前端一看返回值变了,渲染逻辑直接切换——不再是信用卡表单,而是一个 IBAN 输入框。

这时候去 randomiban.com 生成一个德国 IBAN(格式:DE + 2 位校验码 + 8 位银行代码 + 10 位账号,共 22 位),填进去,点提交。

前端校验?只检查 IBAN 格式是否合法(MOD 97-10 校验通过即可)。

后端校验?SEPA 体系下,提交时不做实时账户验证。

结果:Claude 立刻给你开通了订阅。


二、SEPA 直接借记——为什么填个假账号也能过

这是整个攻击的核心利用点,不理解 SEPA 的清算机制,就不可能理解这个漏洞为什么成立。

先搞清楚 SEPA 是什么

SEPA(Single Euro Payments Area,单一欧元支付区)覆盖 36 个欧洲国家和地区,是欧盟推动的统一支付基础设施。其中的 SEPA Direct Debit(直接借记,德语叫 Lastschriftverfahren)允许商家向消费者的银行账户"拉钱"——不是你给商家转账,而是商家拿着你的授权凭证去你的银行把钱取走。

问题就出在这个"拉钱"的流程设计上。

信用卡 vs SEPA 直接借记:两套完全不同的信任模型

授权模式同步在线授权异步离线扣款
交易发起时校验卡号 Luhn 校验 + CVV + 3DS + 银行实时授权IBAN MOD 97-10 格式校验,仅此而已
银行参与时机交易发起的瞬间交易发起后的 1-3 个工作日
资金确认毫秒级返回授权结果银行不返回任何确认,排队等清算
消费者保护Chargeback 争议流程8 周无条件撤回权(SEPA CORE 规则)

信用卡在你点击支付的那一瞬间就跟银行确认了你有没有钱、卡号对不对、你本人是不是同意的。而 SEPA 直接借记完全不做这些——它只检查 IBAN 的格式,然后把扣款请求扔进银行间的批量清算队列里,等着慢慢处理。

SEPA CORE 清算流程

实际的清算过程是这样的:

你提交 IBAN
  ↓
支付网关做 MOD 97-10 校验(纯数学公式,检查 IBAN 格式是否合法)
  ↓
校验通过 → 支付网关返回"交易成功"
  ↓
Anthropic 收到成功信号 → 开通你的 Claude Max 订阅
  ↓
... T+1 到 T+3 工作日 ...
  ↓
扣款请求进入 SEPA CORE 清算系统
  ↓
商家银行 → 消费者银行:请从账号扣 $20
  ↓
消费者银行查询账号 → 账号不存在 / 余额不足 / 未授权
  ↓
返回 R 代码拒绝(R02=无效账号, R04=账户已关, R05=被授权人撤销...)
  ↓
Anthropic 收到扣款失败通知 → 关闭你的订阅

重点来了:从你提交 IBAN 到银行清算出结果,中间有一个 1-3 个工作日的真空期。在这段时间里,你的 Claude Max 订阅是完全生效的——能用 Opus、能用所有高级功能、流量不限。

MOD 97-10:格式校验的局限性

IBAN 的校验算法叫 ISO 7064 MOD 97-10,原理很简单:

  1. 把 IBAN 的国家代码和校验码移到末尾
  2. 把字母转换为数字(A=10, B=11, ..., Z=35)
  3. 对整个大整数做 MOD 97 运算
  4. 结果等于 1 就合法

这个算法能检出格式错误——打错了某一位、漏了一位、国家代码不对。

这个算法检不出一切跟真实世界有关的东西——账户是否存在、余额是否充足、持有人是不是你。

randomiban.com 生成的 IBAN 就是利用了这一点:它按照 MOD 97-10 算法逆向构造,生成的 IBAN 格式 100% 合法,能通过任何基于此算法的前端和后端校验。至于这个账号在不在现实中存在——那是 3 天后银行清算时才会发现的事情。

为什么偏偏是德国 IBAN?

  1. 格式最简单:DE 开头的 IBAN 固定 22 位,没有额外的国家级校验叠加
  2. 通过率最高:德国 IBAN 在 Stripe 等主流支付网关的接受度最好,不会触发额外的地区风控
  3. 银行代码空间大:8 位 BLZ 有大量有效的银行代码段

对比法国 IBAN(27 位,有额外的 RIB 密钥校验)或西班牙 IBAN(24 位,有 DC 校验位),德国确实是阻力最小的选择。


三、脚本逐行技术拆解

3.1 元数据——抢在一切之前

// @name         TestExample Cassia Response Mock
// @match        *://claude.ai/*
// @match        *://*.claude.ai/*
// @run-at       document-start
// @grant        none
// @sandbox      raw

三个关键设定:

  • @run-at document-start:页面 DOM 还没开始构建,脚本已经注入完毕。整个攻击成立的前提就是比目标请求更早完成注入。
  • @sandbox raw:绕过 Tampermonkey 的安全沙箱,确保 Hook 真正生效。
  • @grant none:减少 Tampermonkey 的安全提示弹窗。

3.2 精确制导——只改一个接口

const TARGET_PATH =
  /^\/api\/organizations\/[^/]+\/subscription\/checkout_capabilities\/?$/;

只拦截这一个接口,其他所有请求原样放行。因为它是 Claude 前端支付流的单一决策点——前端根据它的返回值决定渲染 Stripe 信用卡表单还是 SEPA IBAN 输入框。

3.3 Fetch 拦截——先发后改

const nativeFetch = window.fetch;

window.fetch = async function (input, init) {
  const method = init?.method ||
    (input instanceof Request ? input.method : "GET");
  const targetUrl = getTargetUrl(input, method);
  const originalResponse = await nativeFetch.apply(this, arguments);
  if (!targetUrl) {
    return originalResponse;
  }
  return createMockResponse(originalResponse);
};

先用原生 fetch 把真实请求正常发出去,等拿到响应后再替换返回值。从服务端的角度看——你打开了支付页面,后端日志里看到正常的 GET 请求、正常的 200 响应,一切如常。篡改发生在浏览器内部,从 HTTPS 加密通道往外看什么都没变。

3.4 Response 重构——细节决定成败

逐个分析删掉的响应头:

  • content-encoding:原始响应是 gzip/br 压缩的,明文 JSON 不删这个头会解压失败
  • content-length:原始响应体大小和构造的不一样,不改浏览器会截断
  • etag / content-md5:完整性校验头,原始哈希对不上
  • cache-control: no-store:确保每次都走网络请求,每次都经过 Hook

3.5 XHR 双通道保险

脚本不光 Hook 了 fetch,还对 XMLHttpRequest 做了一套完整的拦截——覆盖 responseText、response、status、statusText、getResponseHeader、getAllResponseHeaders。用 Object.getOwnPropertyDescriptor 先取原始 descriptor,再通过 Object.defineProperty 精确替换 getter,保留 descriptor 的所有属性。

3.6 状态指示器

页面右下角一个绿色小角标,确认脚本已激活。


四、横向迁移:这不是一个漏洞,是一类漏洞

漏洞本质

客户端信任边界缺失:服务端将支付流的决策权——"这个用户该走哪条支付通道"——交给了一个客户端可以任意篡改的 API 响应。

这不是孤例。回顾一系列支付漏洞,它们共享同一个底层模式:

漏洞被篡改的对象信任边界裂缝
焚决(本例)checkout_capabilities 响应前端信任 API 返回值决定支付通道
GPT Plus offerToken 注入Google Play Billing 内存中的 offerToken服务端不校验 token 与账号资格的绑定关系
GPT iOS 收据复用Base64 App Store 收据服务端不验证收据与提交者账号的对应关系
Cloudflare Pro 竞态并发请求的状态窗口权限发放与支付确认之间存在可利用的时序差
RevenueCat 回调劫持fetch_token 跨账号提交第三方回调接口缺乏调用方鉴权

通用攻击模型

四步攻击框架:

  1. 定位决策接口:checkout_capabilities, payment_methods, available_plans, feature_flags, entitlements
  2. 注入篡改数据:油猴脚本(Web)、MITM(移动端)、Frida Hook(Native)
  3. 选择确认延迟最长的路径:SEPA > 银行转账 > PayPal > 信用卡
  4. 利用"前端校验 + 后端真空"

这个框架可以复用到任何订阅制平台,只要目标满足:

  • 支付方式的选择依赖客户端可控状态
  • 存在至少一种异步确认的支付方式
  • "服务开通"发生在"支付确认"之前

五、防御视角

给服务端开发者

  1. 支付流决策必须服务端闭环。checkout_flow 的值应该在后端根据用户地区、账号状态、风控信号独立计算,前端只是一个渲染器——它不应该有能力选择自己走哪条支付通道。
  2. 异步支付必须延迟开通。SEPA、银行转账这类异步支付方式,在银行返回扣款成功确认之前,不应该开通任何付费功能。
  3. API 响应签名。对关键 API 响应做 HMAC 签名(密钥存服务端),前端消费前验签。大幅提高了攻击门槛。
  4. IBAN 实时银行验证。接入 SWIFT gpi 或各国央行的 IBAN 验证服务(如德国 Bundesbank 的 IBAN 验证接口),在提交时做实时的账户存在性校验。

给安全研究者

任何一个返回了影响支付逻辑数据的 API 端点,且这个数据可以被前端篡改,都是潜在的攻击入口。高价值信号接口:

  • checkout_capabilities / payment_methods / available_plans
  • region / locale / country_detection
  • eligible_promotions / offers / discounts
  • feature_flags / entitlements / subscription_tiers

六、实战翻车现场:"验证失败并清除旧会话"

在实际操作中,你大概率会遇到两条红色报错同时弹出来:

  1. "未提交付款:发生了意外错误。请检查表单后重试。"
  2. "验证失败并清除旧会话"

这是会话状态失效的问题。

五种常见的会话失效原因

① SK Cookie 过期(最常见)

Claude.ai 使用 __Secure-next-auth.session-token 维持登录态。自动化场景下尤其常见:任务在队列里排了一段时间,等轮到你的时候 cookie 已经不新鲜了。

② Session 被踢(多端互踢)

同一个账号如果在多个地方同时登录,后登录的会话会使前一个失效。

③ 代理 IP 切换(Session-IP 绑定)

Anthropic 很可能在 session 上绑定了创建时的 IP 或 IP 段。

④ Stripe PaymentIntent 过期

如果从打开支付页面到实际提交间隔太长,Intent 过期了。

⑤ Checkout Session 并发冲突

同一个 Organization 如果同时发起了多个订阅操作。

应对策略

从防御者角度看,这个报错机制本身就是 Anthropic 的一层防线——在处理支付之前先验会话,避免在认证不确定的情况下创建订阅。可以进一步:

  1. Session 绑定 + 支付确认二次绑定
  2. 支付提交时的实时 Session 刷新
  3. 异常模式检测

七、写在最后

焚决这个东西,技术上说穿了就是一个客户端状态注入工具。它不攻击 Claude 的认证系统,不碰 Anthropic 的服务器,攻击目标是前端与后端之间那条看不见的信任边界。

它的精妙不在于代码多复杂——而在于它精准地捏住了两个系统设计缺陷的交叉点:

  • Anthropic 的前端支付流由一个可篡改的 API 响应驱动
  • SEPA 直接借记在清算完成前不验证账户真实性

工具可以被修复,模式值得被记住。

SIGNATURE
错过了不该错过的人,那就是遗憾!
0 0 0 举报
复制成功