区块链

交易结束了 授权却留在 JaredfromSubway 的收割现场

这座反向蜜罐用真实可兑现的小额利润诱使 JaredfromSubway 主动把 WETH、USDC 与 USDT 的持久支出权交给陌生 wrapper,再用只在特定区块生效的切换跳过预期的 transferFrom() ;BlockSec 识别的一笔收割交易由此从单份运营合约转出约 750 万美元资产,暴露出自动交易系统必须补上的完整权限生命周期。

手绘市场观察者沿着被布置好的资金池走进真实代币权限账本,一封收割交易将三路资产带向分散的资金去向。
文章导航

研究依据公开记录支撑的总判断很清楚:攻击者搭起一座能够完成交换、发出熟悉事件并支付真实利润的人工市场,机器人在这座市场里留下了交易结束后仍可使用的真实代币 allowance,攻击者随后集中调用这些权限。本文所称“反向蜜罐”,指的正是这种诱使自动系统主动交出持久提款权的场所。

来源Chainalysis / BlockSec / Ethereum / SOSEC 来源复核

证据分成四层:Chainalysis 与 BlockSec 的事件观察、可独立核查的链上字段、标准与固定提交源码解释的 allowance 语义,以及建立在这些记录上的防守方案。Chainalysis 的 66 个仿冒代币合约与至少 750 万美元活动损失下限、BlockSec 的约 44 个池与单笔约 750 万美元还原、以及运营方约 1500 万美元总损失说法各自描述不同对象;全文保留这些对象和证据等级,不做合并估算。

1 一座人工市场换走了一份真实权限

五类参与者和一个状态变量足以看懂全案。攻击者控制工厂和逐区块开关;仿冒 wrapper 请求真实代币授权;仿冒池提供交换路线与事件;JaredfromSubway 的运营合约充当 owner 并签署交易;WETH、USDC、USDT 合约保存 owner 到 spender 的 allowance。池子与 wrapper 负责让路线看起来值得执行,真实代币账本负责把权限一直保存到后来的收割。

Chainalysis 的 6 月 26 日复盘把活动窗口放在 2026 年 6 月 20 日至 21 日:一名身份未知的攻击者部署 66 个仿冒代币合约,模仿 WETH、USDC、USDT 等资产,并把它们配进欺诈性流动性池。机器人在多笔交易中授予支出权限;部分权限在操作中未被消耗,事后也未撤销。Chainalysis 据此给出至少 750 万美元的活动损失下限,并记录稳定币换成 ETH、资金分散与 Tornado Cash 路径。

BlockSec 的技术还原识别了运营合约 0x1f2F10D1C40777AE1Da742455c65828FF36Df387、仿冒代币工厂 0x81F248Ff583d3f8592Ea0354A7b8DBe66de40091、约 44 个 Uniswap v2 风格池和一条依赖当前区块的 getStatus() 分支。wrapper 复用真实代币名称,在展示符号前加 f,还能从攻击者储备向机器人返还少量真实代币。交易结果因此同时具备可执行路线、正常事件和可计价利润三种可信外观。

1.1 代码与处置图锁定公开可核验的控制点

手绘机械观察者面对仿冒代币容器、池子、攻击者控制的工厂和一条格外诱人的发光路线,真实代币权限位于路线尽头。
图 1|攻击者、wrapper、池、机器人 owner 与真实代币账本承担不同角色;贯穿五者的唯一持久状态是 owner 交给 spender 的 allowance。

1.2 一张证据账本限定每项结论能支持的动作

事实对象来源与证据等级可直接采取的动作
66 个仿冒代币合约、至少 750 万美元活动损失下限Chainalysis 活动观察与资金追踪用作资产与合约清单种子;完整性需要逐地址、逐交易重建
约 44 个池、逐区块分支、区块 25,360,519 的积累模式锚点BlockSec 链上技术还原按工厂、代码家族、激活调用与残留 allowance 扩展排查
区块 25,360,696 的收割交易与三种代币单位净额以太坊交易、回执、日志和 BlockSec 汇总复算余额变化、调用路径、资产净额和直接接收方
运营方约 1500 万美元总损失说法BlockSec 转述 JaredfromSubway用于扩大初始事件范围;总钱包与交易集合仍需独立建立
ERC-20 allowance 的持久语义EIP-20 与 commit 固定的 OpenZeppelin 参考实现设计状态不变量;具体代币行为仍以历史链上代码和状态为准

这张账本把活动观察、链上字段、参考标准和运营方说法放回各自的位置。后文只在证据等级会改变处置时再次说明边界,调查主线则沿权限如何创建、保留、使用和关闭推进。

2 前几笔交易先教会机器人相信这条路线

BlockSec 把区块 25,354,425 的交易 0x542d8d7ad206672bf484bbad9635ec5f92a429d527a561ae898c2da09742362b作为早期信任样本。机器人先在真实代币合约上批准 wrapper,再调用 wrapTo();wrapper 执行 transferFrom(bot, wrapper, amount),有限 allowance 随支出下降。路线穿过攻击者创建的池,unwrap() 又向机器人送回真实代币。执行、事件、权限消耗和账面利润在这笔交易里保持一致。

交易 0x085ec14424f9b2f290ea7bd2f60fc74f3dc3d8996d70ad7bf384bcae52c37e51展示了下一阶段。外部状态机制已经部署,所在区块的 getStatus() 仍返回未激活值,wrapper 继续正常拉取。仿冒池同时发出真实的 SyncSwap 日志,攻击者储备提供的小额真实代币形成可兑现利润。自动系统看到的是连续成功;场所的部署者、资金来源与代码控制权则始终集中在攻击者一侧。

交易系统在这里需要分开处理五种信号:池日志证明 emitter 执行并写出日志;余额增长证明 owner 收到资产;调用追踪证明 wrapper 是否拉取真实代币;固定区块的 allowance 证明权限结果;工厂、运行时代码和控制状态证明交易对象身份。任何单一信号都只回答自身问题,策略的业务回执需要把五项放在同一个 route ID 下。

三幅手绘画面依次展示授权被拉取并消耗、两个池子发出事件涟漪,以及隐藏储备向机器人送回少量真实代币。
图 2|早期交易真实消耗权限、真实发出事件、也真实返还资产;这些成功为后来的同形路线积累了机器信任。

2.1 激活区块唯一改变的是必需拉取

积累阶段从 BlockSec 引用的区块 25,360,519、交易 0x85609286d68bd47065772c21fd9542c4343348ff8c7c2e6d63d0692be5781915获得公开锚点。激活交易把当前区块写进外部状态;同一区块内,wrapper 读取到激活值并跳过 transferFrom()unwrap() 继续发送攻击者出资的真实代币,顶层交易顺利完成,机器人仍看到正利润。价值结果沿用旧形状,权限结果则从“已消耗”变成“完整保留”。

BlockSec 评估,攻击者很可能通过 builder bribe 把激活交易与机器人交易放入同一区块。事件处理可以直接依赖更窄的确定性事实:实际 trace 缺少必需拉取,交易后 allowance 高于策略后置条件,已经构成完整告警。

两扇手绘区块窗口对比未激活开关消耗权限,以及激活开关绕过拉取、却仍返回同一滴小额利润的过程。
图 3|两条路径拥有相同入口和相似利润;激活区块少了一次必需调用,完整 allowance 因此留在真实代币账本。

负执行证据需要预先定义才能稳定检测。策略在签名前记录 spender、获准金额、必需 transferFrom() 的 owner、recipient 与 amount,以及允许的交易后额度;结算器把实际调用树和固定区块状态与这份约定比较。缺少必需调用、额度计算失败或状态读取超时都让该 owner-spender 路线暂停。手续费代币、部分成交和获准复用必须在策略版本里写明公式、舍入、上限与到期时间。

3 真实代币账本继续承认交易留下的 spender

EIP-20把 allowance 定义成 owner 允许 spender 提取的剩余额度;approve(spender, value) 写入权限,transferFrom(owner, recipient, amount) 在授权范围内移动资产。标准要求成功的 approve() 发出 Approval 事件,却没有要求每次支出都再发一条同名事件。授权事件适合发现授予动作,当前能力仍要从明确区块读取 allowance(owner, spender)

OpenZeppelin Contracts commit 60a102a65b3402aa899376d0da3819e09053f961提供了可读参考路径:approve()transferFrom() 位于 ERC20.sol 120–147 行,后者先调用 _spendAllowance(),再移动资产;_approve()_spendAllowance() 位于 273–305 行,有限额度随支出递减,最大 uint256 值按无限授权惯例保留。

固定源码解释常见状态变化,案件事实仍由具体代币在相关区块上的代码、调用与存储决定。代币可能返回 false、不返回数据、收取转账费、暂停、维护拒绝名单或通过代理更换实现。公开来源也未给出本案每一项 allowance 的精确值。已确认机制只需要足够大的非零额度持续存在;有限额度同样可以覆盖 owner 的全部当前余额。

手绘 owner 金库、真实代币账本和 spender 拉杆由一根持久权限绳相连;交易卡片关闭后,三瓶资产仍可沿稍后的拉取路径流出。
图 4|交易生命周期存在于应用层,真实代币只保存 owner、spender 与额度;应用没有完成清理时,这份能力继续有效。

3.1 交易后额度是结算结果的一部分

最小可用主键是 (chain, real token, owner, spender)。策略再为这条元组附上 route ID、批准版本、额度上限、预期支出、允许残留、过期规则与清理状态。对一次覆盖式有限授权,允许残留通常来自“本次写入值减去已追踪支出”;既有额度、再次 approve()、部分成交和自定义代币行为需要自己的计算分支。无法解释的差值进入失败状态。

当前暴露可用 min(balance(owner), allowance(owner, spender)) 作为常规代币的分诊上限,随后再用代币专属模拟核对手续费、rebase、暂停、hook 与转账限制。余额降到零时,名义权限仍可能等待下一笔入账;仪表盘应同时显示“现在可拉取”与“额度仍允许”两个数值,并在余额或实现变化时重新估算。

签名授权延续同一纪律。ERC-2612deadline限制 permit 提交时间;成功提交会写入 allowance,随后由支出或撤销改变。短 deadline 解决签名提交窗口,交易后的权限结果仍由状态读取确认。本案公开材料描述链上授权,未把 permit 列为事件机制。

4 一笔收割交易把权限换成了三路资产

2026 年 6 月 20 日 18:49:11 UTC,收割交易 0x2be870…cf3e65进入以太坊区块 25,360,696。发送方是 0x5aF38735B215b00aa7C9f93fEd7ee415CeCB36e1,顶层目标是收割合约 0xb84db016324e8F2BFdD8DD9c260338AEE0A8DF52。交易成功回执、调用树、代币日志与前后余额共同固定了单份运营合约的损失现场。

资产0x1f2F…f387 转出的公开净额保存方式
WETH1,474.58252300499497779218 位小数代币数量、合约地址、日志索引、调用帧与余额差
USDC2,870,573.127686 位小数代币数量与带时间戳的独立估值
USDT2,035,760.1558716 位小数代币数量与代币特有行为记录

BlockSec 报告相同数量,并把这笔已识别交易估值为约 750 万美元;Chainalysis 给出至少 750 万美元的活动级损失下限;BlockSec 另行转述 JaredfromSubway 约 1500 万美元总损失说法。代币单位数量、单笔估值、活动下限与运营方总额说法分别进入四个字段,既能安排事件范围,也保留各自的证明强度。

交易还包含从收割合约到 Etherscan 标记为 Eureka Builder 地址的 0.01 ETH 内部转账,位置为区块内第零笔。BlockSec 将其解释为确保打包的 builder bribe。案件记录分别保存原始转账、浏览器标签与分析用途,后续标签变化不会改写链上数值。

一封手绘封印交易穿过多份 wrapper 进入受影响金库,三路彩色真实资产汇入同一收集池,旁边另有一笔中性小额 builder 转账。
图 5|一笔外层交易协调多次提款;三路代币单位净额构成可复算损失,0.01 ETH 的用途判断保留 BlockSec 归属。

4.1 换币、分散与报告日期运行在三只时钟上

Chainalysis 称,攻击者在取得稳定币后数分钟内将其换成 ETH,并评估发行方冻结风险可能推动了这一步;随后数日,资金被拆分到其他钱包,部分路径进入 Tornado Cash。换币交易、后续分散和 mixer 归因属于三个独立记录层。每条直接资金边保存来源、目的、资产、数量、交易与区块;地址聚类和服务归因另带来源与置信度。

Chainalysis 在 6 月 26 日发文时记录“尚无资金追回”。该状态绑定报告日期。后续冻结、扣押、退还或新增损失需要新的带日期来源。稳定币换成 ETH 也改变了处置窗口:经授权的响应团队可以在证据保全同时联系发行方与交易所,提交对象分为直接接收方、已追踪中间方、服务存款点和待确认集群,并分别记录请求、回执与实际动作状态。

两瓶稳定币进入兑换磨坊并变成 ETH,既有 WETH 从旁路汇入,资金随后分散到多个钱包并淡入 mixer 雾气。
图 6|稳定币快速换成 ETH,既有 WETH 直接汇入下游,钱包分散与 mixer 路径随后展开;每个阶段保留自己的时间和证据等级。

资金路径说明第一笔资产流出以后干预选项会迅速收窄。更早的控制点位于签名器:候选合约的身份、权限请求、必需内部调用与交易后状态必须在同一份策略决定中闭合。

5 签名器同时审查合约身份与权限结果

合约身份从链 ID 与完整地址开始,并绑定候选区块的运行时代码哈希、创建交易、部署者或工厂、实现与管理员、资金来源和变更历史。Uniswap v2 风格的 SwapSync 事件只描述 emitter 的日志;策略还要确认 pair 是否来自获准工厂、两端代币是否来自版本化注册表。BlockSec 记录的 CREATE2 批量部署同样属于来源线索:EIP-1014 能预先确定地址,地址的实际信任仍来自 deployer、初始化代码、运行时代码与当前状态。

代理进一步要求按区块解析 implementation、beacon 与 admin。ERC-1967 给出了常见存储槽;代理地址与既有存储可以在实现更换后继续存在,implementation 或 beacon 则指向新的执行代码。已批准路线因此绑定实现与状态版本,代码、实现、工厂、资产、金额、目标区块或打包条件变化都会使旧决定过期。

手绘档案把链、代码、部署者、工厂、交易对与实现六项身份凭据,连到 owner、token、spender、额度和回执五个权限格。
图 7|身份档案回答“谁在请求”,权限档案回答“它还能拿走什么”;两份记录在签名、结算和异常暂停时使用同一个 route ID。

5.1 预检、授权、执行与对账形成前半个闭环

  1. 预检:固定链、候选区块、完整调用对象、运行时代码、工厂与实现、分支外部读取、流动性来源和打包条件;每项记录负责人、来源与失效条件。
  2. 授权:为每份真实代币指定 owner、spender、精确上限、route ID、获准持续时间和允许后置额度;陌生场所使用隔离 owner 与小额资本。
  3. 执行:实际 trace 必须出现策略列出的真实代币调用、参与方与金额;池日志、资产回流和顶层回执作为并列事实写入业务回执。
  4. 对账:在交易所在规范区块读取余额与 allowance,把价值结果和权限结果分别验收;身份变化、调用缺失、额度差异或读取超时立即暂停 owner-spender 组合。

这四步把本案利用的空隙压缩到一次结算里。真实利润仍可被系统接受,攻击者出资的利润会因来源集中而进入更低信任等级;交易成功仍进入账本,缺失的必需调用和超出预期的权限会把整条策略判为失败。速度来自预计算注册表和紧凑不变量,安全决定保留所有输入与版本。

6 撤销、验证和受控重启关闭后半个生命周期

发现残留 allowance 后,值班团队先暂停相关路线与签名策略,停止新授权和新资本进入。随即保存规范链头、最终确认区块、owner 余额、allowance、待处理交易、签名队列、运行时代码、代理槽和相关 trace。这份快照为撤销前后对比、范围扩展和后续复盘提供固定起点。

撤销使用策略控制的干净路径,直接调用真实代币设置目标额度。恶意支出迫近时,可同时把资产转移到由独立签名策略控制的新 owner;目的地址预先确认 gas、nonce、私有提交、代币暂停与发行方协调条件。范围排查沿 spender、运行时代码家族、实现、工厂、部署者、资助者和收割接收方扩展,并覆盖所有流动真实代币、待处理交易与近期失败路线。

手绘顺时针闭环连接身份预检、精确授权、交易、状态检查、置零回执、暂停闸门和受监控重启七站,旁边还有测试台与独立复核椅。
图 8|权限从预检进入授权,在执行后经过对账;异常路线沿暂停、置零、最终性验证和金丝雀重启回到入口。

6.1 每项撤销都有状态和完成证据

  1. 已计划:元组、目标额度、签名者、gas 来源、nonce 策略和资产转移优先级已经批准。
  2. 已提交:保存撤销交易哈希、提交渠道、替换关系和待处理期间的 owner 隔离状态。
  3. 已打包:回执成功,交易位于规范链;重组窗口内继续保持未完成状态。
  4. 已验证:达到选定最终性后,在固定区块读取 allowance 得到目标值,并复核 owner 余额与实现身份。
  5. 已重启:由与实施者分离的审批人确认代码、配置、开放权限、签名角色和回归结果;金丝雀 owner 在单笔与累计限额下运行预定区块数。

重启方案明确人工值守窗口和自动回退条件。合约身份变化、状态核对超时、提供商分歧或新残留额度都会重新关闭资本闸门。恢复目标同时覆盖恶意合约发现、工厂与代码策略、必需调用、allowance 后置条件、例外治理和历史状态访问,避免只关闭已经暴露的几条元组。

6.2 回归测试重放这次事故的关键变化

第一组测试提交一个储备充足、事件完整、模拟盈利但来自未知工厂的 pair;系统应拒绝自动授权,并让获准工厂对照路线继续运行。第二组在模拟与执行之间改变 wrapper 的外部状态;系统应让签名决定过期,或在结算时捕获缺失拉取与超额残留。第三组让支出更新 allowance 却不发额外 Approval 事件,验证对账器依赖状态读取。

清理测试分别覆盖置零交易丢弃、替换、revert 和被重组移出规范链,仪表盘在固定区块复读前始终显示未完成。向仍有沉睡权限的 owner 再次存入资产,应立即提高暴露估值;代理地址保持不变而实现升级,应同时使候选路线与旧例外过期;索引器重启、提供商切换和短时断网不得丢失暂停状态。自动重试只用于幂等读取,创建权限或移动资产仍需新的策略批准。

这起事件留下的工程结论非常具体:自动交易的完成条件包含价值和权限两份结果。报价值得执行、交易成功、利润到账,只完成了价值侧;必需调用出现、allowance 落入获准状态、异常权限被撤销并完成最终性复读,才关闭权限侧。两侧共享同一个 route ID、证据版本和负责人。

JaredfromSubway 遇到的市场先用正确行为建立信任,再用一个区块改变内部拉取。更安全的系统会在同一个区块读懂这项变化,并在下一笔资本进入以前关闭对应能力。交易结束时,权限也带着可重放证据结束。

研究记录

7证据、对象与来源

下面保留本文实际使用的标识、时间和原始材料,便于继续调查。

7.1研究对象

报告涉及的产品、行为者、技术、受影响对象和控制点。

被归因受影响合约0x1f2F10D1C40777AE1Da742455c65828FF36Df387

BlockSec 识别、Etherscan 标记为 JaredfromSubway MEV Bot 2 的合约

收割交易0x2be8704f5a59b69e0b71f64aefdb99eb0e8ae9fb3926147c581910d71bcf3e65

区块 25,360,696 中成功执行,包含公开 WETH、USDC 与 USDT 流动

收割合约0xb84db016324e8F2BFdD8DD9c260338AEE0A8DF52

已识别收割交易的顶层目标

收割发送方0x5aF38735B215b00aa7C9f93fEd7ee415CeCB36e1

已识别收割交易发送方

仿冒代币工厂0x81F248Ff583d3f8592Ea0354A7b8DBe66de40091

BlockSec 还原中识别的工厂地址

初始信任交易0x542d8d7ad206672bf484bbad9635ec5f92a429d527a561ae898c2da09742362b

BlockSec 引用的早期授权消耗路线

持续信任交易0x085ec14424f9b2f290ea7bd2f60fc74f3dc3d8996d70ad7bf384bcae52c37e51

BlockSec 引用的未激活开关路线

积累模式锚点0x85609286d68bd47065772c21fd9542c4343348ff8c7c2e6d63d0692be5781915

BlockSec 引用的区块 25,360,519 积累模式交易

权限模式ERC-20 approve / transferFrom

获准封装 spender 持有的真实代币持久 allowance

公开资金去向Tornado Cash

Chainalysis 报告的后续资金目的类别

7.2事件时间

  1. 早期授权消耗路线

    BlockSec 引用一笔早期交易,封装器拉走真实代币并消耗授权。

  2. 积累模式锚点

    BlockSec 将交易 0x856092…1915 作为预期拉取被跳过、权限仍然存在的锚点。

  3. 已识别收割交易

    区块 25,360,696 的成功交易,从被归因受影响合约转出三种公开真实资产净额。

  4. 转换与分散

    Chainalysis 报告稳定币迅速换成 ETH,随后拆分到其他钱包,部分路径进入 Tornado Cash。

  5. 技术报告发布

    BlockSec 于 6 月 25 日发布技术还原;Chainalysis 于 6 月 26 日发布活动与资金流分析。

7.3来源与材料

  1. Chainalysis:JaredfromSubway.eth 遭遇 750 万美元损失的三明治攻击复盘https://www.chainalysis.com/blog/sandwich-attack-jaredfromsubway-hack/
  2. BlockSec:jaredFromSubway、Aztec 等事件技术还原https://blocksec.com/blog/web3-security-jaredfromsubway-aztec-more
  3. Etherscan:已识别收割交易https://etherscan.io/tx/0x2be8704f5a59b69e0b71f64aefdb99eb0e8ae9fb3926147c581910d71bcf3e65
  4. Etherscan:被归因 JaredfromSubway 受影响合约https://etherscan.io/address/0x1f2F10D1C40777AE1Da742455c65828FF36Df387
  5. Etherscan:BlockSec 识别的仿冒代币工厂https://etherscan.io/address/0x81F248Ff583d3f8592Ea0354A7b8DBe66de40091
  6. Etherscan:收割合约https://etherscan.io/address/0xb84db016324e8F2BFdD8DD9c260338AEE0A8DF52
  7. BlockSec Phalcon:初始信任交易https://app.blocksec.com/phalcon/explorer/tx/eth/0x542d8d7ad206672bf484bbad9635ec5f92a429d527a561ae898c2da09742362b
  8. BlockSec Phalcon:持续信任交易https://app.blocksec.com/phalcon/explorer/tx/eth/0x085ec14424f9b2f290ea7bd2f60fc74f3dc3d8996d70ad7bf384bcae52c37e51
  9. BlockSec Phalcon:积累模式锚点https://app.blocksec.com/phalcon/explorer/tx/eth/0x85609286d68bd47065772c21fd9542c4343348ff8c7c2e6d63d0692be5781915
  10. ERC-20 代币标准https://eips.ethereum.org/EIPS/eip-20
  11. OpenZeppelin ERC20.sol 固定提交 60a102ahttps://github.com/OpenZeppelin/openzeppelin-contracts/blob/60a102a65b3402aa899376d0da3819e09053f961/contracts/token/ERC20/ERC20.sol
  12. OpenZeppelin SafeERC20.sol 固定提交 60a102ahttps://github.com/OpenZeppelin/openzeppelin-contracts/blob/60a102a65b3402aa899376d0da3819e09053f961/contracts/token/ERC20/utils/SafeERC20.sol
  13. Ethereum.org:JSON-RPC APIhttps://ethereum.org/developers/docs/apis/json-rpc/
  14. Go Ethereum:内置 EVM tracershttps://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers
  15. EIP-1014:CREATE2https://eips.ethereum.org/EIPS/eip-1014
  16. Uniswap v2 架构https://developers.uniswap.org/docs/protocols/v2/concepts/architecture
  17. Uniswap v2 池https://developers.uniswap.org/docs/protocols/v2/concepts/pools
  18. Etherscan:合约验证https://docs.etherscan.io/contract-verification/whats-contract-verification
  19. Solidity:合约元数据https://docs.soliditylang.org/en/latest/metadata.html
  20. ERC-1967 代理存储槽https://eips.ethereum.org/EIPS/eip-1967
  21. ERC-2612 签名授权https://eips.ethereum.org/EIPS/eip-2612
  22. EIP-712 类型化结构数据https://eips.ethereum.org/EIPS/eip-712