深入解析两种针对Optimistic rollups欺诈证明的攻击以及解决办法
在这篇文章中,我将介绍两类针对Optimistic rollups的攻击:时间旅行攻击(time travel attacks)和现实扭曲攻击(reality distortion attacks)。我将使用OVM 1(Optimism虚拟机1)来演示这些攻击,这是Optimism在2021年11月已经退役的一个过时的版本实现。
这篇文章的目的是强调研究防欺诈安全的意义,并为大家在接近防欺诈悬赏时提供一个思维框架。
等一下,等一下,医生,你是说你用DeLorean造了一台时间机器?
Optimistic rollups和欺诈证明
Optimistic rollups使用L1(又称 以太坊 主网)网络主要用于存储,在其他地方做昂贵的计算来扩展以太坊。我们假设序列器(sequencer)正确地执行了这些计算(因为它锁定了一个债券),因为如果它执行的计算结果不正确的话,那么锁仓的债券将被系统没收。
如果序列器提交了一个欺诈性的状态转换。那么这时候可以用一个L1欺诈证明合约来证明欺诈,撤销状态转换并删掉进行欺诈的序列器。如果提交的交易在应用于前一个状态时没有产生由序列器证明的新状态,那么状态转换将被认为是具有欺诈性的。
在欺诈证明成功后,链会滚回到欺诈前的状态,然后通过以相同的顺序重新播放(replay)相同的交易,从而使用正确的状态,因此它就会被向前滚动。
欺诈证明的实现是很棘手的。它们涉及到用EVM(以太坊虚拟机)本身的任意状态和输入来模拟EVM的执行,并且必须始终达到与L2执行相同的结果。它们不能有执行不同的极端情况。
Georgios Konstantopoulos在一篇伟大的文章中描述了OVM-1中Optimism的欺诈证明实现。我的帖子的技术部分假定对它有一定的了解。
动机:对区块链进行时间旅行攻击
在这一节中,我将解释恶意欺诈证明的含义,以及攻击者如何利用它通过精心策划的时间旅行攻击造成最大的损害并提取最大的价值。
通常情况下,交易是在L2上进行处理的,但当L2上的交易受到欺诈证明的挑战时,它们会在L1上作为证明的一部分进行重新处理。为了避免欺诈证明,这两个流程必须得到相同的结果。
时间旅行攻击的作用是创建交易,在正常的L2处理过程中以一种方式修改状态,而在欺诈证明过程中则以另一种方式修改。在交易再处理的过程中,一个追溯性状态的改变可能会对之后发生的一切产生连带影响。通过选择性的改变过去的状态,我们可以进一步改变现在和未来的状态。
OVM-1有多个漏洞,攻击者可以利用这些漏洞通过序列器植入一个看似合法的交易,然后在L1上证明其产生的状态是欺诈性的。由于欺诈证明者的服务没有尝试在L1上进行模拟实现,而是依赖于在L2上执行,所以它未能检测到这些交易是欺诈性的,这使情况变得更糟。因此,攻击者可以植入交易,等待长达7天的欺诈证明窗口并通过在L1上产生欺诈证明从而将链滚回到该点。
以下是Doc(人名)如何实现基本的 双花 攻击的过程:
-
在攻击之前,将他能获得的 ETH 尽可能多地发送到L2,发送到Account_1。
-
植入一个欺诈性交易,如下图所示。该交易在L2执行时将ETH转移到Account_2,但在欺诈证明过程中在L1模拟时还原(反之亦然,Account_1和Account_2可能被颠倒,这取决于使用下面的哪个漏洞——还原是在L2还是在L1模拟时发生)。
-
从拥有ETH的账户中提取ETH,通过快速桥(Fast Bridges)。在攻击期间,典型桥将没有用处,因为它受到7天的延迟,但Hop、Connext和Celer等桥愿意承担风险,在没有这个延迟的情况下向L1释放资金。此时,在攻击开始之前,Doc的资金在L1上已经是安全的(减去费用)。
-
使用Account_1(不再有资金的账户)发送另一笔交易,试图通过典型桥向L1发送相同的金额。由于缺乏资金,这笔交易此时应该在L2上恢复。
-
等待6天。
-
提交一个欺诈证明,将链上的资金回滚6天。在这一情况下,链的状态落后6天,用户无法通过序列器提交交易,因为它不是同步的,直到它被修复以提交与L1模拟相匹配的状态根(改变欺诈交易的结果)。如果序列器试图重新用旧的状态根将链向前滚,Doc就会不断地重新去除这个序列器,拿走它抵押的债券。
-
最终,序列器重新上线并重播链,Doc的第一次交易产生了由L1模拟决定的状态根。
-
此时,Doc的资金已经翻倍。从账户_1到账户_2的转账结果已经改变,所以随后的快速桥转账由于缺乏资金而被退回。Doc在L1上有他原来的资金,但是L2上的快速桥转账也被追溯到了,所以他在L2上也有资金。这个时候,快速桥的流动资金池已被耗尽。
-
当修复后重放该链时,第一笔交易结果的变化会产生连带效应,也会改变第二笔交易的结果。帐户_1现在有了资金,所以之前恢复的向典型桥的转账现在成功了。
-
一天后,7天的窗口期结束了,典型桥的资金在L1上被释放。Doc在L1上的ETH增加了一倍,他成功抢到了快速桥的资金。
但Doc很贪婪,他想获得超过2倍的利润,所以他决定继续进行奖励回合:
-
链上的状态回到了6天前,Doc针对目前的代币价格开启了主要的套利机会。就在桥提款之后,Doc挑选了一个高波动性、高交易量的代币。Doc没有发送上面的第二笔交易(将资金发送到典型桥的那笔交易),而是用账户_1在 Uniswap 上的交易来代替,试图以当前价格购买大量的代币。由于账户_1缺乏资金,该交易被退回。
-
在接下来的6天里,Doc监测代币价格。如果代币价格下跌,Doc什么都不做,再等一天,等待欺诈窗口期满,然后从头开始攻击。
-
如果代币价格在这6天内大幅上涨,Doc就会发出另一笔交易,试图以当前价格出售代币。这个交易会被退回,因为Doc实际上并没有代币。
-
Doc最后发送了欺诈证明,并等待序列器被修复,以及链被改变状态后进行的重放。
-
在重放过程中,第一笔交易的结果发生了变化,开始产生了连环效应。因为账户_1里有资金,第二笔交易(6天前买入代币)现在成功了。Doc现在有了代币,而Uniswap上的代币价格也因为这次交易而上涨。其他用户在这6天内进行的许多后续重放交易都被退回,因为他们的限额不再与Uniswap的代币价格相匹配。当他们的价格限制与Doc的回溯交易所产生的新价格相匹配时,最后的交易就会成功。代币最终达到了它的当前价格。然后Doc重新进行第3次交易,以当前价格出售Doc的代币。
-
Doc这次的获利表现可比2倍要好得多。他双花了他的ETH,但也用双花的资金追溯利用了6天的套利,使其抢跑(frontrun)于所有其他交易者。这些用户的任何进一步的交易可能也会失败,因为他们的重放交易不再与他们持有的资产匹相配。
-
如果Doc真的很贪婪,他可以创建任意复杂的攻击后交易序列攻击不同的DeFi协议。例如,他可以只在前3天利用套利机会,积累大量的ETH。然后他将利用这些ETH来操纵代币价格,在一些抵押协议中触发清算,并收集这些清算。Doc会成功地抢跑于其他人,因为清算发生在过去的3天里,而只有Doc在那里接受清算。
-
同样地,他通过有选择地买入6天前的期权来利用诸如Synthetix这样的期权协议。有了时间旅行攻击,DeFi中的可能性似乎无穷无尽。
-
最后,Doc并没有将收益发送到典型桥,而是在条件序列(condition sequence)的最后将所有新获得的ETH发送到L2的Tornado Cash,这样在网络修复后,这些地址就不会被桥列入黑名单。
-
当网络恢复运行后,Doc慢慢地从Tornado Cash中提取ETH并将其发送到L1。
整个像电影情节一样的攻击可能会失败,因为L1合约仍然可以集中升级,而升级可以修复防欺诈合约而不是序列器,确保在重放时状态保持不变。或者升级可以暂时禁用欺诈证明,并以相同的状态重放区块链。
然而,这不是社区应该依赖的升级方式。在某些时候,rollups将是去中心化的,回滚链来撤销攻击将变得不可行。我们需要欺诈证明足够可靠,永远不需要中心化回滚来拯救它。
在确定了我们应该关心的原因之后,让我们继续探讨实际的漏洞。
两类漏洞
在我的研究中,我专注于两类漏洞:
-
由匿名用户进行的时间旅行攻击
-
由一个恶意的序列器进行的现实扭曲攻击
在接下来的章节中,我将展示这两种类型的攻击。
由匿名用户进行的时间旅行攻击
如上所述,证明合法状态转换的欺诈行为可以实现链的恢复和时间旅行。这类攻击是最危险的,因为它可以由网络上的任何匿名用户进行。
最简单的一种:存储gas成本差异
OVM-1由一个l2geth(用于L2执行)和一组OVM合约(用于L1模拟)组成。两者差别很大的一个地方是状态管理器。例如,OVM_StateManager.sol以一种奇特的方式为L1实现了SSTORE/LOAD,这与L2的实现不同。因此,任何使用存储合约的gas行为都会在L1和L2之间有所不同。ovm_state_manager.go中实现的其他函数也是如此。
这可以通过直接访问gasleft()来利用,或者通过使用一个有gas限制的调用,只在其中一个上引起gas外的还原。
这是我第一次使用这个微不足道的合约进行欺诈证明的攻击:
由于储存gas1时消耗的gas,这个合约在L1和L2上发出的数字是不同的。这也导致了不同的状态根,因为不同的数字被储存在gas2中。
为了利用这一点,我需要一个欺诈验证器来模拟L1上的交易。不幸的是,repo中的欺诈验证器将状态根与l2geth报告的状态根相比较,因此不会触发L1模拟。我给它打了补丁,让它接受FORCE_BAD_ROOT环境变量,使它在指定的区块号中“看到”一个不同的状态根,并试图在L1上证明它。下面的所有演示中都使用了这个打了补丁的验证器。
实际的攻击行为:
而成功的欺诈证明输出可以在这里看到。底部的标记部分显示,它产生了一个不同的状态根,因此欺诈证明是成功的。
绕过白名单部署器:非合约攻击
当我研究OVM-1欺诈证明时,OVM已经作为Synthetix的特定链在主网上上线了(见下面的时间线),但方式有限。Optimism很聪明,它为Synthetix链和后来的主网软启动使用了多层防御。第一道防线是一个白名单部署器,它可以防止未经授权的用户部署合约。因此,上述琐碎的漏洞攻击在主网上是行不通的。我决定把研究重点放在破解OVM而不部署合约上,从而突破第一道防线。这意味着我需要找到可以通过正常(非创建)交易触发欺诈证明的系统合约。
最简单的一个是针对OVM的创新账户抽象(account abstraction)。OVM用ECDSA合约取代了EOA,模仿了EOA的行为。这是一个很好的账户抽象的实现,但它也引入了一个漏洞。
通常情况下,当一个EOA发送的交易少于最小gas时,该交易不会被挖出来,链的状态也不会受到影响。但有了账户抽象,所需的最低gas量要高得多,如果gas量在L1最低值和L2最低值之间,恢复就会发生在链上。任何有足够gas应用(~25000)但不足以完全执行ECDSA合约的交易(对于一个没有数据的简单交易来说,539745)在L1和L2上的表现是不同的。
在L2上,EVM将其作为账户级别的恢复来抓取,nonce没有增加,gas支付也没有转移。在L1上,费用转移和nonce增加成功,实际调用失败,因为那是用户指定的gas限制被欺诈证明模拟应用的地方。
因此,L1(欺诈证明)的状态被改变了,但L2上的交易却没有受到任何影响,所以L2的状态根仍然与前一批相同,尽管区块链在不断进步,这是不应该发生的事情。
这个漏洞很简单,就是发送一个空的交易,gas限制为25000,然后提交一个欺诈证明。
成功的欺诈证明输出可以在这里找到。
绕过gas保护:嵌套(nested)交易
2021年4月,我下载了最新的OVM版本(标签v0.2.0),看到非合约的、基于gas的漏洞停止工作。这让我很惊讶,因为在一个单轮(single round)欺诈证明系统中修复gas计算似乎几乎是不可能的。仔细观察发现,序列器开始忽略用户指定的gas限制,并将其硬编码为9000000,这消除了我之前实现的与gas相关的整类攻击。
为了修复我的漏洞,我需要它们在不依赖指定我自己gas限制的情况下工作。幸运的是,账户抽象再次帮助了我。
ECDSA合约通常由序列器调用,但没有什么能阻止它被其他合约调用,甚至在嵌套调用中被自己调用。只要内部调用包括这种EOA仿真的ECDSA签名,它就可以从该账户进行调用。这使得一些有趣的交易成为可能,比如一个多次增加nonce的交易,或者在同一个交易中增加多个账户的nonce。
顺便提一下,交易嵌套还可以在欺诈证明期间从二级EOA创建合约,而不经过ovmCREATE检查,从而允许创建的合约突破沙盒,而同样的创建会由于ovmCREATE检查在L2上的失败。这种攻击很复杂,所以我们在这篇文章中会坚持使用更简单的攻击。
我使用嵌套来修复上述ovm_exploit3.py,并精心制作了一个涉及多个账户的多层交易。只要有足够的层数,一个nonce在L1和L2上的表现就会不同。
成功的(尽管很长)欺诈证明输出可在这里获得。
我在匿名非合约类中又实现了几个漏洞,但希望上面的漏洞能充分说明单轮欺诈证明很难确保安全。
这些漏洞中的任何一个都可以被用于时间旅行攻击。
恶意序列器的现实扭曲攻击
一个无法证明的恶意状态转换可以改变链上的现实。如果一个合法的交易不能被模拟,一个恶意的序列器可以通过滥用它来做一个任意的状态变化,比如把桥上的整个L1流动性铸成自己在L2上的流动性,并开始向L1提款。这只会被序列器操作者作为rugpull(跑路)来利用,目前这种情况是允许的,但当rollups是去中心化的时候,它可能成为一个主要问题。
无法证明的欺诈:滥用防欺诈的gas检查
在欺诈证明的最后阶段,验证者必须调用OVM_StateTransitioner.applyTransaction来模拟交易。状态转换器执行某些检查,比如这个gas检查,导致它在条件不满足的情况下回退。
此外,由OVM_StateTransitioner.applyTransaction调用的OVM_ExecutionManager.run实际上是在gas少于用户指定量的情况下调用用户事务。
如果一个有效的L2事务使得在L1上不可能完成applyTransaction而不被还原,那么序列器将能够证明一个任意的状态根。每个人都能看出它是错误的,但不可能在链上证明它并撤销它。序列器可以利用这一点,为自己铸造任何数量的L2代币,然后通过典型桥将它们发送到L1,有效地耗尽桥。
OVM试图通过限制gas来防止这种情况,但是这个限制在OVM_ExecutionManager.run中应用得太晚了,所以它没有考虑到calldata的大小。因此,有可能创建一个交易,其使用的gas量略低于最大gas量,但也有calldata填充,由于主网块gas量的限制,它不可能在L1上进行模拟。对于这样的交易来说,在applyTransaction过程中的gas检查总会被恢复,因为即使L1交易开始时gasleft()与块gas限制一样高,有预先发生的calldata费用,它仍然不会满足(gasleft()>= 100000 + _transaction.gasLimit * 1032 / 1000)。
利用这一点的一个简单方法是使用上述嵌套交易的漏洞,并指定一个大的填充(padding)值。我不会重复几乎相同的代码(为了完整起见,这里可以提供更多信息),但这里会进行失败的欺诈证明尝试。在这个输出中,重要的部分是结尾处的回复。“没有足够的gas来决定性地执行交易。”这使得欺诈证明无法完成。
这类漏洞表明了一个重要的问题:如果欺诈证明变得过于复杂,它们可能会使完全的去中心化变得过于危险。一个恶意的序列器如果能做出一个无法证明的状态转换,它就能破坏并影响整个rollup。
欺诈证明不应该比正常执行的安全检查少,但也不应该有更多的安全检查。它们的行为必须是相同的。
时间线:我是如何开始探索时间旅行攻击的
自从我第一次听说EVM欺诈证明,并意识到它们在被滥用的情况下具有时间旅行的潜力,我就对这个想法非常着迷。我以前在自己的设计中使用过欺诈证明,例如在GSN中,但将欺诈证明应用于任意代码是一个不同的层次。Truebit开创了这个想法,但在L1上没有模拟出完全的执行。
2021年1月16日,Optimism宣布即将推出主网,我对他们如何解决单轮欺诈证明的惊人复杂性感到好奇,所以我不得不仔细看看到底是怎么回事儿。
Optimism计划在2021年4月上线主网,但后来我意识到,它已经在主网上为Synthetix提供了一条特定的应用链,在上线后不久就有价值超过1亿美元的SNX通过其L2桥被质押。这给我的好奇心增加了一些紧迫感。在接下来的两个月里,我全职深入研究了这个平台。
在这个研究过程中,我发现了一些漏洞,并实施了概念验证的攻击,比如这篇文章中的漏洞。
由于已经有资金面临风险,因此,实行负责任的披露是很重要的。由于当时没有Optimism的赏金(他们现在有一个很好的赏金),而且我不认识这个团队,所以我不确定如何安全地报告这些问题。最后,在2021年4月,Vitalik把我介绍给了Optimism团队(谢谢!),我们进行了长时间的电报聊天,然后进行了zoom会谈,讨论OVM安全模型。该团队非常细心和透明,我非常喜欢与志同道合的研究人员讨论欺诈证明的安全性。
OVM-1欺诈证明在主网上被禁用,从而消除了所有相关风险。并且它很快就会启用,以更好的形式-——Cannon!
我还想利用这个机会感谢Optimism为我的安全研究提供的追溯资助。追加拨款是支持研究的最佳选择。
单轮与交互式欺诈证明
欺诈证明可以通过两种方式实现:单轮模拟或作为交互验证游戏。前者实现起来比较简单,但如上所示,有一定的安全性和可用性的缺点。
在单轮证明中,证明者在欺诈性交易前重建最后一个良好状态的所需子集,针对最后一个良好交易的状态根进行证明,然后在一个交易中模拟整个欺诈性交易。Optimism在OVM-1中实现了这种方法。
在交互式证明中,证明者和序列器进行验证游戏来对执行过程进行证明。当他们达到分歧点时(一个他们结果不同的单一指令)他们只需要在链上模拟该指令,使用其输入并比较其输出。大部分的验证发生在链外,但有一个链上机制来确保双方的及时参与。Arbitrum在AVM(Arbitrum虚拟机)中实现了这种方法,如本文所述,Optimism正在OVM-2中使用Cannon实现这种方法。
单轮证明明显具有更大的攻击面。合约的状态机由整个执行的交易组成,所以可能的状态数量只受EVM中可能的执行流数量的限制。EVM的所有复杂性和极端案例都在发挥作用。另一方面,交互式证明只模拟一个操作码,所以状态的数量受架构操作码的数量和它们可能的输入的约束。例如,Arbitrum的OneStepProof.sol只需要证明79个操作码——可能小到足以实现状态机的全部测试覆盖。
单轮证明还对一个事务使用的gas进行了限制,因为它需要在另一个事务中进行模拟。这引入了可用性问题,如限制合约大小和不能执行某些在L1上可能的EVM流程。在OVM-2中,Optimism切换到了交互式证明,从而使EVM的状态对等,这在OVM-1中是不可能的。这种转换是正确的设计选择,原因有很多。
这篇文章中演示的漏洞是针对OVM-1实现的。在写这篇文章的时候,还没有公布未修补的OVM-2漏洞。
Cannon:OVM-2故障证明(fault proofs)
Optimism最近宣布了Cannon,即由geohot构建的交互式故障证明(以前称为欺诈证明)实现。
我不会在这里完全解释它,因为Ben解释得比我好得多,但总的想法是根本不需要一个单独的EVM实现,这是本帖子中描述的所有漏洞的根本原因。相反,它交互式地证明了MIPS指令集的一个子集,并编译了实现EVM状态转换的geth(又称minigeth)的一个子集。因此,在 L2 中运行的 EVM(在 l2geth 中实现)的行为应该与在 L1 上证明的 EVM 完全一样。
Cannon使用MIPS操作码的一个子集,只需要证明minigeth使用的55个操作码,使其状态机足够小,就可以进行全面的测试覆盖,有效的模糊处理,以及可能的形式验证。
新的架构使OVM-2严格地优于OVM-1:EVM状态对等,由于转换为交互式证明,攻击面最小,与geth的差异最小,从而减少了L1和L2执行环境差异的风险。
转向交互式证明需要研究新的攻击载体。例如,必须仔细审查确保挑战游戏中双方及时参与的机制设计和实施。
有趣的是,Arbitrum已经在他们的下一代Nitro架构上工作了一段时间,它似乎以同样的方式工作:它删除了AVM并将geth状态转换代码编译为WASM(与Truebit的架构选择相同)。虽然它目前还没有实现,但似乎两个项目最终都会使用类似的证明系统。我期待着对两者进行探索和比较。
桥:一个需要保持谨慎的词
如果欺诈证明在主网上被利用,主要受害者可能是快速桥。这些桥的流动性供应商有效地保证了他们的用户免受滥用欺诈证明系统的攻击。因此,我们建议这些项目采取一些预防措施来保护他们的流动性:
-
限制可用的流动性以保持风险的约束。例如,避免像MakerDAO提出的“无限流动性”,在L1上铸造无限 DAI 。像上面展示的攻击可能会导致严重的抵押不足,并使协议面临风险。我希望Wormhole团队能考虑在L1端实施限制。
-
在桥预言机运行一个 真正 的欺诈验证器,充分尝试对主网分叉上的任何交易进行欺诈验证。这比仅仅观察L2状态(就像OVM-1欺诈验证器所做的那样)的计算成本要更高,而且需要对所有的L2交易进行验证,而不仅仅是与桥相关的交易。但它可以防止上述所有的攻击和任何形式的时间旅行攻击,以及现实扭曲攻击。区块链仍然会回滚或改变,但桥会被立即冻结,所以没有资金损失。
-
考虑到防欺诈的保险。损失是可以在链上证明的,这样会使链上保险索赔变得更加容易。也许像Nexus Mutual这样的项目可以为流动性提供者提供一些保证。
欺诈证明的未来
Optimism的rollups正在向基于geth的EVM实现和geth执行的交互证明靠拢。希望Cannon和Nitro都能在未来几个月内上线,攻击面将随之变得更小,更容易保证安全。
赏金 将激励安全研究,不仅仅是在rollups桥上,还有欺诈证明的安全。Optimism已经开始为Cannon提供5万美元的赏金(我希望当Cannon被并入OVM-2时,他们最终能将其并入200万美元的赏金),Arbitrum也有100万美元的赏金,涵盖他们的欺诈证明。祝你在探索时间旅行的过程中也能赚到钱!
随着Optimistic rollups的成熟,增加快速桥的流动性会变得更加安全。
在更遥远的未来,也许我们会看到Optimistic rollups使用他们的欺诈证明的多种实现方式来增加安全性。这类似于以太坊的多客户端方法,单个客户端的错误不太可能导致共识失败。社区可以开发L1 EVM证明器的多种实现方式,在充分测试后,rollup可以选择使用其中的一些。一个单一的欺诈证明实现的失败,而其他实现认为状态转换是有效的,这将导致删除有漏洞的实现,而不是对L2链进行恢复。找到一个对多个欺诈证明实现有类似作用的漏洞将比攻击任何单一的实现要难得多。
总结
这篇长文展示了为什么欺诈证明会有风险以及它们如何被利用。它还讨论了减轻风险的方法,并对Optimism的rollups的未来进行简单的预测。
我希望能够提高人们的认识,吸引安全研究和资源,以确保欺诈证明的安全性,并防止有风险的时间旅行攻击的发生。
Lightchain AI Enters Bonus Round With Precision Timing While Dogecoin Hangs on Meme Buzz Alone
The post Lightchain AI Enters Bonus Round With Precision Timing While Dogecoin Hangs on Meme Buzz Al...
Traders Focused on Cardano Are Now Watching a Different Project Set to Launch by End of July
The post Traders Focused on Cardano Are Now Watching a Different Project Set to Launch by End of Jul...
Filecoin Integrates into Avalanche to Transform Scalable Smart Contract Data Storage
Filecoin collaborates with Avalanche to provide scalable, verifiable off-chain storage for smart con...