背景
2026 年 6 月 18 日,以太坊 Layer2 隐私协议 Aztec Connect 的 RollupProcessor 合约遭遇攻击。攻击者利用其 Escape Hatch(逃生舱)机制在 Solidity 层缺少独立的资金归属与提款额度校验这一信任边界缺陷,以及相关电路约束缺失,提交了 TurboVerifier 接受的 escape hatch proof,直接从合约余额中提走 1,158 ETH(约 206 万美元),另通过同一机制盗取 150,000 DAI 与 0.47 renBTC,合计损失约 222 万美元。
攻击概览

攻击交易:

漏洞根因
escapeHatch() 的信任边界缺失
RollupProcessor 的公开入口函数 escapeHatch() 仅检查 escape hatch 状态是否开启,随后直接进入 processRollupProof(),不执行任何调用者权限校验:

相比之下,普通 rollup 处理路径 processRollup() 会校验 rollupProviders[provider] 授权并验证 provider signature。而本攻击中调用的是 escapeHatch(),传入的 signatures = 0x、viewingKeys = 0x,完全绕过了 provider 授权流程。
verifyProofAndUpdateState() 对 Verifier 的盲目信任
进入 processRollupProof() 后,合约调用 TurboVerifier.verify(proofData, 0):

TurboVerifier 本身行为正确——它完成了 Plonk proof 的数学验证。真正的问题在于 RollupProcessor 将数学验证成功等同于业务合法性,未在 Solidity 层再做一层独立校验。
processDepositsAndWithdrawals() 无条件执行提款
Verifier 返回成功后的核心执行逻辑:

最终资金转出函数:

zkSNARK 电路缺失等式约束门(根本原因)
在 Aztec join-split 证明电路中,old_data_root 被分为两条独立路径使用:
内部见证 A:传入 join-split 子电路,用于验证私有笔记的 Merkle 成员资格
公共输入 B:作为公共输入暴露给 Solidity 层,供 validateMerkleRoots() 与链上 dataRoot 比对
电路中缺失约束 A == B,导致两者可以独立赋值:
A 可以是攻击者构造的伪造 Merkle 树根(树中含任意金额笔记)
B 可以是真实链上 dataRoot(使 Solidity 检查通过)
由于约束系统允许 A ≠ B,整个 zkSNARK proof 仍然有效。
攻击流程
准备阶段:伪造 Merkle 树和 proof
攻击者在本地构建一棵伪造的 data Merkle 树,树中插入一条私有笔记:
价值:1158 ETH(合约当时持有约 1158.7598 ETH)
所有者:攻击者持有对应私钥
攻击者构造 zkSNARK proof:
内部见证 A(old_data_root) = 伪造树根
公共输入 B(old_data_root) = 链上实际 dataRoot = 0x184bea7d9493cd9a5efb6b679d04066a8c92a34ac8ec150e9635133c6010977b
第一阶段:构造攻击交易
攻击者 EOA 0x6952d9246e9aFE8B887B2877225163436F78E97F 直接向 RollupProcessor(0x737901bea3eeb88459df9ef1be8ff3ae1b42a2ba)发起顶层调用:
函数签名:escapeHatch(bytes,bytes,bytes)
msg.value = 0
signatures = 0x
viewingKeys = 0x
攻击者构造的 proofData 中包含关键 public inputs:

第二阶段:绕过权限检查
escapeHatch() 仅检查 getEscapeHatchStatus() 返回 true 后,直接调用 processRollupProof()。该路径不执行普通 processRollup() 中的 rollupProviders[provider] 授权检查,也不验证 provider signature。调用者身份与提款接收者之间无任何绑定关系。
第三阶段:Verifier 数学验证
RollupProcessor 对 TurboVerifier(0x48cb7ba00d087541dc8e2b3738f80fdd1fee8ce8)发起 STATICCALL:

因为 rollup_size = 0,TurboVerifier 通过 VerificationKeys.getKeyById(0) 选择 EscapeHatchVk(vk.num_inputs = 26)进行 Plonk proof 数学验证。Verifier 只验证 proof 与 public inputs 的数学一致性,不读取 RollupProcessor 状态,不检查余额归属。
本交易中 verifier 调用成功返回,说明攻击者提交的 escape hatch proof 在数学上正确。
第四阶段:执行提款
Verifier 返回成功后,RollupProcessor 更新 rollup 状态:

随后进入 processDepositsAndWithdrawals(),在 escape hatch 模式下 rollupSize == 0 仍按 1 笔 inner tx 处理。解析 proofData 后发现:

直接调用 withdraw(1158 ETH, 攻击者地址, 0),通过 receiverAddress.call{value: 1158 ETH}("") 将资金转出。
第五阶段:同模式攻击 DAI 与 renBTC
攻击者在同一时期使用完全相同的漏洞路径,分别构造了针对 ERC20 资产(assetId ≠ 0)的 escape hatch proof,将 150,000 DAI 和 0.47 renBTC 从 RollupProcessor 中盗取。withdraw() 函数中 assetId ≠ 0 分支执行 ERC20 的 transfer() 完成转账。
三笔攻击的净收益:

资金追踪
通过慢雾 MistTrack 反洗钱追踪系统对攻击者 EOA 0x6952d9246e9aFE8B887B2877225163436F78E97F 进行分析:
资金来源:攻击者创建地址使用的初始 Gas 来自 0x963737c550e70ffe4d59464542a28604edb2ef9a(unionchain.ai 实体地址)。该地址首次活跃于 2026 年 6 月 18 日 02:21:11 UTC,恰在攻击行动启动时刻,属于专为本次攻击创建的一次性地址。
资金去向:被盗资金当前分布如下:
802 ETH 代币仍留存于攻击者主地址 0x6952d9246e9aFE8B887B2877225163436F78E97F, 150,000 DAI 和 0.47 renBTC 也仍未转移
300 ETH 已转移至 0x15930a0fef3421f48c6553b5691682cc1b22edb3(MistTrack 标记为 Aztec Exploiter 关联恶意地址)
约 56 ETH 已转移至另一恶意地址(同样标记为 Aztec Exploiter 关联)
0x33d6a0d9bc210e823e043d604179cd844eb467df AML 风险评分 100/100(Severe),同样涉及 Aztec Exploiter 事件
总结
本次攻击的核心教训是:zkSNARK 电路的"数学正确"≠"业务安全"。验证器只能证明"你知道某个满足约束的见证",如果约束本身是错的(或缺失的),证明系统就成了合法的伪造机器。慢雾安全团队建议项目方对所有零知识证明约束路径进行专项审计,确保电路的安全。
本文由 SlowMist 威胁情报团队结合 MistEye 威胁情报系统、MistTrack 追踪平台及 SlowMist Agent AI 驱动分析编写,有任何问题欢迎咨询反馈。

