当前位置: 首页 > 科技观察

跨链桥:Web3黑客必争之地

时间:2023-03-18 19:39:54 科技观察

跨链桥,区块链的基础设施之一,实现的功能是让用户将自己的资产从一条链转移到另一条链,区别于连接的区块链的关键桥梁往往以中心化的方式实现。由于跨链桥本身往往存储着用户质押的巨额资产,因此是Web3黑客最热门的攻击目标之一。近年来,与跨链桥接相关的攻击和漏洞频频出现。本文将详述Harmony跨链桥被黑客攻击事件和Wormhole代理合约未初始化漏洞。Harmony跨链桥被黑客攻击事件2022年6月23日,Harmony链与以太坊之间的跨链桥发生恶意攻击。攻击者控制了跨链桥所有者的私钥后,直接利用管理员权限通过特权函数转移了大量桥持有的各种代币,导致价值约9700万美元的资产被盗Harmony链上的资产。跨链桥由多重签名钱包控制。其初衷是通过要求至少N个管理员进行操作来提高钱包的安全性和攻击门槛。在实际代码实现中,N=2。多重签名钱包实现为一个合约MultiSigWallet.sol,其中本身可以处理资产转移的函数executeTransaction()有一个confirmed(transactionId,msg.sender)校验,两个不同的owner需要调用submitTransaction和confirmTransaction依次运行,以通过操作权限验证,并作为多重签名钱包发起资产转移等操作。多重签名钱包地址:https://etherscan.io/address/0x715CdDa5e9Ad30A0cEd14940F9997EE611496De6external_call(txn.destination,txn.value,txn.data.length,txn.data))emitExecution(transactionId);else{emitExecutionFailure);(transactionId)txn.executed=false;}}}https://etherscan.io/address/0x715CdDa5e9Ad30A0cEd14940F9997EE611496De6#codeMultiSigWallet.sol因此,保证这些所有者私钥的机密性是Harmony跨链桥的安全瓶颈之一。从目前的证据来看,攻击者可能通过其他链下攻击方式控制了多重签名钱包中的两把所有者私钥,成为了桥的实际所有者。通过这些权限验证不再是问题。攻击者可以正常使用所有者权限管理资产,包括将所有资产转移到攻击者账户中。实际资产转移交易https://etherscan.io/tokentxns?a=0x0d043128146654c7683fbf30ac98d7b2285ded00总结在密码学基础上构建的区块链世界,没有什么比保证私钥本身的机密性更重要,私钥泄露是最无法解决和最有害的安全风险。跨链桥的开发者需要注意项目本身的中心化风险,并确保项目使用的私钥的安全。同时,每一位区块链的使用者也必须清醒地认识到:道路万千,私钥第一。虫洞代理合约未初始化漏洞2022年2月24日,匿名白帽黑客satya0x负责任地披露了一个虫洞主桥(以太坊端)的代理合约未初始化漏洞,一举获得Web3漏洞赏金平台immunefi。最高单个漏洞赏金:1000万美元。看到这个数字的读者朋友一定不禁要问:到底是什么逆天漏洞,才能收获这天价赏金?据长亭科技区块链安全研究人员分析,该漏洞是由于虫洞跨链项目以太坊端代理合约未初始化造成的。虫洞项目的所有以太币将被冻结在原地址,用户转入虫洞的资产也将被永久冻结,无法再提现。根据国家区块链漏洞数据库区块链漏洞分级规则中的智能合约漏洞分级规则v1.0,该漏洞将导致智能合约核心业务无法正常运行,导致他人大量资产被冻结,危害程度分为严重危害;同时,利用成本低,没有特殊的利用门槛,漏洞可以稳定触发,在利用难度上属于低难度。综合考虑这两个因素,将该漏洞归类为高危漏洞。现在漏洞已经修复了,下面我们来讨论一下这个虫洞漏洞的原理。漏洞合约地址:https://etherscan.io/address/0x736d2a394f7810c17b3c6fed017d5bc7d60c077d什么是虫洞虫洞跨链协议是连接solana和以太坊的桥梁。通过Wormhole,DeFi项目可以避开以太坊的拥堵和高手续费,享受Solana的低手续费和高吞吐量,快速的交易体验。——SolanaWormhole项目主页从Wormhole项目的介绍可以知道,Wormhole是一个连接两条互不兼容的公链:以太坊和Solana的协议。合约程序部署在以太坊和Solana上。运行在广场上的程序称为以太坊智能合约,运行在Solana上的程序称为Solana程序。运行在不同公链上的两个程序需要协同工作才能完成用户的资产跨链请求。虫洞项目以太坊智能合约中出现代理合约未初始化漏洞。什么是以太坊智能合约以太坊(Ethereum)是目前最知名的公链系统,其原生货币以太币是市值第二大的加密货币。以太坊智能合约是在以太坊EVM(以太坊虚拟机)上运行的程序。以太坊智能合约的主要功能是实现各种资产管理功能。刚接触智能合约这个术语的读者可以从概念上直接将智能合约理解为用Java编写的程序,运行在Java虚拟机(类比EVM)中。以太坊的一大特点是智能合约程序一旦部署在链上,其代码逻辑就无法再修改。也就是说,运行中的以太坊智能合约的代码逻辑无法更新。使用代理合约更新合约以太坊的智能合约在部署后无法更改,这使得开发人员在发现安全漏洞后更新智能合约代码成为一个难题。但是,更新智能合约的代码也不是完全不可能的。通过一个特殊的以太坊EVM调用:Delegatecall,以太坊程序可以间接更新智能合约的代码。以太坊智能合约使用耦合代码和存储的编程模型。换句话说,合约的代码逻辑和合约的数据存储存储在同一个地址。合约只能通过存储在其自身地址上的代码逻辑来访问。修改自己地址的数据存储。但情况并非总是如此。除了最常用和最常见的call调用,以太坊智能合约还有一种特殊的调用方式:Delegatecall,实现了智能合约代码和智能合约存储的分离,即使用别人的代码修改自己的datastore.合约之间的通用CALL,如下左图所示:A合约CALL调用B合约,B合约的程序逻辑运行在B合约地址的数据存储中,读取和修改的数据属于B地址数据。合约之间调用DELEGATECALL,如下右图所示:合约A调用DELEGATECALL调用合约B,运行的程序代码仍然是B的代码,但与CALL的根本区别是所有代码都在上下文中运行在A中,当使用DELEGATECALL时,B的代码修改了调用方A的数据存储状态,可见使用Delegatecall可以运行B的合约代码,修改A的合约存储。使用这个特性自然可以升级合约。此时合约A为代理合约(Proxy),合约B为执行合约(Implementation)。每次调用合约时,代理合约A都会使用Delegatecall将实际调用参数传递给合约A的数据存储中指定的实现。合约B,B的合约代码修改合约A的数据存储。当执行合约B的代码发现严重漏洞,需要升级,将合约A的数据存储中指定的执行合约由B改为新的B',从而实现合约的升级。原来的合约B虽然还在链上保存着,但是实际上已经被丢弃了,代理合约不会再把调用转发给B合约。B运行所需的数据原先存储在A中,新合约B'可以无缝继承和使用。以上就是实现智能合约代码升级的基本原理。目前,以太坊社区通过代理合约进行智能合约升级的主流标准有两种:透明代理模式(TPP)和通用可升级代理标准(UUPS)。透明代理模式(TPP)下,合约升级相关的逻辑会在代理合约A中实现,缺点是开销大,代理合约和执行合约中的同名函数无法实现叫。通用可升级代理标准(UUPS)中的代理合约A由EIP-1822标准统一。它没有实现任何逻辑,只是调用了目标合约的DELEGATECALL,更新的逻辑也在目标合约中实现。本文开头提到的Wormhole协议采用的是第二种UUPS模式。UUPS代理合约模式UUPS模式下,代理合约A不实现任何逻辑,所有合约均由开发者在实现合约B中实现。主要功能包括:合约初始化逻辑和合约更新逻辑。contractImplementation{//实际合约地址publicupgrade;//初始化逻辑:设置管理员账户functioninitialize()externalonlyonce{upgrader=msg.sender;}//更新逻辑functionupgradeToAndCall(addressnewImplementation)external{authorizeUpgrade();//身份验证setImplementation(newImplementation);//设置新执行合约的地址newImplementation.delegatecall('..');//调用新执行合约的初始化函数}}当实际合约B需要更新时,管理员提前部署更新后的合约B',以新合约B'的地址为参数调用upgradeToAndCall函数,该函数会将代理合约A中记录实现合约的存储由B修改为B',然后使用delegatecall调用新的合约B'的初始化函数initialize完成初始化。在初始化时,A合约存储中的管理员帐户将被更新。使用UUPS模式的风险我们重点关注代理合约A和执行合约B中负责认证的upgrade变量的作用:因为可更新智能合约将原本在一个合约中完成的逻辑拆分为两个,在一个合约中,代理合约A和实际的合约B,因此这两个合约在自己的存储中维护自己的升级程序存储变量。管理员调用upgradeToAndCall完成更新后,由于delegatecall的特性,代理合约A中的upgrader变量更新成功,但执行合约B'中的upgrader没有更新。B'的存储虽然不是UUPS模式下的实际存储,但B'不仅是代理合约A的执行合约,也是一个可以公开访问的普通合约。实际上,在使用UUPS模式更新合约时,升级器存储变量有两个独立的重要步骤:将A存储中的升级器更新为msg.sender(已在upgradeToAndCall中实现)更新B存储中的升级器对于msg.sender(管理员需要的额外步骤),管理员不仅需要通过调用原执行合约B中的upgradeToAndCall函数,通过新执行合约B'中的initialize函数更改A存储中的upgrader变量(第一步),但也需要在外部独立调用一次初始化函数来更改B'存储中的升级程序变量(第二步)。在没有第2步的情况下,相当于代理合约A被正确初始化,用户不能通过代理合约A进行任何恶意行为,但是B'没有做任何初始化,用户仍然可以直接调用B'的初始化函数initialize将B'存储中的升级器更新为自身,控制B'的升级行为调用自毁操作实现销毁B'合约的操作,使得执行A合约指向的合约B'消失了,代理人对合约A剩余的数据存储也将无用。Wormhole的实际漏洞Wormhole项目源码:https://github.com/certusone/wormholeWormhole负责更新和鉴权的具体逻辑比上面描述的思路稍微复杂一点。负责实现UUPS标准upgradeToAndCall函数实际名称为submitContractUpgrade,在认证时使用parseVM等自定义虚拟机相关操作:abstractcontractGovernance..{functionsubmitContractUpgrade(bytesmemory_vm)public{Structs.VM内存vm=parseVM(_vm);...setGovernanceActionConsumed(vm.hash);upgradeImplementation(upgrade.newContract);//设置新执行合约的地址}}wormhole/ethereum/contracts/Governance.sol在执行合约中,initialize负责初始化管理员变量:contractImplementationisGovernance{functioninitialize(...,bytes32governanceContract)。..{...setGovernanceContract(governanceContract);//setupgrader}}wormhole/ethereum/contracts/Implementation.solWormhole在最后一次调用submitContractUpgrade()时更新了区块高度13818843(2021年12月16日),此后实际合约B'始终处于未初始化状态。攻击者可以擅自调用实际合约B'的初始化函数initialize()获取B'合约管理员权限,然后利用获取的管理员权限恶意调用更新函数submitContractUpgrade()。update函数中的delegatecall允许执行攻击者指定的任何代码,其中危害最大的是执行selfdestruct使实际合约B'自毁,从而冻结Wormhole项目中的资产。Wormhole项目在14269474高度(2022年2月24日)调用初始化函数后修复了该问题。使用UUPS代理模式时,有几个关键步骤需要特别注意。在没有必要初始化的情况下,黑客可以通过初始化实现合约获取管理员权限,恶意调用更新函数破坏实现合约,导致资产被永久锁定。边注。你是不是也想尝试独立发现这个天价漏洞,悬赏千万?以太坊安全实践平台Ethernaut提供了该漏洞模式的实践环境,快来亲身体验吧!总结跨链桥由于自身的中心化特性,在去中心化的Web3世界中引入了中心化风险,其安全性很大程度上取决于跨链桥项目本身。因此,跨链桥的开发者对跨链桥的安全起着决定性的作用。