Wormhole 桥漏洞攻击:3.26亿美元剖析技术分析
2022年2月2日,Wormhole 代币桥失窃12万 wETH——当时约合3.26亿美元——这一事件成为 DeFi 历史上最大规模的智能合约攻击之一。此次攻击无需密钥被盗、社会工程攻击或对 Guardian 验证节点网络的多数控制。它只利用了一个要素:一个废弃的 Solana 程序 API,该 API 未能校验其读取的账户身份。
理解此漏洞对所有基于 Solana 的团队、依赖跨链消息传递的协议以及审计 Solana 程序的审计师都至关重要。根本原因是 Wormhole Solana 桥程序中的智能合约验证缺陷——并非密钥泄露,也非 Guardian 网络失效。Guardian 密钥从未被触碰。桥自身的代码赋予了攻击者不该拥有的授权。
Wormhole 攻击的主要技术漏洞是什么?
漏洞存在于 Wormhole Solana 桥程序 (bridge.so) 的 verify_signatures 指令中。该函数负责验证 SignatureSet 账户——包含 Guardian 节点的 Ed25519 或 Secp256k1 签名——是否代表真正的共识,方可授权可验证动作批准(VAA)。
漏洞概述:verify_signatures 调用了废弃的 solana_program::sysvar::instructions::load_instruction_at 函数,用以检查同一交易中先前是否调用了 Secp256k1 的预验证instruction。该废弃版本未验证传入的账户是否真的是 Solana 指令 sysvar (Sysvar1nstructions1111111111111111111111111),它读取的是位于指定索引的任何账户——包括完全由调用方控制的账户。
攻击者可传入一个伪造账户,模仿指令 sysvar 布局,预先填充包含攻击者控制的签名数据和攻击者选择的有效载荷。函数读取伪造数据,错误地认定 Secp256k1 调用“存在”(完全不同的上下文中),将 VAA 标记为 Guardian 批准,并返回成功。
拿到伪造的批准 VAA 后,攻击者调用 complete_wrapped,该函数信任桥程序的 VAA 批准,从而授权在 Solana 上铸造封装资产。结果是:铸造了12万个 wETH,但在 Ethereum 上没有锁定相应的 ETH。
修复措施——用 load_instruction_at_checked 代替 load_instruction_at,后者强制验证账户确实是指令 sysvar——已在被攻击前提交到 Wormhole 的 GitHub 仓库,但尚未部署到主网。
| 因素 | 描述 | 影响 |
|---|---|---|
废弃的 sysvar API |
使用无 _checked 变体的 load_instruction_at,未对传入账户进行所有者校验 |
攻击者可用伪造指令账户替代真正的指令 sysvar |
verify_signatures 绕过 |
函数接受攻击者伪造的 SignatureSet 作为 Guardian 共识的证明,未验证账户来源 |
无任何真实 Guardian 签名即获得铸造授权 |
缺失账户所有者校验 |
SignatureSet 账户未验证为指令 sysvar 程序所有(Sysvar1nstructions11...) |
这一缺失校验构成整个漏洞根基 |
封装资产铸造权限 |
Solana 桥程序对封装代币拥有无限制的铸造权限;授权只需 VAA 被标记为批准 | 120,000 wETH 在 Solana 被无源铸造;攻击时约值3.26亿美元 |
Wormhole 攻击与 Ronin 等其他桥攻击有何不同?
Ronin 桥攻击(2022年3月,约6.25亿美元)经常与 Wormhole 一同提出作为桥安全失败的典型案例,但它们的失效模式根本不同。
Wormhole 是 Solana 桥程序中的智能合约验证漏洞。Guardian 网络——由19个节点组成的门限签名方案——从未被攻破。攻击者通过绕过 verify_signatures 没有校验它读取的账户身份的缺陷,完全绕过了 Guardian。Guardian 密钥完好,网络正常运行,但这些都无关紧要:代码从未真正读取过 Guardian 共识。
Ronin 是运营密钥被盗。Sky Mavis 运营了 Ronin 九个验证节点中的五个。2021年11月,Axie DAO 临时将签名权限委托给 Sky Mavis 以应对交易负载,此权限过期后未被撤销。攻击者(被归为 Lazarus 组织)入侵了 Sky Mavis 内部系统,获得了四把验证节点的密钥。利用 Axie DAO 仍有效的委托权限,攻击者通过 Sky Mavis 的无 GAS RPC 节点获得第5个签名。拥有5个验证签名后,攻击者提交了合法的撤款消息给 Ronin 桥合约。这里未涉及智能合约漏洞,桥合约在收到5个真实的验证签名时,照常授权提款。
| Wormhole (2022年2月) | Ronin (2022年3月) | |
|---|---|---|
| 攻击向量 | Solana 程序账户验证绕过 | Sky Mavis 节点的社会工程与凭证泄露 |
| 利用方式 | 通过废弃 sysvar API 伪造 SignatureSet 账户 |
利用被盗验证密钥的合法签名的提款请求 |
| Guardian / 验证者网络 | 完好无损;攻击者被代码绕过 | 被攻破——攻击者持有 5/9 验证密钥 |
| 影响方式 | 在 Solana 非法铸造,无 Ethereum 上锁定的 ETH | 通过有效签名从 Ronin 桥合约直接提现 |
| 被盗金额 | 约3.26亿美元(120,000 wETH) | 约6.25亿美元(173,600 ETH + 2550万 USDC) |
| 安全启示 | Solana 程序中账户所有权校验不可或缺,门限签名仅在桥程序真实读取时有效 | 门限签名方案不能抵御多数密钥被盗,授权委托须强制撤销 |
Wormhole 攻击的执行步骤
-
账户伪造。 攻击者创建了一个 Solana 账户,模仿 Wormhole 桥程序指令数据中
SignatureSet位置的布局。该账户内含攻击者控制的有效 Ed25519/Secp256k1 签名,但针对攻击者自选的 VAA,有效载荷授权铸造120,000 wETH 到攻击者的 Solana 地址。 -
利用废弃 sysvar API 绕过。 攻击者提交调用 Wormhole 桥程序的交易,执行
verify_signatures。程序调用了废弃的、不进行校验的load_instruction_at,用于确认同一交易早先是否调用过 Secp256k1 指令。该函数不验证账户身份,直接读取指定索引账户。 -
伪造证明被接受。
verify_signatures遍历攻击者控制账户中的签名,验证其密码学有效(攻击者使用自己的密钥签名自己的有效载荷),将 VAA 标记为 Guardian 批准。Guardian 网络未被查询。 -
铸造授权。 攻击者使用已批准的 VAA 调用
complete_wrapped。桥程序信任 VAA,通过授权,在 Solana 上将12万个 wETH 铸造到攻击者账户。 -
跨链套现。 攻击者通过 Wormhole 正常跨链路径,将铸造的 wETH 桥回 Ethereum。因 wETH 存在攻击者 Solana 钱包,桥处理为普通转账。3.26亿美元的无抵押 wETH 到达以太坊,攻击者兑换为 ETH 和稳定币并退出。Wormhole 母公司 Jump Crypto 用自己的准备金补充了失窃的12万 ETH,保障桥用户利益。
针对 Solana 程序及跨链桥的关键技术教训
1. 传入的每个 AccountInfo 在 Solana 程序中均为攻击者可控,除非被明示验证。
这是一条基础安全规则。程序必须校验每个账户的 owner、key,以及适用时的 executable 和 is_signer 字段。废弃的 load_instruction_at 函数接受任何传入的账户。现代替代方案 load_instruction_at_checked 强制验证账户确实为指令 sysvar。Soken 的 Solana 审计方法论将非 _checked 变体的 sysvar API 使用判定为关键安全缺陷,无论上下文。
2. 签名验证与资产铸造的权限必须严格分离。
桥程序的铸造权限应被封装于验证模块之后,该模块从标准的、程序所有的状态中重新派生证明——而非依赖调用者提供的账户。当 verify_signatures 接受攻击者控制的账户作为共识的“事实来源”,签名验证与铸造授权的分离失效。铸造指令应从程序自行书写并验证真实 Guardian 输入的状态派生授权。
3. VAA 重放保护必须链上强制执行。
即使 SignatureSet 是真实有效的,complete_wrapped 也应拒绝已被消费过的 VAA 哈希。Wormhole 在后续版本中加入了重放保护,但漏洞版本缺失该保护,导致批准 VAA 可被重复利用。每次桥操作都应受制于一个原子更新的 consumed-VAA 映射(processed_vaas: mapping[bytes32, bool])。
4. 废弃 API 的使用属于关键审计发现。
Wormhole 漏洞基于已知的废弃情况。Solana SDK 在漏洞发生前就标注了 load_instruction_at 为废弃,并提供了 _checked 版本。未能及时从废弃API迁移,不是代码质量问题,而是安全关键差距,攻击者可主动通过阅读变更日志和比较未部署提交与已部署字节码查找此漏洞。Solana 审计必须检测 solana_program::sysvar::instructions::* 中的非 _checked 用法并判为拒绝项。这是 Ottersec、Halborn 和 Soken 自2022年以来的标准审计流程。
5. 未部署的安全修复是公开的攻击地图。
load_instruction_at_checked 的修复在漏洞发生前已被提交至 Wormhole 公开仓库。先进攻击者通过监控目标协议的安全相关提交(尤其是废弃API替换)即可单凭差异重构漏洞。对安全关键程序的部署流程,必须视“合并到主干”与“主网部署”之间的时间差为暴露期。应制定紧急部署流程,将该窗口压缩到数小时,而非数天。
6. 多签门限并不补偿账户验证失败。
Wormhole 的 Guardian 网络由19个节点组成门限签名方案,结构坚固,但对本次攻击无效。攻击绕过了整个共识层,因为 verify_signatures 读取了错误账户。门限签名能防密钥被盗,却不能防止永远不接触 Guardian 的代码漏洞。深度防御要求每道安全层被实际调用——绕过的控制即不存在控制。
这对桥审计与 Solana 程序安全的启示
Wormhole 攻击表明跨链桥安全是多层次问题。即使 Guardian 网络密码学完备、经济激励合理、协议设计良好——链上程序中一处废弃函数调用即可使一切失效。
对于 Solana 开发团队,账户验证教训具有普适性:视传入的每个参数为潜在敌意输入。必须在操作任何账户前,校验账户所有权、地址、判别符和数据长度。推荐使用 anchor 框架的 Account<T> 封装或等价措施,在类型层面强制这些校验,而非依赖手动断言。
对桥架构设计者而言,教训是权限分离及标准状态锚定。铸造权限应溯源至程序拥有、程序自身验证的状态,而非程序无验证地接受调用者提供的证明。
Soken 审计会把废弃 sysvar API 使用、缺失账户所有权校验和 VAA 重放保护缺陷视为关键安全事项。绕过多签门限通过操控验证函数读取账户的攻击方式,已成为我们 Solana 审计方法论中的一类命名攻击。如果您的协议涉及跨链消息或封装资产铸造,请联系我们 soken.dev/services-it.html,在部署前进行安全评估。