前言
最近圈子里流传着一个叫「焚决」的东西,名字取得很玄,搞得好像什么了不得的秘术。拿到手一看——是一段不到 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,原理很简单:
- 把 IBAN 的国家代码和校验码移到末尾
- 把字母转换为数字(A=10, B=11, ..., Z=35)
- 对整个大整数做 MOD 97 运算
- 结果等于 1 就合法
这个算法能检出格式错误——打错了某一位、漏了一位、国家代码不对。
这个算法检不出一切跟真实世界有关的东西——账户是否存在、余额是否充足、持有人是不是你。
randomiban.com 生成的 IBAN 就是利用了这一点:它按照 MOD 97-10 算法逆向构造,生成的 IBAN 格式 100% 合法,能通过任何基于此算法的前端和后端校验。至于这个账号在不在现实中存在——那是 3 天后银行清算时才会发现的事情。
为什么偏偏是德国 IBAN?
- 格式最简单:DE 开头的 IBAN 固定 22 位,没有额外的国家级校验叠加
- 通过率最高:德国 IBAN 在 Stripe 等主流支付网关的接受度最好,不会触发额外的地区风控
- 银行代码空间大: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 跨账号提交 | 第三方回调接口缺乏调用方鉴权 |
通用攻击模型
四步攻击框架:
- 定位决策接口:checkout_capabilities, payment_methods, available_plans, feature_flags, entitlements
- 注入篡改数据:油猴脚本(Web)、MITM(移动端)、Frida Hook(Native)
- 选择确认延迟最长的路径:SEPA > 银行转账 > PayPal > 信用卡
- 利用"前端校验 + 后端真空"
这个框架可以复用到任何订阅制平台,只要目标满足:
- 支付方式的选择依赖客户端可控状态
- 存在至少一种异步确认的支付方式
- "服务开通"发生在"支付确认"之前
五、防御视角
给服务端开发者
- 支付流决策必须服务端闭环。checkout_flow 的值应该在后端根据用户地区、账号状态、风控信号独立计算,前端只是一个渲染器——它不应该有能力选择自己走哪条支付通道。
- 异步支付必须延迟开通。SEPA、银行转账这类异步支付方式,在银行返回扣款成功确认之前,不应该开通任何付费功能。
- API 响应签名。对关键 API 响应做 HMAC 签名(密钥存服务端),前端消费前验签。大幅提高了攻击门槛。
- 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
六、实战翻车现场:"验证失败并清除旧会话"
在实际操作中,你大概率会遇到两条红色报错同时弹出来:
- "未提交付款:发生了意外错误。请检查表单后重试。"
- "验证失败并清除旧会话"
这是会话状态失效的问题。
五种常见的会话失效原因
① 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 的一层防线——在处理支付之前先验会话,避免在认证不确定的情况下创建订阅。可以进一步:
- Session 绑定 + 支付确认二次绑定
- 支付提交时的实时 Session 刷新
- 异常模式检测
七、写在最后
焚决这个东西,技术上说穿了就是一个客户端状态注入工具。它不攻击 Claude 的认证系统,不碰 Anthropic 的服务器,攻击目标是前端与后端之间那条看不见的信任边界。
它的精妙不在于代码多复杂——而在于它精准地捏住了两个系统设计缺陷的交叉点:
- Anthropic 的前端支付流由一个可篡改的 API 响应驱动
- SEPA 直接借记在清算完成前不验证账户真实性
工具可以被修复,模式值得被记住。
