以太坊智能合约的发明使区块链用户能够自定义交易中的计算逻辑。但是,类似于传统的计算机程序,智能合约也具有漏洞,可以利用这些漏洞造成合约所有者的财务损失。尽管有许多用于检测智能合约字节码中漏洞的软件工具,但很少有工具专注于交易。在本文中提出TXSPECTOR,这是一种通用的,逻辑驱动的框架,用于研究以太坊事务以进行攻击检测。在较高级别,TXSPECTOR重放历史事务并记录EVM字节码级别的跟踪,然后将控制和数据相关性编码为逻辑关系。 TXSPECTOR无需设置预定义的功能集,而是允许用户指定自定义规则以发现事务中的各种类型的攻击。
已经构建了TXSPECTOR的原型,并对其进行了评估,以检测三种利用该漏洞的以太坊攻击:(i)重入(Re-entrancy)漏洞,(ii)未检查调用(UncheckedCall)漏洞和(iii)自杀(Suicidal)漏洞。
结果表明,TXSPECTOR可以有效地检测交易中的攻击,并作为副产品来检测智能合约中的相应漏洞。还展示了TXSPECTOR如何可用于交易的法证分析,并提出了检测规则,用于检测三种以太坊攻击之外的其他类型的攻击。
0x01 Introduction
以太坊是建立在区块链技术之上的最大的公共去中心化计算平台之一。与比特币网络相比,以太坊不仅支持简单交易,而且还以智能合约的形式提供图灵完备的计算功能。像许多其他软件程序一样,可以使用诸如Solidity之类的高级编程语言来开发智能合约,然后将其编译为字节码,然后在以太坊虚拟机(EVM)中为对等节点的每个节点执行对等(P2P)网络。与第一代区块链网络相比,执行复杂智能合约的能力已成为以太坊的一项关键功能。
但是,更高的可用性也带来更大的风险。两项功能使智能合约比传统软件程序更容易受到软件攻击。
(i)智能合约一经部署就一成不变。任何不可变的分布式分类帐都需要此功能。结果,智能合约中的漏洞无法修补,因此无法轻松修复。
(ii)以太坊是由加密货币驱动的;许多流行的智能合约还涉及将cryp转换为货币。因此,利用智能合约通常会导致巨大的财务损失。
例如,在臭名昭著的DAO攻击中,攻击者利用了DAO合同中的可重入漏洞并窃取了超过5,000万美元。再例如,Parity MultisigWallet中的漏洞已导致超过3,000万美元损失。许多此类攻击实例已引起人们对以太坊智能合约安全性的严重关注。
由于以太坊的流行,人们已经努力理解和检测这些智能合约漏洞,例如重入和整数溢出,并使用诸如符号执行的技术来分析智能合约或形式验证以验证其正确性。但是,对智能合约使用静态或符号分析来识别漏洞有两个方面的局限性。首先,这些工具很难同时实现完整性和准确性。
例如,使用符号执行的工具会遇到路径爆炸问题,并且现有工具无法检测到涉及多个智能合约的漏洞。其次,这些工具不能用于检查和了解现实世界中的以太坊攻击。法务信息,例如攻击的方式和统计信息,攻击者使用的地址以及受害者的地址,只能从交易中了解。这样,可以对事务执行字节码级分析的工具可以将两个方面的优点融合在一起,从而能够有效地检测和分析以太坊中的攻击和漏洞。
在本文中介绍了TXSPECTOR,这是一种用于以太坊交易的通用分析框架,用于识别针对交易中智能合约的真实攻击并进行取证分析。 TXSPECTOR的关键思想是通过对以太坊交易进行逻辑驱动的程序分析来检测对智能合约的攻击,该设计的灵感来自VANDAL ,这是基于Soufflé的EVM字节码静态分析工具。但是,对事务执行逻辑驱动的分析有两个挑战:首先,需要开发新的方法来提取数据并控制以太坊事务中的依存关系,并将其编码为逻辑关系。其次,尽管智能合约的数量很少,但交易量却很大。因此,跟踪和分析以太坊交易需要创新的方法来优化性能。
TXSPECTOR应对这些挑战:首先,它在区块链上重播交易并记录交易执行的字节码级别跟踪。随着新交易被追加到区块链,交易重放可以立即全部实现或逐步实现。为了避免重复的工作,建立了字节码级执行跟踪的数据库,该数据库可以重复使用。其次,它构造执行流程图(EFG)来对控制和数据依赖进行编码。第三,它从EFG中提取逻辑关系并将其存储到数据库中。第四,它使用用户特定的逻辑规则(称为“检测规则”)来查询数据库。 TXSPECTOR支持用户定义的任意检测规则,使他们能够研究其兴趣的任何方面。TXSPECTOR是第一个对以太坊交易执行字节码级,逻辑驱动的分析的通用框架。为了简化与交易相关的分析的后续研究,在https://github.com/OSUSecLab/TxSpector 的开放源代码许可下将TXSPECTOR提供给研究社区。
0x02 Background
智能合约是一种通用程序,在区块链上执行。它可以利用三个内存区域在执行期间执行数据操作:堆栈,内存和存储。 (数据)堆栈是可用于存储数据的虚拟堆栈。请注意,EVM还具有一个调用堆栈,该调用堆栈与数据堆栈不同。内存是在运行时分配的字节可寻址区域。存储是将256位字映射到256位字的键值存储。堆栈和存储器都是易失性的,这意味着每次执行后都会清除存储的数据。但是,存储是持久性的,可用于跨事务存储数据。结果,用于存储操作的天然气价格远高于堆栈和存储操作。
当前,EVM支持150多个OPCODE。根据指令操作的目标,它们可以分为五类:
•类别1:不对任何数据结构(例如JUMPDEST)进行操作的OPCODE。
•类别2:执行堆栈操作(例如PUSHx)或对堆栈中的现有值(例如ADD)进行操作的OPCODE。
•类别3:从区块链(例如,TIMESTAMP)或当前事务(例如,ORIGIN)中检索信息的OPCODE。
•类别4:读/写内存的OPCODE(例如MSTORE)。
•类别5:读/写存储的OPCODE(例如SSTORE)。
与比特币类似,以太坊也有一个由以太坊worker(节点)维护的P2P网络。要提交交易,用户需要支付称为gas的费用,以鼓励以太坊员工执行交易。用以太币(与以太坊相关的加密货币)来测量gas。每笔交易所需的gas量是根据其所包含的操作码计算得出的。如果没有足够的资金来执行交易,则执行将中止,所有更改将被还原。但是,在此过程中使用的以太币将不予退还。 Gas的采用还可以防止恶意交易(例如,具有无限循环的交易)危害网络。
以太坊有两种类型的账户:外部拥有账户(EOA)和合约(即智能合约)账户。两种类型的帐户都可以执行以太币转账。它们之间的主要区别在于,智能合约具有可以执行的关联字节码,而EOA没有任何代码。在以太坊中,交易是由EOA触发的。共有三种交易类型:
•类型1:在EOA之间转移以太币;
•类型2:在以太坊上部署新的智能合约;
•类型3:执行已部署合约的功能。
以太坊转移交易不涉及智能合约;也就是说,在处理这些交易时不会执行任何代码。但是,要部署新的智能合约或执行智能合约的功能,EVM需要执行相关的字节码。
与传统的计算机程序类似,智能合约也包含错误。其中一些可能被恶意攻击者利用。这些错误称为漏洞。智能合约中发现了许多漏洞。为了利用这些漏洞,攻击者需要制定智能合约并针对易受攻击的合约进行交易。因此,攻击与特定交易有关,而攻击的根本原因是智能合约的漏洞。
0x03 TXSPECTOR Overview
目标: TXSPECTOR是一个软件框架,用于对以太坊事务执行逻辑驱动的分析,以发现具有三个目标的攻击和漏洞。首先,它被设计为以太坊交易的通用分析框架,而不是为检测以太坊中的特定攻击而设计的。为此,它逐渐将事务转换为抽象,而不会丢失原始事务的重要信息。其次,它具有灵活性,可以通过自定义检测规则扩展到多个方面的事务分析,甚至包括与安全无关的分析。第三,TXSPECTOR还被设计为具有高性能。尽管使用逻辑驱动的框架无法实时执行一般的攻击检测,但已努力显着减少使用TXSPECTOR进行分析的存储和性能开销。
范围: TXSPECTOR的重点是根据给定的检测规则来检测以太坊交易的攻击。因为执行事务基本上是从多个智能契约执行字节码片段,因此TXSPECTOR也可以将智能合约的漏洞作为副产品显示出来。因此,TXSPECTOR能够通过交易以及与交易相关的易受攻击的智能合约,识别在区块链中发生的攻击。但是,TXSPECTOR并非旨在检测智能合约中的漏洞,这是大多数静态分析工具所针对的。
概述:TXSPECTOR由四个组件组成(上图)。跟踪提取器执行以太坊事务并生成字节码级别的跟踪,这些跟踪存储在跟踪数据库(DB)中以进行进一步处理。然后由执行流图生成器解析字节码级别的跟踪信息,以构造执行流图(EFG)。逻辑关系生成器遍历此EFG以提取数据和控制依赖项,然后将它们表达为逻辑关系,这些关系存储在逻辑关系数据库中。最后,攻击检测器将用户指定的检测规则作为输入,以查询逻辑关系数据库并输出最终的攻击报告。
0x04 Trace Extractor
跟踪提取器在以太坊虚拟机(EVM)内执行以太坊事务,并在执行期间记录字节码级别的跟踪。字节码级跟踪是一个三元组序列。对于由EVM执行的字节码的每个OPCODE,Trace Extractor将其在EVM中的程序计数器(PC),OPCODE和其自变量(ARGS)记录到一个三元组{<PC>; <OPCODE>; <ARGS>}中,其中PC用于通过字节码中的相对位置来标识OPCODE。为了减少数据冗余,Trace Extractor仅记录不是从堆栈中生成的参数。因为一个事务可以间接调用多个智能合约,所以可以从一个或多个智能合约的执行中生成单个字节码级别的执行跟踪。还记录交易的元数据,例如交易接收者的地址和交易的时间戳。
为了重放以太坊事务并收集跟踪,修改了Go-Ethereum EVM(版本1.8.0)以提取事务跟踪并将其与相关元数据一起存储在Trace DB中。并非所有操作码都需要记录。类型1事务是EOA之间的事务,并且没有与之关联的字节码。因此,跟踪提取器仅记录类型2和类型3事务。
Go-Ethereum EVM的修改:为了记录事务跟踪,修改了Go Ethereum EVM以记录OPCODE的相关信息,更具体地说,修改后的EVM记录了以下三种OPCODE的参数:
•与区块链/交易相关的操作(类别3)。此类型包括需要从区块链或当前交易中获取数据的OPCODE。例如,TIMESTAMP获取当前块的Unix时间戳; CALLER检索调用者的地址。
•与内存/存储相关的操作(类别4和5)。此处,跟踪提取器仅记录从内存/存储读取数据的OPCODE,即MLOAD和SLOAD。请注意,无需记录MSTORE和SSTORE的参数,因为它们仅需要来自堆栈的数据,而这些数据可以从跟踪的其他部分获得。
•PUSH操作。此类型包括所有PUSH OP CODE,即PUSHi,i = 1,···,32。
对于其余的OPCODE,它只记录PC值和OPCODE,因为不需要记录参数。
示例跟踪:跟踪提取器记录的跟踪与反汇编的EVM字节码相似。具体而言,迹线是一个三元组的序列,{<PC>; <OPCODE>; <ARGS>}。List 1中显示了一个事务跟踪代码段。跟踪和反汇编的字节码之间的主要区别在于,记录的跟踪还包含事务中使用的实际值。
跟踪数据库:跟踪数据库在执行事务时存储记录的字节码级跟踪和相关的元数据。具体来说,交易的每条痕迹都可能涉及一个以上的智能合约,因为可以调用多个智能合约,并且元数据包括以下信息:
(i)交易发送方(即交易的发送方),
(ii)交易的接收方(即接收方),
(iii)交易的时间戳(即,交易包含在一个区块中的日期和时间)。
0x05 Execution Flow Graph Generator
为了更明确地表达控制流,执行流图生成器会构建执行流图(EFG),以将轨迹的控制和数据流信息编码为图。由于字节码级跟踪是从事务生成的,因此EFG中没有未解决的分支。因此,每个智能合约中的执行流程都是顺序的。 EFG中的一个节点表示一个智能合约的执行,其中包含该合约生成的字节码级执行跟踪。 EFG中的边缘表示从一个智能合约到另一个智能合约的控制流转移。
执行流图生成器解析字节码级别的跟踪以构造执行流图(EFG)。由于跟踪是动态生成的,因此没有未解决的分支,每个JUMP仅具有一个目标。 EFG中的节点和边是通过以下方式创建的:
•节点:当执行流程从一个智能合约更改为另一个智能合约时,执行流程图生成器将生成一个新节点。具体而言,当遇到与CALL有关的操作码,即CALL / DELEGATECALL / CALLCODE / STATICCALL或与STOP有关的操作码,即STOP / REVERT / RETURN时,将生成一个新节点。
•边:当执行流从一个智能合约转移到另一个智能合约时,执行流图生成器将生成代表两个节点之间的控制流的边。有两种类型的边:类型I边是从调用方合约到被调用方合约的一条边。 II类边是从被调用方合约到调用方合约的一条边。
涉及三个智能合约的EFG示例如上图所示:智能合约A(节点a)首先调用智能合约B(节点b),生成类型I边,将执行流转移到智能合约B。当智能合约B完成执行时,返回智能合约A(节点c),生成II型边。 EFG在节点c中结束。
为了分析涉及多个智能合约的执行跟踪,将原始跟踪中的每个3元组增加到6元组:{<PC>; <OPCODE>; <ARGS>; <idx>; <depth>; <callnum>}。将idx,depth和callnum定义如下:
•Idx:由于不同合约中具有相同的PC值,因此无法仅从其PC值中判断哪个OPCODE首先执行。因此,为每个操作码引入了idx参数,以表示EFG中当前操作码的索引。
•depth:在处理带有大量外部调用的跟踪时,重要的是要知道每个OPCODE处于哪个调用级别。为此,引入了深度,它描述了EFG中每个OPCODE的调用深度。遇到与调用有关的OPCODE,深度增加1;当它返回时,深度减小1。
•Callnum:调用编号代表在EFG中每个OPCODE之前发生的调用数量。它是一个非递减值:遇到与调用相关的OPCODE时,它会增加1。
0x06 Logic Relation Builder
逻辑关系生成器首先解析EFG,以构建适合于分析的中间表示(IR),然后通过定义逻辑规则来提取表达事务语义的逻辑关系。之后,逻辑关系被存储在数据库中。特别地,逻辑规则被定义为表达控制流和数据流信息,以便获得交易中的控制和数据依赖性。例如,一些规则规定了操作码的执行顺序,这与控制流有关。一些规则跟踪如何定义和使用OPCODE的参数,这与数据流有关。为此,Logic RelationBuilder会为每个OPCODE生成逻辑关系,例如代表其操作数的寄存器及其PC值,同时将事务中的实际值与寄存器相关联,从而捕获动态信息。控制和数据依赖关系被编码成逻辑关系,然后被组织并存储在数据库中。
将基于跟踪的EFG转换为IR: TXSPECTOR在VANDAL中采用IR规范。此IR是基于寄存器的语言,是表示数据和控件相关性的另一种形式。 IR用寄存器代替了堆栈操作。例如,List 2中显示了List 1中示例跟踪片段的相应IR。因此,在以下两个方面扩展了VANDAL:首先,需要处理实数值而不是符号值。这是在Logic Relation Builder中通过使用具有真实值的寄存器模拟EVM堆栈操作来实现的,从而相应地更新了寄存器的值,并正确记录了所有中间值。这是在事务执行过程中捕获所有动态信息的关键步骤,而静态分析工具无法实现。例如,在处理TIMESTAMP时,字节码级跟踪中记录的实时时间戳值将被压入堆栈并分配给其相关的寄存器。第二,需要处理合约间的调用。对于EFG中的I型边,将密封当前堆栈并创建一个空堆栈。对于EFG中的II型边,将删除当前堆栈,并恢复最后的密封堆栈。
从IR生成逻辑关系:受VANDAL的启发,TXSPECTOR采用并扩展了其逻辑规则,以处理具有多个智能合约的真实价值和追踪。 TXSPECTOR中使用的规则如上图所示。例如,关系op将OPCODE与pc及其idx关联。通过值关系提取在事务中使用的实际值,该值关系记录寄存器和相关值。每个操作码及其相关的寄存器也被提取成逻辑关系。例如,SSTORE的逻辑关系记录了EFG中的所有SSTORE以及与它们相关的元组({pc,registers,idx,depth,callnum})。
逻辑关系的一个示例在上表中列出,该示例表示图2所示的EFG的PUSH1 OPCODE。第3和4行来自节点b,其深度已从1更改为2,callnum从0更改为1。第5行来自节点c。自调用返回以来,其深度已从2更改为1,但其callnum保持不变,且调用次数未更改。
0x07 Attack Detector
攻击检测器是TXSPECTOR的关键组件,它以用户指定的查询规则(称为“检测规则”)作为输入并查询由Logic RelationBuilder生成的Logic Relation DB,以推理出事务的特定安全性。一旦生成了逻辑关系数据库,就可以将其用于不同类型的分析。无需为每个检测规则重建一个新的数据库。对于特定查询,输出结果不是简单的是或否答案。相反,还提供了有关攻击的详细信息(如果检测到),可以进行进一步分析。
选择使用Soufflé构建攻击检测器,这是一种高性能的最新Datalog查询工具。因此,在TXSPECTOR中,检测规则以Soufflé编写,这是Datalog语言的一种变体。在本节中将说明如何构造检测规则以使用三个示例检测事务中的攻击:重入,未检查调用和自杀。
1)重入攻击规则
描述:重入攻击是最严重的攻击之一,它针对以太坊智能合约,因为输入的智能合约可能会多次转移以太币。当合约A调用合约B时,合约B可能会在同一笔交易中再次重新输入合约A。如果合同B恶意,则可能利用合约A的中间状态(例如,过期的帐户余额)从A窃取以太币。这种攻击称为重入攻击,因为它们是通过重新进入调用者合约而引起的(合约A)在同一笔交易中。最臭名昭著的重入攻击是DAO攻击,其中,攻击者从DAO合同中窃取了大量以太币(价值超过5,000万美元)。
目标是设计检测规则来检测SEREUM中提到的高级重入攻击。如果在重入并返回受害者合约后状态发生变化(即,存储变量的更新),并且此存储变量在重新输入受害者合约时影响控制流决策,则将导致状态不一致。
要求:假设合约A是受害者,合约B是攻击者。定义了四个阶段:
•阶段1:A执行其代码(阶段1.1)并调用B(阶段1.2); B再次调用A重新进入A(阶段1.3)。重新输入的A表示为A’;
•阶段2:A’在返回B之前执行其代码;
•阶段3:A’返回到B(阶段3.1),B返回到A(阶段3.2);
•阶段4:A继续执行。
事务应至少具有所有这四个阶段才能执行重新进入攻击,并且具有至少一个不一致的状态是重新进入攻击的必要条件。四个阶段的示例如上图所示。鉴于四个阶段,对于不一致的状态有两个要求:
(i)SLOAD-JUMPI依赖性:在阶段2中,A’使用SLOAD加载(使用SLOAD)存储变量(V)控制流决策(即,JUMPI指令的条件);
(ii)对SLOAD-SSTORE的依赖性:在阶段4中,A更新(使用SSTORE)存储变量V。如果事务满足两个要求,则很明显,在阶段2中,A’从不一致的状态为控制流决策加载V。结果,B能够通过重新进入A来操纵控制流,从而发起重入攻击。
检测规则:根据不一致状态的要求定义检测规则。更具体地说,定义以下检测规则(如上图所示):
检测规则首先检查SLOAD-JUMPI依赖关系。如果在JUMPI的情况下使用了由SLOAD加载的值(sloadVal),则将获得SLOAD地址(sloadAddr)。 SLOAD和JUMPI应该具有相同的深度和callnum(在§5中定义),并且JUMPI的条件(jumpiCond)应取决于SLOAD的值(sloadVal),该值由“依赖检测规则”强制执行。 Depends(A,B)检测规则检查是否存在从A到B的数据流。
接下来,“检测规则”检查SLOAD-SSTORE依赖关系。首先,检查在地址(sloadAddr)上是否存在SSTORE。如果是这样,将检查
(i)通过checkSameAddr检测规则满足第一条件的SLOAD是否已经使用了该地址,
(ii)是否在SLOAD之后执行了SSTORE,并且在外部调用返回之后执行了(分别通过过滤器ByDepth检测规则和filterByIdx检测规则检查idx和深度)。
checkSameAddr规则检查SSTORE和SLOAD是否具有相同的地址。 filByDepth检测规则保留sloadDp大于sstoreDp的SLOAD和SSTORE对。 filterByIdx检测规则保留sloadIdx小于sstoreIdx的SLOAD和SSTORE对。此外,检查SLOAD,SSTORE和JUMPI是否在同一合约中(通过检查Samecontract Detection Rule)。如果是这样,则检测到不一致状态,这表示重入攻击。
2)未检查调用攻击的规则
描述:由于缺少对外部调用返回值的检查,因此可以利用UncheckedCall攻击来窃取以太币。具体来说,在以太坊智能合约中,CALL OPCODE经常用于合约间通信和加密货币转移(即发送功能)。
在外部调用过程中,可能会发生异常,这将导致被调用方合约恢复执行并返回。理想情况下,调用方应检查该调用的返回值。如果它为零(例如,由调用期间的异常引起),则应采取措施(例如,恢复其执行)以正确处理异常。但是,许多开发人员不执行此类检查。结果,这些合约具有UncheckedCall漏洞,并导致钱被盗。
要求:采用字节码分析工具SECURIFY的检测标准来定义UncheckedCall攻击的要求。包含UncheckedCall攻击的事务应满足以下要求:
(i)外部调用:事务中至少有一个外部调用(与CALL相关的OPCODE)。
(ii)未检查的调用返回值:至少有一个外部调用的返回值未被任何JUMPI使用。
拥有至少一个未经检查的返回值是UncheckedCall攻击的必要条件。更具体地说,在字节码级别上,根据CALL的返回值使用JUMPI检查返回值isdone,因此,如果在事务中存在任何JUMPI未使用的调用返回值,则意味着该值是未选中,此交易正受到UncheckedCall攻击。
检测规则:为了检测UncheckedCall攻击,在上图中定义了检测规则。UncheckedCall检测规则首先提取事务中所有的调用返回值(第7行),然后根据每个请求中的每个检查是否有JUMPI。调用返回值,使用jumpiDep检测规则。如果任何JUMPI都没有使用调用返回值,则该事务将标记为UncheckedCall。请注意,交易可能包括来自多个合约的OPCODE。“检测规则”中的CALL和JUMPI的深度都设置为1,因此仅检测到针对接收方合约的UncheckedCall攻击。
3)自杀攻击规则
描述:由于缺乏适当的权限检查,“自杀”攻击可能导致智能合约被任何人而不是合约所有者杀死。具体来说,以太坊提供了智能合约,能够通过SELFDESTRUCT操作码将自己从区块链中删除。尽管SELFDESTRUCT的设计是供合约所有者管理其智能合约的生命周期的,但某些智能合约在调用SELFDE STRUCT之前无法添加适当的权限检查。由于TXSPECTOR会检查交易而不是智能合约,因此它会检测未经授权的用户触发智能合约SELFDESTRUCT的攻击。每个合约最多只能被检测到一次,因为此后它已被销毁。
要求:可以通过在执行SELFDESTRUCT之前检查是否存在权限检查(即JUMPI)来检测自杀攻击。有两个要求:
(i)SELFDESTRUCT:交易中至少有一个SELFDESTRUCT。
(ii)不对CALLER进行权限检查:没有依赖CALLER的JUMPI。
如果事务中不存在CALLER-JUMPI依赖关系,则表示它在执行SELFDESTRUCT之前不检查msg.sender(CALLER),这进一步表明该合约可能被任何人杀死,即自杀攻击。
检测规则。上图显示了用于检测自杀的检测规则。该检测规则首先确保事务中至少有一个SELFDESTRUCT,然后通过jumpiDep检测规则检查在SELFDESTRUCT之前是否已检查msg.sender。 (第3-4行)。
0x08 Evaluation
1)实验设置
跟踪收集:跟踪是在Microsoft Azure云上的L8s v2instance上收集的,该实例具有8个VCPU,64GB RAM和2TB SSD,运行Ubuntu 18.04。 Trace Ex运行了一个完整的以太坊节点,以收集字节码级别的跟踪,从第0个块到第7,200,000个块。请注意,以太坊拥有大约10180000的区块数量(截至2020年6月1日),并且一直呈指数增长。无法收集所有这些数据用于实验,因为这需要大量的存储空间和处理时间。因此,在第7,200,000个数据块处停止收集,这导致了1,577 GB的大小,总共包含397,269,533个事务。通过Trace Extractor将它们存储在Trace DB(由atopMongoDB 实现)中。
数据集:收集了块事务的跟踪之后,从它们中导出事务,这些事务是TXSPECTOR的输入。鉴于拥有巨大的区块数量,无法将它们全部用于交易,因为一个区块可能包含多个交易。因此,决定只关注从第700万个区块开始的交易,因为它包含16,485,279笔交易,涵盖了2019年1月至2019年2月之间的交易。通过这样的1600万笔交易,相信它具有代表性实验的各种情况。
逻辑关系生成:与逻辑关系生成相关的数据集包含9,661,593笔交易,这是通过两步获取的。首先,数据集最初包含16,485,279笔交易。但是,并非所有交易都有跟踪。也就是说,它们不调用智能合约的执行。必须将它们过滤掉,因为逻辑关系生成过程仅将具有跟踪的事务作为输入。过滤之后,还剩下9,662,675笔交易。第二,处理原始数据以生成逻辑关系。但是,由于超时,并非所有事务都可以处理。因此,将超时阈值设置为60秒,并通过执行流图生成器和逻辑关系生成器处理这些跟踪中的每条跟踪,以生成逻辑关系。不幸的是,1,082个事务(0.01%)没有按时完成逻辑关系生成。因此,最终数据集包含9,661,593笔交易。逻辑关系数据库占用2,949 GB空间。
大多数逻辑关系是在很短的时间范围内生成的,具体来说,只有94,277(1.0%)个事务的处理时间大于4s。在上图中绘制了在4秒钟内完成处理的事务的处理时间分布(9,662,675个事务中的99.0%),大约60%的事务在1秒钟内完成了生成逻辑关系。如果将超时阈值设置为2s,则可以生成约90%事务的逻辑关系。生成一次事务的逻辑关系平均需要1.03s。请注意,对于每笔交易,无论要在攻击检测器中应用多少检测规则,都只需生成一次逻辑关系,因为不同的检测规则使用相同的逻辑关系。
生成逻辑关系后,将“检测规则”应用于检测交易中的攻击和漏洞。在“攻击检测器”中将超时阈值设置为1s。
评估步骤和标准:在评估过程中采取了几个步骤,还比较了其他静态分析工具的结果。首先根据检测规则应用TXSPECTOR标记事务。对于标记的交易,如果可以使用接收方智能合约的源代码,则将执行手动检查以检查它们是否容易受到特定攻击。接下来,如果智能合约易受攻击,则与这些合约相关的标记交易被视为真实肯定。否则,相关交易被视为误报。由于交易量很大,无法分析负面结果。其他相关作品(例如SEREUM)也面临相同的问题。最后,如果这些工具可以检测到特定的漏洞,还将TXSPECTOR与三种基于Datalog的静态分析工具进行比较:SECURIFY,VANDAL和GIGAHORSE。
2)重入攻击的结果
首先介绍了重入攻击的检测结果,并将重入检测规则应用于数据集中的9,661,593个事务。由于超时,未完成336,909笔交易(3.5%)。对于有判决的9,321,684笔交易,TXSPECTOR将3,357笔交易(0.04%)标记为重新进入攻击。这3,357笔交易与30份智能合约有关,其中22笔是开源的。使用在线Solidity反编译器反编译了8个封闭源合约。经过手动检查,确认22份开源合约中的10份和8份封闭源合约中的7份包含重入漏洞。 TXSPECTOR误标记了13个智能合约的主要原因有两个:
(i)无法检测到禁止未经授权重新进入重入函数的锁;
(ii)可以重新输入错误标记的合约,但不能可能从他们那里窃取以太币或令牌。
误报的一个示例是事务0xd32496。List 3中显示了相关功能的代码段,该代码段使用锁来防止未经授权的重新进入尝试。在调用之前检查锁定变量(reentrancyLock),并在调用之后更新日期,这使合约只能重入一次。这样,就可以防止实际攻击,但是交易的执行符合重入攻击要求。结果,这是误报。
检测结果与SEREUM相当,在49,080笔交易中(总共77,987,922笔交易中)错误地标记了46,743笔,误报率为0.06%。遵循相同的标准,TXSPECTOR错误标记了3,072笔交易(与13份智能合约有关),误报率为0.03%。但是,认为这些值只是检测精度的近似值,因为不可能计算出真实的负值。
为了将TXSPECTOR与其他工具的检测结果进行比较,或者与相应的专家联系以寻求帮助和说明,或者使用相同的数据集运行开源工具。结果总结在下表中。在下面详细介绍了比较。
与SEREUM比较:没有SEREUM的开源租赁;但是与SEREUM的作者取得了联系,并出于比较目的获得了他们的评估结果。对于同一数据集,SEREUM将10,278个事务标记为重入攻击,其中2,732个也由TXSPECTOR标记。对于其余的7,546个事务,发现由于超时,其中有7,271个在数据集中没有结果。 TXSPECTOR标记了625个事务,但SEREUM没有标记。虽然人工检查表明它们导致状态不一致,但不明白为什么SEREUM无法识别它们。
与SECURIFY的比较:使用静态分析工具SECURIFY进行了比较。请注意,SECURIFY旨在检测智能合约中的重入漏洞,而本研究重点是交易。首先在数据集中提取交易的所有接收者智能合约,然后将SECURIFY应用于这些智能合约。数据集中的接收者智能合约总数为105,535。 TXSPECTOR标记的3327笔交易中,只有30个接收者智能合约。在105,535个接收者智能合约上运行了SECURIFY的开源版本。超时阈值设置为60s,以分析每个合约。其中1,315个由于超时而未能完成;由于运行时错误,其中6,226个没有结果。对于其余的97,994个智能合约,SECURIFY将其中的1,196个标记为不合法,并且TXSPECTOR均未标记它们。
阅读SECURIFY的源代码后,发现它定义了两种可重入的形式,“与gas有关的重入” 和“恒定gas重入” 。但是在本研究的定义中检查不一致的状态,这要求在调用后更新状态。因此得出结论,TXSPECTOR导致来自SECU RIFY的不同检测结果,因为它们具有不同的检测重入标准。还可以定义检测规则,以使用TXSPECTOR检测这些类型的重入攻击(。值得注意的是SEREUM还提到“ Securify为重新进入检测定义了一个非常保守的违规模式,该模式禁止在外部调用后进行任何状态更新”,结果导致“非常高的误报率” ”。
与VANDAL的比较:使用VANDAL的开源版本进行比较。超时阈值设置为60s,以分析每个合约。分析105,535个接收者智能合约时,其中1,206个(1.1%)未在60秒钟内完成; 225(0.2%)由于某些运行时错误而没有结果。对于剩余的104,104个智能合约,VANDAL标记为85,721(82.3%)为可重入合约,这显然是不合理的。随机选择了一些检测到的智能合约,并发现它们都是错误的。由于已标记合约的数量巨大,因此无法对所有合约进行人工检查。
通过检查VANDAL提供的规则,发现这些规则比本研究的规则宽松得多。根据他们的结论,“如果调用了足够的gas并且不受互斥锁的保护,则将其标记为可重入”。结果,凡是有足够gas且没有锁定的调用都将被VANDAL标记为可重入,这是一个非常宽松的标准。在TXSPECTOR标记的30个智能合约中,VANDAL也标记了27个。对于剩余的3个,VANDAL由于超时而没有完成对它们的分析。因此,TXSPECTOR优于VANDAL,因为它导致较低的FP速率。
与GIGAHORSE的比较:没有GIGAHORSE的开源版本,但是有一个网站供用户查询GIGAHORSE的结果。从他们的网站中提取了“再入局”的所有结果。在数据集中的105,535个接收者智能合约中,有3,310个(3.1%)被GIGAHORSE标记为可重入。 GI GAHORSE还标记了TXSPECTOR检测到的30个智能合约中的18个; GIGAHORSE认为其余的12个没有漏洞。根据常见问题页面上的解释,只有当“合约进行了外部调用,该合约本身可以在第一次调用更新存储之前重新输入”时,GIGA HORSE才认为智能合约是可重入的。因此,它具有比TXSPECTOR定义的不一致状态更为严格的标准。对于GIGAHORSE而非TXSPEC TOR标记的3,292个智能合约,主要原因是没有交易显示数据集中状态不一致。
DAO合约的案例研究:尽管TXSPECTOR在检测某些类型的重入攻击(与SEREUM相比)方面不尽人意,但证明了它在检测最突出的攻击(如DAO攻击)中仍然有效。 DAO合约是重新进入攻击最初发生的地方,它是重新进入攻击的第一受害者。 DAO盗窃了价值超过5000万美元的以太币。为了避免损失,以太坊社区决定对区块链进行硬分叉以返还被盗的钱,这导致以太坊区块链的分叉。
要检查可能已经攻击了DAO合约的交易,请简单地使用DAO提取交易,因为接收方不起作用,因为恶意合约不是接收方。取而代之的是,扫描了跟踪提取器收集的原始跟踪,以了解从块0到块7,200,000的每笔交易,并且仅保留其跟踪包含DAO合约地址的交易。此过滤过程完成后,还剩下98,914个事务。
对98,914个事务应用了具有重入检测规则的TXSPECTOR,以查看DAO合约受到攻击的次数。使用前面提到的相同过程来生成逻辑关系,并应用了重入检测规则。由于重新进入事务比常规事务复杂,因此将逻辑关系超时阈值增加到200s,将检测规则超时阈值增加到60s。此过程之后,由于超时,仍有3,665笔交易没有结果。在其余的95,249笔交易中,TXSPECTOR将其中的2,108笔标记为重新进入攻击。
为了将TXSPECTOR与SEREUM进行比较,检查了SEREUM标记的这98,914个事务中有多少个。尤其是SEREUM标记了2,112个事务,其中TXSPECTOR也标记了2,108个事务。也就是说,TXSPECTOR检测到的所有攻击也都由SEREUM标记。 SEREUM标记了4个事务,TXSPECTOR则没有。手动检查了这4个事务,以了解TXSPECTOR为什么不标记它们。检查了SSTORE,SLOAD和JUMPI的逻辑关系,以查看是否存在TXSPECTOR遗漏的依赖项。经过检查,确认在这4个事务中,有成对的(SLOAD,SSTORE)操作相同的存储地址。但是,这些对具有相同的深度,这意味着它们不满足不一致状态的条件。因此,TXSPECTOR并未将其标记为重新进入攻击。
3)未检查调用攻击的结果
接下来介绍UncheckedCall攻击的检测结果。将UncheckedCall检测规则应用到的数据集后,由于超时,323,772个事务(3.4%)未完成。在产生结果的9,337,821笔交易中,TXSPECTOR将178,303笔交易标记为UncheckedCall,并且有1,430笔相关的接收方合约。其中有216个是开源的,与28,377笔交易有关。手动检查了216个智能合约,发现其中213个确实存在UncheckedCall漏洞。进一步调查了TXSPECTOR为什么标记了剩余的3个合约。发现这3个合约都对外部调用进行检查,但是在事务中,由于“ out of gas”失败而未执行检查,因此TXSPECTOR对其进行了标记。 3个标记错误的智能合约仅与4个交易相关。值得注意的是,对于其余1,214个封闭源智能合约,无法对合约执行手动分析。但是确实确认在他们的跟踪中发现至少一个{CALL POP},或者他们没有使用至少一个调用值,这表明它们确实是根据本研究检测规则进行的攻击。还将结果与SECURIFY和VANDAL进行比较。比较结果总结在上表中。
与SECURIFY的比较:使用SECURIFY来检测滥用数据集中105,535个接收者智能合约的UncheckedCall漏洞的攻击。在分析这些智能合约时,其中有2,993个(2.8%)未在60秒内完成。此外,由于某些运行时错误(例如,索引超出范围),无法完成对另外3,501个(3.3%)智能合约的分析,因此没有结果。经过处理后,SECURIFY生成了99,041(93.9%)个智能合约的结果,并将其中2,380个标记为具有UncheckedCall漏洞。
进一步研究了TXSPECTOR标记的178,303笔交易。提取了这些交易的接收方合约(总共1,404个),并将它们与SECURIFY的检测结果进行了比较。 TXSPECTOR标记了1,183个(84.3%)接收方合约,这些合约也标有SECURIFY; TXSPECTOR标记了另外142个(10.1%),但是SECURIFY由于超时或运行时错误而没有完成执行; TXSPECTOR标记了79个智能合约,但被SECURIFY标记为“安全”。在执行这79个合约所生成的字节码级别的跟踪中,调用者合约会弹出回叫值,因此跟踪中没有JUMPI-CALL依赖性。推测SECURIFY不标记这些合约的原因可能是与其使用的符号执行方法有关的问题。
仅由SECURIFY而不是TXSPECTOR标记的大约1200个智能合约。检查了其中的一些,发现智能合约中存在UncheckedCall漏洞,但交易中未包含有漏洞的功能。因此,TXSPECTOR未检测到它们。
与VANDAL的比较:使用VANDAL分析105,535个接收方智能合约时,其中1,151个(1.1%)未在60秒钟内完成。对于TXSPECTOR标记的1,403个智能合约,VANDAL还标记了其中的1,367个(97.4%);其中的其余36个未被VANDAL识别。通过手动检查,发现这36个智能合约具有UncheckedCall漏洞。一个示例是合约0x99ECA3。在此合约中,未检查transfer()函数的返回值,这表明存在UncheckedCall漏洞。因此,TXSPECTOR能够识别VANDAL丢失的易受攻击的智能合约。
VANDAL将另外91,012个智能合约标记为UncheckedCall漏洞。由于覆盖范围,TXSPECTOR未检测到这些智能合约:易受攻击的功能未包含在数据集交易中。
4)自杀攻击的结果
最后介绍了自杀攻击的检测结果。将自杀检测规则应用于数据集后,由于超时未完成327,208笔交易(3.4%)。在有结果的9,334,385笔交易中(96.6%),TXSPECTOR标记了23笔交易自杀。其中,只有18个接收者智能合约,因为其中5个交易的接收者地址为0x0,这意味着它们在创建后立即被杀死。无法研究它们的源代码,因为它们在被杀死时已从区块链中删除了它们的字节码和存储期限。从23笔交易的跟踪中,确认没有对调用方进行权限检查(msg.sender )。因此,TXSPECTOR不会产生误报。还将结果与VANDAL和GIGAHORSE进行比较,比较结果总结在上表中。
与VANDAL的比较:对数据集中的所有105,535个智能合约运行了VANDAL,以检查其中有多少具有自杀漏洞。 VANDAL标记其中349个为弱者。 TXSPEC TOR标记的18份智能合约中有13份也标有VANDAL。对于VANDAL未标记的5个智能合约,由于运行时错误和超时,VANDAL无法对其进行分析。仅对于VANDAL标记的336个智能合约,主要原因是这些智能合约具有自杀漏洞,但尚未被杀死(即,未调用功能)。因此,TXSPECTORdid无法检测到它们。
与GIGAHORSE的比较:为了与GIGA HORSE进行比较,从他们的网站中检索了他们的“可访问的自毁”结果。在数据集中的105,535个接收者智能合约中,GIGAHORSE标记了383个智能合约,其中只有一个与TXSPEC TOR的结果相同。
GIGAHORSE并未标记其他17个合约的原因是,在部署GIGAHORSE时,它们的字节码在被杀死后丢失了,因此GIGAHORSE无法对其进行分析。对于由GIGAHORSEonly标记的智能合约,发现它们有一个宽松的标准:只要从公共入口点可以到达SELFDESTRUCT,即使有检查,也将其标记为True。但是,TXSPECTOR仅检测到自杀根本没有检查的漏洞,比GIGAHORSE严格。
5)与其他工具的比较
除了三个基于Datalog的工具之外,还与其他三个静态分析工具进行了比较:MYTHRIL ,OYENTE 和MAIAN。对于MYTHRIL,将它与TXSPECTOR在所有三个漏洞上进行了比较;分别比较了OYENTE的重入和MAIAN的自杀。结果总结并显示在下表中。总而言之,不同工具的检测结果差异很大。主要原因是没有检测这些漏洞的黄金规则,并且不同的工具使用不同的检测规则。除了开放源代码之外,还发布了比较结果,以便社区中的其他人可以将数据用于他们的研究。
6)超时分析
由于生成逻辑关系而超时。产生逻辑关系时,有6082笔交易未能完成。手动检查了这些事务以了解超时的原因。发现这些事务中的大多数都卡在逻辑关系构建器中,后者将基于EFG转换为IR并执行具有实数值的算术运算。算术运算有时可能太复杂而无法即时计算(例如exp(a,b)),这是由于逻辑关系生成而导致超时的主要原因。一个示例是事务0xf9de18,该事务有6,945个操作码。此外,它具有大量的exp操作作为指数,这会导致超时。
由于应用检测规则而超时。分析了应用UncheckedCall检测规则时超过超时阈值的事务。发现,在找到调用返回值和JUMPI之间的依赖关系时,大多数事务都被卡住了。假设在事务跟踪中JUMPI的数量为m,而CALL的数量为n,则将有m * n(CALL,JUMPI)对。对于每对,TXSPECTOR可能会通过许多中间变量来查找它们之间的依赖关系,以尝试检查JUMPI是否使用了调用返回值。当m ∗ n大且轨迹较长时,验证所有m ∗ n对的依赖项将花费大量时间。一个示例是事务0xb513f5,它具有11,664个JUMPI和299个CALL。对于大约350万(CALL,JUMPI)对,TXSPECTOR需要遍历所有潜在的中间变量以确认是否存在依赖关系,这是无法忍受的。
优化:为了优化逻辑关系生成,可以从以太坊节点获取这些中间结果,因为它们应在事务执行期间出现。为了加快“检测规则”的应用过程,可以添加更严格的修剪规则,以过滤掉可能不存在依赖关系的对,然后再尝试查找它们。此外可能会添加一些辅助函数来存储某些节点的依赖关系,从而不必每次都重新计算它,将这些优化留作未来的工作。
0X09 Discussion
时间成本:一次交易生成逻辑关系平均需要1.03秒。考虑到以太坊中的交易量,实时处理它们将非常具有挑战性。 TXSPECTOR被设计为针对交易的取证分析框架,但不打算用作以太坊的实时攻击检测工具。尽管如此,在生成逻辑关系时仍有多种方法可以提高TXSPECTOR的性能。例如,由于事务之间没有依赖性,因此可以将多线程应用于并行生成多个事务的逻辑关系。
存储成本:存储逻辑RelationDB需要大量空间。为了节省空间,TXSPECTOR可以采取措施来缩小逻辑关系数据库的大小。例如,当产生逻辑关系时,可以使用标准的序列化或压缩库(例如,gzip)。此外,如果感兴趣的OPCODE在通过逻辑关系生成器之前是已知的,则TXSPECTOR可以选择OPCODE的子集,并且仅为这些OPCODE生成逻辑关系,而不是为所有OPCODE生成逻辑关系。
事务与字节码:研究交易的好处在于,交易包含智能合约如何相互作用的信息。但是,交易的字节码级别跟踪仅包含智能合约的部分信息;它仅涉及此事务期间调用的功能。如果智能合约中的某个功能从未被其他人调用,则没有与之关联的交易。在这种情况下,交易无法显示与智能合约的此功能相关的任何漏洞。但是,如果智能合约从未涉及任何交易,则也不会利用这些漏洞。因此,对于预测分析,分析交易比研究智能合约字节码更有意义。
反应性方法与主动性方法:作为攻击检测和取证分析工具,TXSPECTOR会检查本质上是反应性的交易,这意味着只有在以太坊区块链上发生攻击后才能检测到攻击。与主动方法(例如静态分析)无法检测可能永远不会触发的智能合约中的漏洞不同,但是研究交易可以揭示过去发生的真实攻击,并从取证的角度向他们学习。另一方面,由于TXSPECTOR只能看到部分智能合约字节码,因此静态分析工具对TXSPECTOR起到了补充作用。在TXSPECTOR涵盖了来自交易的攻击之后,可以使用静态分析工具来研究受害者智能合约以识别其他潜在的攻击面,以及攻击者智能合约以了解攻击机制。
需要努力设计新规则:TXSPECTOR仅可用于对已知的攻击/漏洞功能进行取证分析。为了提出检测规则,用户需要了解一些她想检测的攻击/漏洞的知识,以及对构建检测规则的基本了解。在TXSPECTOR的开源版本中,提供了现有漏洞规则供用户选择,以使他们不必重新发明轮子。此外,为了最大程度地减少用户开发定制的检测规则所需的工作量,还提供了API列表以及文档/自述文件以帮助用户入门。
其他应用程序:在本文中表明TXSPECTOR可用于检测6种不同类型的攻击。但是,TXSPECTOR的应用范围不仅仅在于检测攻击和漏洞。它可用于在交易的许多其他方面进行取证分析。例如,TXSPECTOR可用于检查交易中是否涉及特定地址,如果是,则对交易执行特定的分析。此外,它还可用于检索某些中间结果以了解事务失败原因。
0x0A Conclusion
本文介绍了TXSPECTOR,这是第一个通用的,开源的,逻辑驱动的框架,用于在字节码级别研究以太坊事务。 TXSPECTOR支持用户定义的自定义检测规则来检测以太坊攻击。本文介绍了TXSPECTOR的设计和实现,并演示了检测规则以检测交易中的攻击。评估表明TXSPECTOR是有效的,还演示了如何使用TXSPECTOR对交易进行取证分析。
发表评论
您还未登录,请先登录。
登录