Wormhole 跨链桥被盗:凭空铸出 3.25 亿美元
2022 年初,Solana 生态里最值钱的一行代码,藏在 Wormhole 跨链桥的签名校验里。2 月 2 日,它无声地失效了:攻击者让桥相信一笔从未发生的存款已经到账,凭空铸出 12 万枚 wETH——时值约 3.25 亿美元。本页用 15 张图讲完全程:桥的信任模型是怎么搭的、到底哪一步校验被绕过、攻击如何在代码里逐行复现,以及那笔让所有用户全身而退的兜底资金。
10 分钟 • 15 个图解步骤 • 签名校验剖析、链上证据与 Foundry 复现
编委会史料考证与链上复盘
本篇归档基于公开密码学文献、历史白皮书与链上交易数据进行独立交叉核实,遵循学术严谨性标准。
Wormhole 跨链桥被盗是怎么回事?
2022 年 2 月 2 日,攻击者在 Wormhole 的 Solana 合约上伪造了一份额存款的守护者签名批准,凭空铸出 12 万枚 wETH(约 3.25 亿美元)。Wormhole 是连接以太坊和 Solana 的跨链桥:真 ETH 锁在以太坊一侧,守护者中的 13 位(共 19 位)签署存款证明后,Solana 侧按 1:1 铸出 wETH。问题出在签名校验上——代码对照的是一份调用方可伪造的名单(经由一个已废弃的 Solana 系统函数),攻击者据此凭空捏造了存款证明,随后把其中 93,750 枚 wETH 跨回以太坊,从 Wormhole 金库里取走了真 ETH。约 24 小时内,Wormhole 背后的 Jump Trading 注入 12 万枚 ETH 重新做实全部 wETH,用户一分未损——这成了 DeFi 史上最大的一次自掏腰包救助。2023 年,经英国法院令,被盗 ETH 的大部分也从黑客手中被追回。
核心要点
- Wormhole 是连接以太坊和 Solana 的跨链桥:一侧锁定真 ETH,另一侧按 1:1 铸出 wETH,随时可兑回。到 2022 年初,桥后面锁着超过 10 万枚以太坊(约 2.5 亿美元),而整个 Solana DeFi 都把 wETH 当成真以太坊在用。
- 它的全部安全模型就是一次校验:19 个链下守护者(Guardian)为存款证明(VAA)签名,桥见到 13 个签名就铸币。正如本页复现视频所说:第 3 步的签名校验,就是这座桥全部的安全。
- 2022 年 2 月 2 日,校验无声失效:Solana 侧代码对照的是调用方可伪造的输入——经由一个已废弃的 Solana 系统函数。攻击者用仅抵押 0.1 ETH 的伪造账户骗过校验,凭空铸出 12 万枚 wETH,时值约 3.25 亿美元。
- 攻击者钱包 0x629e…796b 被 Etherscan 标注为「Wormhole Network: Exploiter」,初始资金来自 Tornado Cash;据 Elliptic 分析,其中 93,750 枚 wETH 被跨回以太坊,从 Wormhole 金库兑走真 ETH。失去抵押支撑的 wETH 引发连环清算恐慌,SOL 单日下跌约 13.5%。
- Wormhole 链上喊话开出 1,000 万美元白帽赏金,无人回应;约 24 小时内,Jump Trading 注入 12 万枚 ETH,把所有 wETH 重新做实为 1:1——DeFi 史上最大的一次自掏腰包救助,救回了所有用户,也留下了一个不舒服的问题:下一座桥,还有 Jump 吗?
- 2023 年 2 月,英国高等法院一纸命令允许 Oasis 协议修改自家合约,Jump 顺势反制了黑客的杠杆仓位,追回约 12 万枚被盗 ETH(因行情上涨时值约 1.4 亿美元)。而代码层面的修复只有一行:校验签名时,对照合约自己存储的守护者名单,永远不是调用方递进来的那份。
锁着 Solana 全部以太坊的那座桥
在钱被偷走之前,先弄清 Wormhole 承诺了什么——以及整个 Solana 经济为什么都压在一次签名校验上。
- 1
Solana 的以太坊生命线
Wormhole 是加密世界两大阵营之间的管道:把真 ETH 存进它在以太坊的合约,桥就在 Solana 上按 1:1 铸给你 wETH;把 wETH 烧掉,ETH 原路奉还。到 2022 年初,这个承诺让 Wormhole 成了 Solana 最重要的跨链桥:桥后锁着超过 10 万枚以太坊,约合 2.5 亿美元;Solana DeFi 的借贷市场和流动性池,全都拿 wETH 当真金白银来计价。每一笔用 wETH 做抵押的贷款,本质上都抵押在同一条承诺上:除非桥真正验证过一笔存款,否则一枚 wETH 也不会被铸出来。

整个产品就四条——出事的正是最后一条。 - 2
19 个见证人,13 个签名,一条铁律
另一条链怎么知道存款真的发生了?它信任守护者(Guardian)——19 个链下见证节点盯着每一笔以太坊存款,并签署一份证明消息,称为 VAA(Verified Action Approval)。把携带 19 个签名中任意 13 个的 VAA 递给 Solana 侧的桥,它就照单铸币,不再多问。视频里的示意图用一句话画出了全部信条:第 3 步的签名校验,就是这座桥全部的安全。没有签名就不铸币;签名凑够,无条件放行。后面的一切——盗案、修复、这一整页——都源自桥每次都必须答对的一个问题:谁来认定什么才算真签名?

一张图一条警告:第 3 步的校验就是全部安全。
失效的那道校验
密码学本身没有失守。是桥问错了问题——还用攻击者递来的数据回答了它。
- 3
漏洞:它校验的对象就是错的
安全的做法只有一个:把每个签名对照自己那份固定的真守护者名单逐一核验——那份名单任何调用者都碰不到。而 Wormhole 的 Solana 程序信任了一个调用者可以操控的输入。现实中的漏洞藏在一个已废弃的 Solana 内置函数里:桥使用的 load_instruction_at 会读取另一个账户的指令数据,却不检查这个账户到底是谁的。Solana 在 Wormhole 部署前刚刚上线了带校验的替代版本,桥却带着旧函数上了生产。于是攻击者自己搭了一个冒充系统账户的账户,用它给校验程序喂进一份伪造的签名集,而桥以为自己在读 Solana 官方数据,照单全收。校验通过,12 万枚 wETH 铸出,背后没有任何抵押。

四条要点一个习惯:调用方递来什么,就信什么。 - 4
藏在校验代码里的那道陷阱
视频的复现把漏洞拆成了几行看得懂的代码。守护者把提款要素——付给谁、付多少、一个随机数——哈希成摘要(digest),再用私钥签出 65 字节的签名;桥调用 recover(摘要, 签名),得到签出这个签名的地址。关键在于:recover 永远不会失败,它总会返回某个合法地址。所以真正的问题只有一个——这个地址是不是守护者?而有漏洞的代码是对照 claimedGuardians 回答这个问题的,那是一份由函数调用者随手递进来的名单。截图标出的正是那一行:require(_in(claimedGuardians, signer))——调用方的名单。签名是调用方给的,评判签名的名单也是调用方给的。

第 33 行注释说得明明白白:名单来自调用方,且从未对照真名单核验。
链上盗案全程
2022 年 2 月 2 日:一笔本不可能存在的铸币,一场搬空金库的兑付,以及 Solana 亲眼看清自己抵押品成色的一天。
- 5
被 Etherscan 盖章的「Wormhole Network: Exploiter」
2022 年 2 月 2 日,攻击者把把戏用在了真金白银上:一份伪造的存款证明——签名出自攻击者三小时前建好的账户,桥却把它当成了 Solana 系统账户,而这个账户的全部抵押是 0.1 ETH——合约照单铸出 12 万枚 wETH,按当时币价约 3.25 亿美元。如今 Etherscan 在红色警告横幅下把这个地址(0x629e…796b)直接标注为「Wormhole Network: Exploiter」,并显示钱包的初始资金来自 Tornado Cash。以太坊那边,一笔存款都没有发生过。桥只是被说服了它发生过——因为定义「什么算证据」的,是攻击者,不是守护者。

白纸黑字,就写在地址页上:红色横幅直接点名。 - 6
整场骗局,一张 Recap 卡讲完
假的欠条只值换得到多少真钱,而攻击者兑现得毫不犹豫。据链上分析机构 Elliptic,铸出的 12 万枚 wETH 中有 93,750 枚分三笔跨回以太坊,直接从 Wormhole 金库兑走真以太坊;其余大部分——约合 3.6 万枚——被换成了 USDC 和 SOL。伤害立刻向外辐射:躺在 Solana 借贷市场里的所有 wETH,一夜之间变成了对「不存在的以太坊」的欠条——Paradigm 研究员 Georgios Konstantopoulos 形容为「针对 Wormhole ETH 的无抵押贷款的多米诺效应被严重低估了」——SOL 单日跌去约 13.5%。截图里这张 Recap 卡把骗局压缩成三行:靠签名铸币,信调用方的名单,凭空铸出 12 万枚。

三行讲完的盗案——第四行才是当年没被认真读的那条。
在 Foundry 里复现 3.25 亿美元的漏洞
想真正看懂这次攻击,只有亲眼看它跑一遍。一个极简桥、三幕剧,一台打印出整场犯罪的终端。
- 7
一座极简桥,三幕剧
想真正看懂这个漏洞,就得看它跑起来。视频用 Foundry——以太坊开发的标准工具链——重建了一个极简版的桥:一个「见到守护者签名就铸币」的小合约,然后把攻击排成三幕。第一幕,诚实情形:真守护者为真实存款签名,铸币正常。第二幕,攻击:攻击者用自己的密钥签名,自称守护者,凭空铸出一笔巨款。第三幕,修复:对照真正的守护者名单,攻击当场死亡。同样的三幕,2022 年 2 月在主网上真实上演过一遍——只不过站在测试网 ether 位置上的,是 3.25 亿美元的真钱。

接下来五张图的剧本:诚实、攻击、修复。 - 8
开跑之前,先读测试
动手之前,视频先读了测试文件——因为整个故事早就写在注释和断言里。第一幕用真守护者名单部署桥,让三位守护者中的两位为一个合法的 10 枚代币存款签名,并断言用户真金白银收到这 10 枚。第二幕的注释一口气说穿了攻击:签名对照的是调用方的名单——攻击者把自己的密钥当「守护者」递进去,凭空铸出 12 万枚 wETH;没有存款,签名本身也没有任何问题——它们完全合法,只是不出自真正的守护者。第三幕把同一批伪造签名指向修复后的桥,期待一个 revert:『not a guardian』。

三幕剧,先写成注释,再跑上链。 - 9
第一幕:诚实铸币,绿灯通过
先证明没人作弊时桥是好的。测试构造一笔真实的 10 枚 wETH 提款,哈希成摘要,用真守护者的密钥签名,连同真守护者名单一起传入。桥逐一核对签名、清点法定数量,精确铸出 10 枚。终端打出结果:[PASS] test_1_honest——『user minted (honest): 10.000……』——一项通过,零失败。这盏绿灯正是下一张图刺眼的原因:攻击里,密码学的一切都没变。同一条曲线,同一个 recover,同一座桥。变的只有那份名单。

绿灯,一切如设计。记住这个输出——攻击的结果会和它几乎一样。 - 10
第二幕:凭空铸出 12 万枚 wETH
攻击上演——注意它改动了多少:几乎为零。同一座桥,同一个摘要,同一个函数。唯一的区别:签名出自攻击者自己的两把密钥,而传入的守护者名单里装着同样这两个地址。所有校验照样全部通过,因为每个签名在密码学上都完全合法——recover 返回的就是攻击者的地址,而它们恰好匹配攻击者的名单。终端打出点睛之笔:[PASS] test_2_attack——『attacker MINTED (no deposit): 120000.000……』——套件全绿,12 万枚 wETH 凭空出现。这就是那个 3.25 亿美元的漏洞,几毫秒内复现完毕:桥根本无法分辨真守护者和调用者编造的守护者,因为它从来没问过。

[PASS]。问题就在这——伪造的铸币,通过了这世上所有存在的校验。
第三幕:修复、兜底与清算
一行代码杀死攻击。然后是无人回应的 1,000 万美元悬赏、Jump 写下的 DeFi 史上最大支票——以及两年后账本的清账。
- 11
第三幕:一个词的修复
修复小到能塞进一条注释里,截图把它并排放好了。修复前(有漏洞):receiveAndMint 接收 claimedGuardians 参数——调用方递来的名单——只要求签名者在这份名单里。修复后:参数被整个删掉,函数改为核对 _in(guardians, signer),这里的 guardians 是合约部署时一次性写入、任何调用者都无法更改的名单。同样的签名,同样的 recover,同样的法定人数——唯一的区别是,签名所对照的名单归谁所有。正如视频所说:任何时候,一个合约把信任校验建立在调用者可以提供的数据上,那都不叫安全,那叫建议。

注释写着「唯一的改动」——四行字,本可以省下 3.25 亿美元。 - 12
修复测试:期待「not a guardian」
修复配了一个专属测试,它把攻击原样重做一遍:同一个攻击者,同一套自造密钥,同样索要 12 万枚 wETH——全部指向修复后的桥。不同的是预期。测试用 vm.expectRevert(bytes("not a guardian")) 包住调用,告诉 Foundry 正确的结果是拒绝,然后断言攻击者的余额仍然是零。只要签名校验钉死在真实、存储于合约内部的守护者名单上,一模一样的攻击连一个 wei 都铸不出来。挪动一个词——claimedGuardians 变成 guardians——一整类漏洞就从「印钞机」变成了「一行报错」。

同一场攻击,改写成对失败的期待。 - 13
攻击重跑:回滚,余额归零
跑一遍,看同一场劫案当场毙命。片刻之前还凭空铸出巨款的同一批伪造签名,如今在修复后的桥上撞墙,终端把一切打了出来:[PASS] test_3_fixed——『attacker minted (fixed): 0.000000000000000000』——一项通过,零失败。2022 年 2 月在主网上凭空变出 3.25 亿美元的攻击,被降格成一句回滚原因和一个零余额。整张截图就是全部防御:把信任名单钉死在合约内部,永远不接受调用方递来的那份,并且记住——出自错误签名的完美合法签名,仍然是攻击。

同一个攻击者,同一批签名——换来 0.000 的余额和一个绿勾。 - 14
1,000 万美元悬赏,和一张抹平窟窿的支票
代码坏了,结局却没坏——因为有人决定掏钱。Wormhole 先试了怀柔:在链上向攻击者公开喊话,开出 1,000 万美元白帽赏金,只求 wETH 物归原主。无人回应。约 24 小时内,窟窿还是被填上了:孵化了 Wormhole 的做市巨头 Jump Trading 注入 12 万枚 ETH,让每一枚 wETH 重新回到 1:1 全额抵押——DeFi 史上最大的一次自掏腰包救助。Bankless 在当周的节目录制里,一边为用户毫发无损叫好,一边担心开了坏先例:这种结局不该被指望,因为不是每座桥背后都站着一个 Jump。截图里这张教训卡是同一个故事的代码版:对照正确的名单做校验——否则,替错误名单买单的会是比你更有钱的人。

复现得出的教训卡——Wormhole 用最贵的方式学会了它,账单由 Jump 买单。 - 15
七周之后:Ronin,镜像的另一面
片尾卡写着「下一场盗案,继续拆」——下一场早就排上日程了。2022 年 3 月 23 日,Wormhole 之后七周,攻击者从 Ronin 桥搬走 6.15 亿美元,创下当时 DeFi 最大盗案纪录。两桩案子互为镜像:Wormhole 死于代码缺陷——没有任何人的密钥被偷,只是签名被对照了错误的名单;Ronin 死于社会工程——代码没有问题,攻击者靠「交朋友」拿下 9 把验证者密钥中的 5 把,剩下的交给阈值机制。两次都是同一处伤口——桥的信任假设,从不同的侧面碎掉。Wormhole 之后的时代带来了强制审计、巨额赏金、提款限额和重新设计的证明系统,而复现得出的那条铁律仍在筛选幸存者:把信任名单钉死,永远别让调用方告诉你谁可信。

「下一场盗案。」六周后,它如约而至,署名 Ronin。
常见问题
Wormhole 跨链桥被盗是怎么回事?
攻击者利用的是什么漏洞?
是谁黑掉了 Wormhole?
Wormhole 的钱追回来了吗?
Jump Trading 为什么替 Wormhole 兜底?
Wormhole 之后,跨链桥还安全吗?
继续看这段历史
Ronin 桥被盗:6.15 亿美元的密钥镜像
Wormhole 七周之后,Ronin 以相反的方式丢了更多:代码无懈可击,攻击者却「聊」来了 9 把验证者密钥中的 5 把。
Bitfinex 被盗案:2016 年那次被还清的劫案
另一个「用户最终拿回全款」的故事:12 万枚 BTC 被搬走,内部纾困、平台币,以及一场持续多年的追回长跑。
Mt. Gox 崩盘全时间线
托管方灾难的鼻祖:85 万枚 BTC 蒸发、十年漫长的等待——那是 Wormhole 用户被一纸兜底拯救掉的命运。
2018 加密寒冬:比特币从 2 万美元跌去 84%
交易所事故定义了一个长冬;Wormhole、Ronin 这样的桥盗案拉开了下一个。加密寒冬背后的同一条规律。
加密史上最大骗局逐案拆解
Wormhole 是盗案不是骗局——把它放进 OneCoin、BitConnect、FTX 的全时代骗案名册,看清两者的分界线。
浏览全部图文攻略
区块链历史系列的每个故事,都有一步步的图文攻略版本。
加密货币监管史
跨链桥恰好落在规则书没画到的空白地带——看各国政府追赶加密货币的十三年完整编年。
参考资料
延伸多媒体文献参考
本专题时序与历史图示交叉参考了 0xUnstuck 出品的文献视频 “The $325M Wormhole Hack: What Went Wrong and How to Prevent It”,谨向原研究记录者致谢。
本图解指南仅供区块链技术史学术研讨与公众科普使用,不构成任何财务、证券、投资或法律建议。文中所载历史价格、黑客事件与安全漏洞分析均属于公开历史记录。加密货币市场具有极高风险,请审慎研判。