机器人把钱主动送上门,1500 万没了
机器人把钱主动送上门1500 万没了核心要点横跨 Ethereum 和 BNB Chain 的 3 起安全事件总损失约 $18.3M。攻击类型包括 MEV 机器人合约授权管理不当、ZK 公共输入绑定缺陷以及代币参数错误配置导致的储备金操纵。本期重点 jaredFromSubway 事件揭示了一种与传统 DeFi approval 漏洞方向相反的攻击模式。传统场景中攻击者利用可信 DeFi 合约的漏洞转走用户 approve 给合约的资产本次攻击中MEV 机器人为了套利主动将自身资产 approve 给不可信的第三方合约攻击者积累这些对外授权后一次性提取 ~$15M。过去一周2026/06/15 – 2026/06/21我们观察到 3 起值得关注的安全事件总损失约 $18.3M。下表汇总了本周事件后续小节将对重点事件进行详细分析。Aztec该协议在三天内第二次被攻击这次通过 escape hatch 电路实施凸显了 ZK proof binding 问题的反复出现。jaredFromSubwayMEV 机器人合约对不可信代币合约授权后未验证消耗、未撤销残余额度攻击者得以积累并提取 ~$15M。本期看点jaredFromSubway*与传统 approval 漏洞不同——传统场景中攻击者利用可信 DeFi 合约的缺陷转走用户 approve 给合约的资产——本次攻击方向相反MEV 机器人为了套利主动将自身资产 approve 给不可信的第三方代币合约。攻击者构建了伪造交易环境本质上是蜜罐伪造交易池发出真实的 Swap 和 Sync 事件而伪造代币从未消耗已授予的额度不断积累这些对外授权后一次性收割报告总损失约 $15M。2026 年 6 月 20 日Ethereum 上 MEV 机器人运营者 jaredFromSubway 损失约 $15M [1]。根据链上分析根本原因是机器人合约的授权管理不当机器人向不可信的 wrapper 合约授权后这些合约从未消耗授权额度攻击者不断积累这些未消耗的授权最终在单笔交易中提取了机器人的真实余额。背景jaredFromSubway 是 Ethereum 上知名的 MEV 机器人运营者专注于三明治攻击和链上套利。受害合约0x1f2f…f387是其运营钱包之一持有大量 WETH、USDC 和 USDT 营运资金。此类 MEV 机器人需要动态与链上出现的任意新代币和交易池交互。它们监控 mempool、模拟交易并自动授权代币交互以捕获套利机会。这一运营模式依赖于一个假设执行 swap 时代币合约会通过调用 transferFrom 消耗已授予的额度。漏洞分析根本原因是 MEV 机器人合约对不可信合约的授权管理不当。机器人通过 Uniswap 池和 router 执行多种套利路径。大多数交互中机器人通过 transfer 直接向池推送代币此时机器人本身是 msg.sender不需要授权。但与 wrapper 类型代币合约的交互采用拉取模式机器人调用 wrapper.wrapTo()在该调用内部wrapper 合约调用 realToken.transferFrom(bot, wrapper, amount) 拉取机器人的真实代币。由于 transferFrom 时的 msg.sender 是 wrapper 合约而非机器人本身因此需要事先 approve真实代币.approve(tokenContract, amount) — 向 wrapper 合约授予真实代币的额度tokenContract.wrapTo() → 多跳 swap() → tokenContract.unwrap() — wrap 真实代币经池路由再 unwrap 回真实代币机器人假设 wrapTo() 会通过 transferFrom 消耗授权这是合规 wrapper 合约的标准行为。然而机器人从未验证操作后授权是否实际被消耗也未撤销残余额度。若 wrapTo() 不调用 transferFrom全部授权额度在操作后仍然存续成为持续的攻击面——持有该授权的任何合约日后可调用 transferFrom 转移机器人的真实资产。攻击分析根据链上还原攻击者构建了三层伪造交易环境来利用上述漏洞伪造 wrapper 代币每个伪造代币使用真实代币的 name 但在 symbol 前加 f 前缀如 name 为 USD Coin、symbol 为 fUSDC 对应 USDC。实现了 wrapTo() 和 unwrap() 模拟合法 wrapper以及仅限攻击者调用的 withdraw() 函数通过 transferFrom 提取未消耗的授权额度。伪造交易池攻击者通过自部署的工厂合约创建了约 44 个 Uniswap V2 风格的交易池将伪造代币相互配对形成 swap 路由。调用 swap() 时池发出真实的 Sync 和 Swap 事件与合法交易无法区分。攻击者构造的利润unwrap() 执行时伪造代币通过 transfer 向机器人发送少量真实代币。机器人获得了真实利润但这是攻击者刻意构造的而非来自市场套利。攻击者通过外部合约的按区块 getStatus() 开关控制这些组件。getStatus() 在与激活交易处于同一区块时返回 1激活交易设置 _getStatus block.number否则返回 0。当 getStatus() 0 时wrapTo() 正常调用 transferFrom 消耗授权。当 getStatus() 1 时wrapTo() 跳过 transferFrom——授权不被消耗——而 unwrap() 仍向机器人返还攻击者构造的代币。攻击者很可能通过 builder 贿赂将激活交易与机器人交易放入同一区块以此控制何时积累授权。攻击分三阶段进行第一阶段部署攻击基础设施Step 1攻击者在区块 25354424 至 25354519 期间部署了基础设施包括伪造代币工厂合约0x81f2…0091、通过自部署工厂创建的 ~44 个伪造 Uniswap V2 池、为池注入初始代币余额使 swap() 调用可执行以及向收割合约0xb84d…df52发送 0.01 ETH 用于 gas 和 builder 贿赂。Step 2攻击者通过 CREATE2 批量生产伪造 wrapper 代币每个代币模仿真实代币使用真实 name 但在 symbol 前加 f 前缀并携带仅限攻击者调用的 withdraw() 函数。CREATE2 提供确定性地址使收割合约可以遍历调用。第二阶段建立信任并积累授权Step 3初始信任在最早的交易中如区块 25354425 的 0x542d…362b伪造代币尚未部署 getStatus() 开关——wrapTo() 直接调用 transferFrom 消耗授权。机器人正常执行 approve、wrap、swap、unwrap 并获利。这使伪造代币建立了可盈利的交易机会形象。Step 4持续信任在后续交易中如 0x085e…37e51getStatus() 开关已部署但返回 0与激活交易不在同一区块。wrapTo() 仍调用 transferFrom 消耗授权。机器人继续获利并持续交互。Step 5授权积累从区块 25360519 的 0x8560…1915 开始攻击者通过 builder 贿赂将激活交易与机器人交易放入同一区块使 getStatus() 返回 1。此模式下 wrapTo() 跳过 transferFrom——授权不被消耗——但 unwrap() 仍通过 transfer 向机器人发送少量真实代币。机器人看到盈利操作保持授权不撤销。在约 600 个区块~13 笔交易中机器人对 WETH、USDC 和 USDT 重复此模式三种真实资产的未消耗授权不断积累。第三阶段收割Step 6攻击者在收割交易 0x2be870…cf3e65 中对所有伪造代币调用 withdraw()利用未消耗的授权调用 transferFrom 将机器人的真实余额转移给攻击者。交易包含 0.01 ETH 的 builder 贿赂以确保被打包。仅从受害合约就收割了 1,474.58 WETH 2,870,573 USDC 2,035,760 USDT~$7.5M。已确认的攻击交易造成约 $7.5M 损失总损失约 $15M 来自 jaredFromSubway 的声明 [1]。结论本次事件的根本原因是 MEV 机器人合约对不可信合约的授权管理不当。与传统 approval 漏洞——攻击者利用可信 DeFi 合约的缺陷转走用户 approve 给合约的资产——不同本次攻击方向相反机器人为了套利主动将自身资产 approve 给不可信的第三方合约攻击者积累这些对外的、未消耗的授权后一次性收割。为降低类似风险与不可信代币合约交互的机器人合约应在每次操作后验证授权是否被消耗并撤销任何残余额度。成功交易后授权未被消耗是恶意代币行为的强信号。本期其它事件Aztec2026 年 6 月 18 日Aztec escape hatch ZK 电路中缺失的相等约束使攻击者从 Ethereum 上的旧版 RollupProcessor 合约提取了约 $2.2M1,158 ETH、150K DAI 和 ~0.47 renBTC[2][3]。这是三天内 Aztec 第二次被攻击第一次收录于上期报告针对升级后的 RollupProcessorV3同样源于 ZK 电路 public input binding 缺陷。背景Aztec 的旧版 RollupProcessor 包含 escapeHatch 函数这是一个安全机制当 rollup 运营方停止处理时允许任何人提交单笔交易 proof。与需要授权 provider 的常规 processRollup 不同该 escape hatch 按区块号周期性开放任何人均可调用function escapeHatch(该 escape hatch 使用专用的 ZK 电路escape_hatch_circuit处理 join-split 交易从 Merkle tree 中消费输入 note 并创建输出 note。电路必须验证输入 note 存在于当前 data tree 中使用 old_data_root 进行 Merkle 成员证明然后将相同的根暴露为 public input 供 L1 合约与链上状态比对。漏洞分析漏洞位于 escape hatch 电路escape_hatch_circuit.cpp。old_data_root 值被创建为两个独立的 witness二者之间没有 equality constraint。第一个 witnessline 33传入 join-split 电路组件用于 Merkle 成员证明以验证输入 note 存在于 data tree 中join_split_inputs inputs {第二个 witnessline 50独立创建并暴露为 public inputline 88即 Solidity 合约提取并与链上 data root 比对的值auto old_data_root field_ct(witness_ct(composer, tx.js_tx.old_data_root)); // line 50: second witness在 ZK 电路中每次 witness_ct 调用创建一个独立变量。line 33 和 line 50 之间没有显式 assert_equalprover 可以为这两个 witness 赋予不同的值。在 Solidity 侧validateMerkleRoots 仅使用 line 50 的 public input 检查 require(oldDataRoot dataRoot)对 line 33 使用的值不可见。同样的 unbinding 模式也存在于 input_owner 和 output_owner这些值在 line 38-39 被 witness传入 join_split_circuit_component 用于所有权验证又在 line 111-112 作为独立的 public witness 暴露。不过我们未发现该问题的实际利用路径。攻击分析该 escape hatch 电路已在 aztec-connect 代码库中被删除 [4]但已部署的 verifier 合约仍保留 EscapeHatchVk 验证密钥使用该漏洞电路生成的 proof 仍可通过链上验证。攻击发生时该合约已沉寂 142 天escape hatch 窗口处于开放状态。攻击者地址在攻击前 14 小时才通过 Union Chain 创建 [2]攻击由以下三个核心步骤构成Step 1攻击者构造了一棵包含自有 note任意面额的假 Merkle tree。这些 note 不存在于真实的链上 data tree存储所有有效 note 的 Merkle tree中。Step 2攻击者利用上述未绑定的 witness 生成 escape hatch proof。Line 33 的 witness用于 join-split 组件内的 Merkle 成员验证设为假 Merkle root成员证明通过因为伪造的 note 确实存在于假树中所有权验证通过因为攻击者持有签名密钥。Line 50 的 witness暴露为 Solidity 检查的 public input设为真实链上 data rootSolidity 侧 require(oldDataRoot dataRoot) 检查通过因为该值与合约存储的根一致。Step 3电路和 Solidity 两侧检查均被满足proof 验证成功。合约将该 escape hatch 交易视为合法并释放资金。攻击者针对不同资产通过三笔交易0x9e1d6a…6b03ca、0xab306c…59c2b5、0x5c196c…4705c3重复上述流程分别提取了 1,158 ETH、150K DAI 和 ~0.47 renBTC合计约 $2.2M。结论根本原因是 escape hatch 电路中 old_data_root 的两个 witness 缺少 equality constraint。一个 witness 用于 join-split 组件内的私有 note 成员验证另一个暴露为 Solidity 检查的 public input。缺少约束绑定攻击者在假 Merkle tree 上证明了伪造 note 的所有权而 L1 合约看到的是有效的链上根。值得注意的是从源码中删除漏洞电路并未使已部署的 verifier 合约失效——旧版 RollupProcessor 上的 escapeHatch 函数在其区块号窗口开放时仍可被调用。为降低类似风险当同一逻辑值在 ZK 电路中多处出现时所有实例必须被显式约束为相等——对同一值的独立 witness_ct 调用即为 binding 缺口。电路审计应系统性验证每个 public input 是否绑定到其所代表的电路内部值。