[论文地址] [代码] [USENIX Security 20]
省略了论文的图表部分
TxSpector: Uncovering Attacks in Ethereum from Transactions
Abstract
以太坊智能合约的发明使区块链用户可以在交易中定制计算逻辑。然而,与传统的计算机程序类似,智能合约也有漏洞,这些漏洞可以被利用,造成合约所有者的经济损失。虽然有许多软件工具用于检测智能合约字节码中的漏洞,但很少有人关注交易。在本文中,我们提出了TXSPECTOR,一个通用的、逻辑驱动的框架,用于调查Ethereum交易以检测攻击。在高层次上,TXSPECTOR重放历史交易并记录EVM字节码级别的痕迹,然后将控制和数据依赖性编码为逻辑关系。TXSPECTOR不是设置一套预定义的函数,而是允许用户指定定制的规则来发现交易中的各种类型的攻击。我们建立了TXSPECTOR的原型,并评估了它对三种以太坊攻击的检测:(i)Re-entrancy漏洞,(ii)UncheckedCall漏洞,和(iii)Suicidal漏洞。结果表明,TXSPECTOR可以有效地检测交易中的攻击,作为副产品还可以检测智能合约中的相应漏洞。我们还展示了TXSPECTOR如何用于交易的取证分析,并提出了检测规则,用于检测除三种重点以太坊攻击之外的其他类型的攻击。
I. Introduction
以太坊是建立在区块链技术之上的最大的公共去中心化计算平台之一。与比特币网络相比,以太坊不仅支持简单的交易,而且还具有图灵完备的计算功能,以智能合约的形式存在。像许多其他软件程序一样,智能合约可以使用高级编程语言,如Solidity来开发,然后编译成字节码,在点对点(P2P)网络的每个节点的以太坊虚拟机(EVM)中执行。与第一代区块链网络相比,执行复杂智能合约的能力已经成为以太坊的一个关键特征。
然而,更强的实用性也伴随着更大的风险。有两个特点使智能合约比传统软件程序更容易受到软件攻击。(i) 智能合约一旦部署就不可改变。这一特点是任何不可变的分布式账本所要求的。因此,智能合约中的漏洞不能轻易修复,因为它们不能被修补。(ii) 以太坊是由加密货币驱动的;许多流行的智能合约也涉及加密货币的转移。因此,对智能合约的利用往往导致巨大的经济损失。例如,在臭名昭著的DAO攻击中,攻击者利用了The DAO合约中的重入漏洞,盗取了超过5000万美元。作为另一个例子,Parity Multisig Wallet中的一个漏洞导致了超过3000万美元的损失。许多这样的攻击事例引起了人们对以太坊智能合约安全性的严重关切。
由于以太坊的流行,人们已经努力理解和检测这些智能合约的漏洞,如重入,和整数溢出,使用符号执行等技术来分析智能合约或形式验证来验证其正确性。然而,在智能合约上使用静态或符号分析来识别漏洞有其局限性,原因有二。首先,这些工具很难同时实现完整性和准确性。例如,使用符号执行的工具存在路径爆炸的问题,而且现有的工具不能检测到涉及多个智能合约的漏洞。其次,这些工具不能用于检查和了解真实世界的以太坊攻击。鉴证信息,如攻击的模式和统计数据,攻击者使用的地址,以及受害者的地址,只能从交易中了解。因此,一个能够对交易进行字节码级分析的工具可能会将两方面的优点结合在一起,实现对以太坊攻击和漏洞的有效检测和分析。
在本文中,我们提出了TXSPECTOR,一个针对以太坊交易的通用分析框架,用于识别交易中针对智能合约的真实攻击,并实现对攻击的取证分析。TXSPECTOR的关键思想是在以太坊交易上使用逻辑驱动的程序分析来检测针对智能合约的攻击,这种设计受到VANDAL的启发,它是一个基于Soufflé的EVM字节码静态分析工具。然而,对交易进行逻辑驱动分析的挑战是双重的。首先,需要开发新的方法来提取以太坊交易中的数据和控制依赖关系,并将其编码为逻辑关系。第二,虽然智能合约的数量不多,但交易量可能很大。因此,追踪和分析以太坊交易需要创新的方法来优化性能。
TXSPECTOR解决了这些挑战,具体如下。首先,它重放区块链上的交易,并记录交易执行的字节码级痕迹。交易重放可以一次性实现,也可以随着新交易被添加到区块链上而逐步实现。为了避免重复工作,建立了一个字节码级别的执行痕迹数据库,可以重复使用。其次,它构建了执行流程图(EFG)来编码控制和数据的依赖关系。第三,它从EFG中提取逻辑关系并将其存储到数据库中。第四,它使用用户特定的逻辑规则(称为检测规则)来查询数据库。TXSPECTOR支持用户定义的任意检测规则,这使他们能够研究自己感兴趣的任何方面。据我们所知,TXSPECTOR是第一个对以太坊交易进行字节码级、逻辑驱动分析的通用框架。
作为概念证明,我们应用TXSPECTOR来检测利用(i) 重入漏洞 (ii)未检查调用漏洞和 (iii)自杀漏洞的以太坊攻击。我们对真实世界的以太坊交易进行的实证评估结果表明,TXSPECTOR能够以较低的假阳性率检测交易中的攻击。我们还对TXSPECTOR标记的交易进行了取证研究,发现了这些攻击的几个有趣发现。除了这三个重点漏洞的利用,我们还在附录A中介绍了一些读者感兴趣的其他漏洞的检测规则,如时间戳依赖漏洞、误用原点漏洞和FailedSend漏洞等。
贡献。简而言之,我们在本文中做出了以下贡献:
- New framework 我们提出了TXSPECTOR,这是第一个通用和逻辑驱动的框架,用于在字节码级别检查以太坊交易中的真实攻击。
- Comprehensive evaluation 我们评估了TXSPECTOR在检测三种类型的利用相应智能合约漏洞的攻击。
- Novel application 我们展示了一些作为取证分析工具的TXSPECTOR的一些使用案例,并对真实世界的以太坊交易进行详细的安全分析。
- Open source 为了方便交易相关分析的后续研究,我们将TXSPECTOR以开源许可证的形式提供给研究界。
II. Background and Related Work
在本节中,我们首先提供与以太坊相关的必要背景(S 2.1),包括智能合约和交易,然后介绍相应的相关工作(S 2.2),以阐述我们研究的动机。
A. A Primer on Ethereum Smart Contract
智能合约是一个通用的程序,在区块链上执行。它可以利用三个内存区域在执行过程中进行数据操作:堆栈、内存和存储。一个(数据)堆栈是一个虚拟堆栈,可以用来存储数据。请注意,EVM也有一个调用栈。它与数据栈不同。内存是一个在运行时分配的可由字节寻址的区域。存储器是一个键值存储,将256位的字映射到256位的字。堆栈和内存都是易失性的,这意味着每次执行后存储的数据会被清除。然而,存储是持久性的,可以用来跨交易存储数据。因此,存储操作的gas要比堆栈和内存操作高得多。
目前,EVM支持超过150个操作码。根据指令操作的目标,它们可以被分为五类:
- Category 1 不对任何数据结构进行操作的操作码(如JUMPDEST)。
- Category 2 执行堆栈操作(如PUSHx)或对堆栈中现有值进行操作(如ADD)的操作码。
- Category 3 从区块链(如TIMESTAMP)或当前交易(如ORIGIN)检索信息的操作码。
- Category 4 读/写内存的操作码(如MSTORE)。
- Category 5 读/写存储的操作码(如SSTORE)。
与比特币类似,以太坊也有一个由以太坊工作者(节点)维护的P2P网络。为了提交交易,用户需要支付一笔名为gas的费用,作为对以太坊工作者执行交易的激励。gas是由以太币衡量的,以太币是与以太坊相关的加密货币。每笔交易所需的gas量是根据它所包含的操作码来计算的。如果没有足够的gas来执行交易,执行将被中止,所有的变化将被恢复。然而,在这个过程中使用的以太币将不会被退还。采用gas还可以防止恶意交易(例如,具有无限循环的交易)危害网络。
在以太坊有两种类型的账户。外部拥有的账户(EOA)和合约(即智能合约)账户。这两种类型的账户都有能力进行以太币转账。它们之间的主要区别是,智能合约有可以被执行的相关字节码,而EOA没有任何代码。在以太坊,交易是由EOA触发的。有三种类型的交易:
- Type 1 在EOA之间传输以太。
- Type 2 在以太坊上部署一个新的智能合约。
- Type 3 执行已部署合约的一个功能。
以太币转账交易不涉及智能合约;也就是说,在处理这些交易时,不存在代码执行。然而,为了部署一个新的智能合约或执行一个智能合约的功能,EVM需要执行相关的字节码。
与传统的计算机程序类似,智能合约也包含bug。其中一些可能会被恶意攻击者利用,这些bug被称为漏洞。在智能合约上发现了许多漏洞。为了利用这些漏洞,攻击者需要精心设计智能合约,并发布针对漏洞的交易。因此,攻击与特定的交易有关,而攻击的根本原因是智能合约的漏洞。
B. Related Work
Analysis of transactions 之前很少有研究对以太坊进行基于交易的安全分析。SEREUM在交易执行过程中执行动态污点跟踪,以检测各种重入攻击(例如,跨功能重入、委托重入和基于创建的重入)。它只关注重入攻击,这促使TXSPECTOR支持定制的检测规则来检测其他各种攻击。ECFCHECKER是另一个交易分析工具,它可以检测智能合约的执行是否有效回调(ECF),这个属性对于不容易受到重入攻击的智能合约是成立的。ECFCHECKER的重点是智能合约中的重入漏洞,而TXSPECTOR是一个揭开交易中的攻击的工具。Perez等人最近对21,270个脆弱的智能合约及其相关交易进行了调查。虽然也采用了基于Datalog的方法,这给TXSPECTOR带来了启发,但他们研究的重点是对智能合约漏洞的调查;他们只分析了与其他工具标记的智能合约有关的交易。而TXSPECTOR是一个对交易进行攻击检测和取证分析的工具,它不依赖于智能合约。此外,TXSPECTOR支持自定义规则,这超越了现有的漏洞和攻击。
Analysis of smart contracts 符号执行工具,如OYENTE、MAIAN、SECURIFY、TEETHER、MYTHRIL和MANTICORE,已经被开发用来检测智能合约的特定漏洞和错误。虽然符号执行是一种发现漏洞的强大方法,但它受到路径爆炸问题的影响,而且不能很好地扩展。虽然没有使用符号执行,但SLITHER执行数据流分析和污点分析来检测solidity程序的漏洞。SLITHER也受到了其他静态工具的限制。与我们最接近的是VANDAL,这是一个静态分析框架,从智能合约字节码中提取逻辑关系,进行基于逻辑的分析。VANDAL研究的是智能合约,而TXSPECTOR分析的是交易。为了研究交易中包含的动态信息,TXSPECTOR必须克服一些技术上的挑战(例如,追踪参数的真实值),这些挑战将在以下章节中详细介绍。最近,ETHBMC被提出来,使用基于符号执行的有界模型检查来检查智能合约代码。EthBMC可以捕获合约间的关系、加密哈希函数和记忆式操作,它可以用来检测自杀式和不安全的平衡漏洞。
Formal verification of smart contracts Bhargavan等人提出了EVM*和Solidity*,它们可以将智能合约的源代码和字节码翻译成可以正式验证的F*程序。ZEUS是一个用于分析智能合约安全属性的框架。它将智能合约翻译成LLVM IR,添加验证谓词,并将其反馈给验证引擎进行验证。KEVM是EVM的第一个完全可执行的形式化语义,它是用K框架实现的。Park等人扩展了KEVM并增加了一些优化。
Summary 表1将TXSPECTOR与这些相关工作进行了比较。虽然像OYENTE和ZEUS这样的静态工具能够识别智能合约中的一个或多个漏洞,但没有一个能够适用于所有的漏洞。此外,它们不能够检测交易中的攻击。SEREUM和ECFCHECKER等动态工具可以检测Ethereum攻击,但它们只针对重入攻击。相比之下,TXSPECTOR能够检测各种攻击并对以太坊交易进行取证分析。动态信息在TXSPECTOR中至关重要,这也是TXSPECTOR与其他静态分析工具的最大区别。然而,TXSPECTOR不能检测一些攻击/漏洞,这将在S 7.4中讨论。
III. TXSPECTOR Overview
Objectives TXSPECTOR是一个软件框架,用于对以太坊交易进行逻辑驱动的分析,以发现攻击和漏洞,有三个目标。首先,它被设计为以太坊交易的通用分析框架,而不是为检测以太坊的特定攻击而定制。为此,它逐渐将交易转换为抽象,而不丢失原始交易的重要信息。第二,它很灵活,可以通过定制检测规则,扩展到多个方面的交易分析,甚至包括非安全相关的分析。第三,TXSPECTOR还被设计成具有性能的。尽管使用逻辑驱动的框架不可能实时进行通用的攻击检测,但我们已经做出努力,大大减少使用TXSPECTOR进行分析的存储和性能开销。
Scope TXSPECTOR的重点是根据给定的检测规则,检测来自以太坊交易的攻击。由于执行交易基本上是执行多个智能合约的字节码片段,TXSPECTOR也可以作为副产品揭示智能合约的漏洞。因此,TXSPECTOR能够识别区块链中通过交易发生的攻击,以及与这些交易相关的脆弱智能合约。然而,TXSPECTOR并不是为了检测智能合约中的漏洞,这也是大多数静态分析工具的目标。
Overview TXSPECTOR由四个组件组成(图1)。痕迹提取器(Sec 4)执行以太坊交易并生成字节码级别的痕迹,这些痕迹被存储在痕迹数据库(DB)中,以便进一步处理。然后由执行流图生成器(Sec 5)解析字节码级别的轨迹,以构建执行流图(EFG)。逻辑关系生成器(Sec 6)遍历这个EFG以提取数据和控制的依赖关系,然后将它们表达为逻辑关系,并存储在逻辑关系数据库中。最后,攻击检测器(Sec 7)将用户指定的检测规则作为输入,查询逻辑关系数据库并输出最终的攻击报告。
IV. Trace Extractor
Trace Extractor在以太坊虚拟机(EVM)内执行以太坊交易,并在执行过程中记录字节码级的痕迹。一个字节码级的痕迹是一个3元组的序列。对于EVM执行的每个字节码的操作码,Trace Extractor将其在EVM中的程序计数器(PC)、操作码及其参数(ARGS)记录为一个3元组,{<PC>; <OPCODE>; <ARGS>},其中PC用于通过它们在字节码中的相对位置来识别操作码。为了减少数据冗余,Trace Extractor只记录不是由堆栈产生的参数。因为一个交易可以间接地调用多个智能合约,一个字节码级别的执行痕迹可以从一个或多个智能合约的执行中产生。交易的元数据也被记录下来,如交易接收者的地址和交易的时间戳。
为了重放以太坊交易并收集痕迹,我们修改了Go-Ethereum EVM(1.8.0版)以提取交易痕迹并将其与相关元数据一起存储在Trace DB中。不是所有的操作码都需要被记录。类型1交易(定义于S2.1)是EOAs之间的交易,没有与之相关的字节码。因此,跟踪提取器只记录类型2和类型3的交易。
Modifications for Go-Ethereum EVM 为了记录List 1中所示的交易痕迹,我们修改了Go-Ethereum EVM以记录操作码的相关信息。更具体地说,修改后的EVM记录了以下三种类型的操作码参数:
- Blockchain/transaction related operations (Category 3 defined in S2.1) 这种类型包括需要从区块链或当前交易中获取数据的操作码。例如,TIMESTAMP获取当前区块的Unix时间戳;CALLER获取调用者的地址。
- Memory/Storage related operations (Category 4 and 5) 在这里,Trace Extractor只记录从内存/存储空间读取数据的操作码,即MLAD和SLAD。注意,没有必要记录MSTORE和SSTORE的参数,因为它们只需要来自堆栈的数据,这些数据可以从跟踪的其他部分获得。
- PUSH operations 这一类包含所有的PUSH操作码,如 P U S H i , i = 1 , ⋅ ⋅ ⋅ , 32 PUSH_{i}, i = 1,··· ,32 PUSHi,i=1,⋅⋅⋅,32
对于其余的操作码,它只记录PC值和操作码本身,因为不需要记录参数。
Example traces 由Trace Extractor记录的痕迹与反编译的EVM字节码相似。具体来说,一个痕迹是一个3元组的序列,{<PC>; <OPCODE>; <ARGS>}。清单1中显示了一个交易痕迹片段。痕迹和反编译字节码之间的主要区别是,记录的痕迹也包含交易中使用的真实值。
Trace DB 痕迹数据库存储了执行交易时记录的字节码级痕迹和相关元数据。具体来说,由于多个智能合约可能被调用,每一个交易的痕迹可以涉及一个以上的智能合约,元数据包括以下信息:(i)交易发送方,(ii)交易接收方,以及(iii)交易时间戳(即交易被纳入区块的日期和时间)。
V. Execution Flow Graph Generator
为了更明确地表达控制流,执行流图生成器建立了执行流图(EFG),将跟踪的控制和数据流信息编码成图。由于字节码级的跟踪是由事务生成的,在EFG中没有未解决的分支。因此,在每个智能合约中,执行流是有顺序的。EFG中的一个节点代表一个智能合约的执行,它包含该合约产生的字节码级别的执行轨迹。EFG中的一条边代表了从一个智能合约到另一个智能合约的控制流转移。
执行流图生成器解析字节码级的跟踪,构建执行流程图(EFG)。由于跟踪是动态生成的,所以没有未解决的分支,每个JUMP只有一个目的地。EFG中的节点和边是以下列方式创建的。
- Node 当执行流程从一个智能合约改变到另一个智能合约时,执行流程图生成器会生成一个新节点。具体来说,当遇到与CALL相关的OPCODE,即CALL / DELEGATECALL / CALLCODE / STATICCALL,或遇到与STOP相关的OPCODE,即STOP / REVERT / RETURN,就会生成一个新的节点。
- Edge 当执行流从一个智能合约转移到另一个时,执行流图生成器将生成代表两个节点之间控制流的边。有两种类型的边。第一类是一个从调用者合约到被调用者合约的边。第二类是一个从被叫者合同到叫者合同的边。
一个涉及三个智能合约的EFG例子如图2所示:智能合约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参数,代表当前OPCODE在EFG中的索引。
- Depth 当处理一个有大量外部调用的痕迹时,知道每个操作码处于哪个调用级别是很重要的。为此,我们引入了深度,它描述了EFG中每个操作码的调用深度。每当遇到一个与调用有关的操作码时,深度增加1;当它返回时,深度减少1。
- Callnum 表示EFG中每个OPCODE之前发生的调用次数。它是一个不递减的值:当遇到一个与调用有关的OPCODE时,它将增加1。
VI. Logic Relation Builder
逻辑关系生成器首先解析EFG以构建适合我们分析的中间表示法(IR),然后通过定义逻辑规则提取表达交易语义的逻辑关系。之后,逻辑关系被存储在数据库中。特别是,逻辑规则被定义为表达控制流和数据流信息,以获得交易中的控制和数据依赖关系。例如,有些规则规定了操作码的执行顺序,这与控制流有关;有些规则跟踪操作码的参数如何定义和使用,这与数据流有关。为了实现这一点,逻辑关系生成器为每个操作码生成逻辑关系,如代表其操作数的寄存器,以及它们的PC值。同时,它将事务中的实际值与寄存器联系起来,这样就可以捕捉到动态信息。因此,控制和数据的依赖关系被编码为逻辑关系,然后被组织并存储在数据库中。
Converting Trace-based EFG to IR TXSPECTOR采用了VANDAL中的IR规范。这个IR是一种基于寄存器的语言,它是表达数据和控制依赖关系的另一种形式。IR用寄存器代替了堆栈操作。例如,清单1中的例子跟踪片段的相应IR显示在清单2中。因此,我们在以下两个方面对VANDAL进行了扩展。首先,我们需要处理实值而不是符号值。在逻辑关系生成器中,通过使用具有真实值的寄存器模拟EVM堆栈操作来实现这一点,这样寄存器的值就会相应地更新,所有的中间值都会被正确地记录下来。这是捕捉交易执行过程中所有动态信息的关键步骤,这是静态分析工具无法实现的。例如,在处理TIMESTAMP时,字节码级跟踪中记录的真实时间戳值被推入堆栈并分配给其相关寄存器。第二,我们需要处理合约间的调用。对于EFG中的I-类边,当前堆栈被封存,并创建一个空堆栈。对于EFG中的II-类边,当前堆栈被删除,最后密封的堆栈被恢复。
Generating logic relations from IR 受VANDAL的启发,TXSPECTOR采用并扩展了其逻辑规则,以处理真实值和多个智能合约的痕迹。TXSPECTOR中使用的规则如图3所示。例如,关系操作码将一个操作码与一个PC和它的idx联系起来。交易中使用的真实值是由价值关系提取的,它记录了寄存器和相关的值。每个操作码和它相关的寄存器也被提取到逻辑关系中。例如,SSTORE的逻辑关系记录了EFG中所有的SSTORE和与之相关的元组({pc,registers,idx,depth,callnum})。
表2列出了逻辑关系的一个例子,它代表了图2所示EFG中的PUSH1操作码。特别是,表中的第1行和第2行来自节点a;第3行和第4行来自节点b,它们的深度已经从1变为2,callnum从0变为1。第5行来自节点c,它的深度从2变成了1,因为调用返回,但它的callnum保持不变,因为调用的数量没有变化。
VII. Attack Detector
攻击检测器是TXSPECTOR的关键组件,它将用户指定的查询规则(称为检测规则)作为输入,并查询由逻辑关系生成器生成的逻辑关系数据库,以推理交易的特定安全属性。一旦生成了逻辑关系数据库,它就可以用于不同类型的分析;不需要为每个检测规则重新构建一个新的数据库。输出结果不是对特定查询的简单的是或否的答案;相反,如果检测到攻击,也会提供关于攻击的详细信息,以便进一步分析。
我们选择使用Soufflé构建攻击检测器,它是一种最先进的Datalog查询工具,具有很高的性能。因此,在TXSPECTOR中,检测规则是用Soufflé编写的,它是Datalog语言的一个变种。在这一节中,我们展示了如何构建检测规则,用三个例子来检测交易中的攻击。Re-entrancy、UncheckedCall和Suicidal。我们还在附录A中介绍了另外三个检测规则,用于检测时间戳依赖性、误用原点和失败发送攻击,供感兴趣的读者参考。
A. Rules for Re-entrancy Attacks
Description 作为最严重的攻击之一,重入式攻击针对的是以太坊智能合约,因为重新进入的智能合约可能会多次转移以太币。当合约A调用合约B时,合约B可能在同一交易中再次进入合约A。如果合约B是恶意的,它可能会利用合同A的中间状态(例如,过时的账户余额)从A那里窃取以太币。这种类型的攻击被称为重入式攻击,因为它们是由在同一交易中重新进入调用者合同(合同A)引起的。最臭名昭著的重入攻击是DAO攻击,攻击者从DAO合约中窃取了大量的以太币(价值超过5000万美元)。
我们的目标是设计检测规则来检测SEREUM中提到的高级重入攻击。如果在重新进入和返回受害合约后,有一个状态变化(即存储变量的更新),并且这个存储变量影响到重新进入受害者合约时的控制流决策,那么将导致不一致的状态。
Requirements 假设合约A是受害者,合约B是攻击者。我们定义四个阶段。
- Phase 1 A执行它的代码(1.1阶段)并调用B(1.2阶段);B再次调用A,重新进入A(1.3阶段)。重新进入的A被表示为A’。
- Phase 2 A’执行其代码,然后再返回到B。
- Phase 3 A’返回到B(3.1阶段),而B返回到A(3.2阶段)。
- Phase 4 A继续执行。
交易至少要有这四个阶段才能进行重入攻击,而至少有一个不一致的状态是重入攻击的必要条件。图4显示了一个具有这四个阶段的例子。鉴于四个阶段,不一致状态有两个要求:(i)SLOAD-JUMPI依赖。在第二阶段,A’为控制流决策(即JUMPI指令的条件)加载(使用SLOAD)一个存储变量(V);(ii)SLOAD-SSTORE依赖性。在第四阶段,A更新(使用STORE)存储变量V。如果一个事务满足这两个要求,很明显,在第二阶段,A’为控制流决策从一个不一致的状态加载V。因此,B能够通过重新进入A来操纵控制流,从而发起一个重入攻击。
Detection Rules 我们根据不一致状态的要求来定义我们的检测规则。更具体地说,我们定义了以下检测规则(如图5所示):
检测规则首先检查SLOAD-JUMPI的依赖性。如果一个由SLOAD(sloadVal)加载的值被用于一个JUMPI的条件中,SLOAD地址(sloadAddr)会被获得。SLOAD和JUMPI应该有相同的深度和callnum(在S5中定义),JUMPI的条件(jumpiCond)应该依赖于SLOAD(sloadVal)的值,这是由依赖检测规则执行的。depends(A, B) 检测规则检查是否存在从A到B的数据流。
接下来,检测规则检查SLOAD-SSTORE的依赖性。首先,他们检查是否有一个在地址(sloadAddr)上工作的SSTORE。如果有的话,它们会检查(i)这个地址是否已经被满足第一个条件的SLOAD通过checkSameAddr检测规则使用,(ii)SSTORE是否在SLOAD之后执行,以及在外部调用返回之后(分别通过filterByDepth检测规则和filterById检测规则检查idx和深度)。checkSameAddr检测规则检查SSTORE和SLOAD是否有相同的地址。filterByDepth检测规则保留sloadDp大于sstoreDp的SLOAD和SSTORE对。filterByIdx检测规则保留sloadIdx小于sstoreIdx的SLOAD和SSTORE对。此外,他们还检查SLOAD、SSTORE和JUMPI是否在同一个合约中(通过checkSamecontract检测规则)。如果是,那么就会检测到不一致的状态,这表明有重入攻击。
B. Rules for UncheckedCall Attacks
Description 由于缺乏对外部调用的返回值的检查,一个UncheckedCall攻击可以被利用来窃取以太币。具体来说,在以太坊智能合约中,CALL操作码经常被用于合约间通信和加密货币转移(即发送功能)。
在外部调用过程中,可能会发生异常,这将导致被调用者合约恢复其执行并返回。理想情况下,调用者应该检查调用的返回值。如果它是零(例如,由调用过程中的异常引起的),它应该采取行动(例如,恢复其执行)来正确处理异常。然而,许多开发者并没有进行这样的检查。因此,这些合同有UncheckedCall漏洞,导致钱被盗。
Requirements 我们改编了字节码分析工具SECURIFY的检测标准,来定义UncheckedCall攻击的要求。包含UncheckedCall攻击的事务应满足以下要求。(i) 外部调用。交易中至少有一个外部调用(CALL相关的OPCODE)。(ii) 未检查的调用返回值。至少有一个外部调用,其返回值不被任何JUMPI使用。
至少有一个未检查的返回值是UncheckedCall攻击的一个必要和充分条件。更具体地说,在字节码层面,检查返回值是根据CALL的返回值由一个JUMPI完成的。因此,如果在一个事务中,有调用的返回值没有被任何JUMPI使用,这意味着这个值没有被检查,这个事务处于UncheckedCall攻击之下。
为了检测UncheckedCall攻击,我们在图6中定义了我们的检测规则。UncheckedCall检测规则首先提取交易中的所有调用返回值(第7行),并使用jumpiDep检测规则检查是否有一个依赖于每个调用返回值的JUMPI。如果有一个呼叫返回值没有被任何JUMPI使用,该交易被标记为未检查调用。注意,一个交易可能包括来自多个合约的操作码。在我们的检测规则中,CALL和JUMPI的深度都被设置为1,所以只有针对接收方合约的UncheckedCall攻击才会被检测出来。
C. Rules for Suicidal Attacks
Description 由于缺乏适当的权限检查,"自杀式"攻击可能导致智能合约被任何人而不是合约所有者杀死。具体来说,以太坊为智能合约提供了通过SELFDESTRUCT操作码将自己从区块链上删除的能力。虽然SELFDESTRUCT的设计是为了让合约所有者管理他们的智能合约的生命周期,但一些智能合约在调用SELFDESTRUCT之前未能添加适当的权限检查。由于TXSPECTOR检查交易而不是智能合约,它可以检测到未经授权的用户触发智能合约SELFDESTRUCT的攻击。每个合约最多可以被检测到一次,因为它之后已经被销毁。
Requirements 通过检查在执行SELFDESTRUCT之前是否有权限检查(即JUMPI),可以发现自杀式攻击。有两个要求。(i) SELFDESTRUCT。事务中至少有一个SELFDESTRUCT。(ii) 对CALLER没有权限检查:没有依赖CALLER的JUMPI。
如果交易中没有CALLER-JUMPI依赖关系,意味着它在执行SELFDESTRUCT之前没有检查msg.sender(CALLER),这进一步表明合约可以被任何人杀死,也就是自杀式攻击。
Detection Rules 检测自杀式攻击的检测规则如图7所示。检测规则首先确保交易中至少有一个SELFDESTRUCT,然后通过jumpiDep检测规则(第3-4行)检查msg.sender是否在SELFDESTRUCT之前被检查。
D. Rules for Other Attacks
除了利用上述三个漏洞的攻击,我们还展示了使用TXSPECTOR检测其他类型的攻击,如时间戳依赖(S A.1)、误用原点(S A.2)和失败发送(S A.3)。然而,并非所有已知的攻击/漏洞都能被TXSPECTOR检测到。例如,交易顺序依赖涉及多个交易,但TXSPECTOR对单个交易进行分析;限制性转移在交易中无法观察到;检测整数溢出/下溢需要源代码级别的信息(如类型),这在字节码中是缺失的。请注意,我们已经在表1中总结了这些攻击/漏洞。
VIII. Evaluation
在这一节中,我们首先解释了我们的评估设置,并在应用第7节中提到的3个检测规则检测交易中的攻击时对TXSPECTOR进行评估。
A. Experiment Setup
Trace collection 痕迹是在微软Azure云[32]上的L8s v2实例上收集的,有8个VCPUs,64GB内存和2TB SSD,运行Ubuntu 18.04。Trace Extractor运行一个完整的Ethereum节点来收集字节码级的痕迹,从第0个区块到第7,200,000个区块。请注意,以太坊有大约10180000个区块(截至2020年6月1日),并且它一直在以指数形式增长。我们不能为我们的实验收集所有的区块,因为这需要大量的存储和处理时间。因此,我们在第7,200,000个区块时停止收集,这导致了1,577 GB的大小,总共包含397,269,533个交易。我们通过Trace Extractor将它们存储在Trace DB(在MongoDB[33]上实现)。
Dataset 在收集了区块交易的痕迹之后,我们就可以从中推导出交易,这就是TXSPECTOR的输入。鉴于我们有大量的区块,我们不能把所有的区块都用来推导交易,因为一个区块可能包含多个交易。因此,我们决定只关注从第7,000,000个区块开始的交易作为我们的数据集,其中包含16,485,279笔交易,涵盖2019年1月至2019年2月的交易。有了这样的1600万笔交易,我们相信它是有代表性的,可以涵盖我们实验的各种情况。
Logic relation generation 我们与逻辑关系生成相关的数据集包含9,661,593个交易,这是通过两个步骤获得的。首先,该数据集最初包含16,485,279个交易。然而,并非所有交易都有痕迹;也就是说,它们没有调用智能合约的执行。我们必须把它们过滤掉,因为我们的逻辑关系生成过程只把有痕迹的交易作为输入。在过滤之后,还剩下9,662,675个交易。第二,对原始痕迹进行处理以生成逻辑关系。然而,由于超时,并非所有的交易都能被处理。因此,我们将超时阈值设置为60秒,并通过执行流程图生成器和逻辑关系生成器处理每一个交易,以生成逻辑关系。不幸的是,1,082个交易(0.01%)没有按时完成逻辑关系的生成。因此,最终我们的最终数据集包含9,661,593个交易。逻辑关系数据库需要2,949GB的空间。
大部分的逻辑关系是在很短的时间窗口内产生的。具体来说,只有94277个(1.0%)交易的处理时间大于4s。我们在图8中绘制了在4s内完成处理的事务(9,662,675个事务的99.0%)的处理时间分布。大约60%的事务在1s内完成了逻辑关系的生成。如果我们把超时阈值设置为2s,大约90%的事务的逻辑关系可以生成。生成一个交易的逻辑关系平均需要1.03秒。请注意,对于每个交易,我们只需要生成一次逻辑关系,无论在攻击检测器中应用多少个检测规则,因为不同的检测规则使用相同的逻辑关系。
生成逻辑关系后,我们应用检测规则来检测交易中的攻击和漏洞。攻击检测器的超时阈值被设置为1s。我们在S 8.6中研究了逻辑关系超时和检测规则超时的原因。
Evaluation steps and criteria 在评估过程中,我们做了几个步骤,还比较了其他静态分析工具的结果。我们首先应用TXSPECTOR来标记基于检测规则的交易。对于被标记的交易,如果接收方智能合约的源代码可用,我们就进行人工检查,以检查它们是否容易受到特定攻击。接下来,如果一个智能合约是脆弱的,与这些合约相关的被标记的交易被认为是真阳性。否则,相关交易被认为是假阳性。由于交易量很大,我们没有办法分析负面结果。其他相关工作也面临同样的问题(如SEREUM)。最后,我们还将TXSPECTOR与三个基于Datalog的静态分析工具进行比较。SECURIFY、VANDAL和GIGAHORSE,如果特定的漏洞能被这些工具检测到的话。
B. Results of Re-entrancy Attacks
首先,我们介绍了重入攻击的检测结果。我们对数据集中的9,661,593个交易应用了重入检测规则。336,909笔交易(3.5%)由于超时而没有完成。在有判决的9,321,684个交易中,TXSPECTOR将3,357个交易(0.04%)标记为重入攻击。这3,357个交易与30个智能合约有关,其中22个是开源的。我们使用在线Solidity反编译器[19]反编译了8个闭源合约。经过我们的人工检查,我们确认22个开源合约中的10个和8个闭源合约中的7个包含了重入漏洞。TXSPECTOR误报13个智能合约的主要原因有两个:(i)它未能检测到禁止未经授权重新进入重入函数的锁;(ii)被误报的合约可以被重新进入,但不可能从中窃取以太币或代币。一个误报的例子是交易0xd32496。清单3中显示了相关函数的代码片段,它使用一个锁来防止未经授权的重入尝试。锁变量(reentrancyLock)在调用前被检查,并在调用后被更新,这使得合约只能被重新输入一次。因此,一个实际的攻击被阻止了,但是事务的执行符合我们对重入攻击的要求(S 7.1)。因此,它是一个假阳性。
检测结果与SEREUM相当,SEREUM在49,080个交易中错误地标记了46,743个(在总共77,987,922个交易中),产生了0.06%的假阳性率。按照同样的标准,TXSPECTOR错误地标记了3,072个交易(与13个智能合约有关),产生了0.03%的假阳性率。然而,我们相信这些数值只是检测准确性的近似值,因为不可能计算出真正的阴性。
为了将TXSPECTOR的检测结果与其他工具进行比较,我们或者向相应的作者寻求帮助和澄清,或者用相同的数据集运行开源的工具。结果总结在表3中。我们在下文中详细介绍了这些比较。
Comparison with SEREUM SEREUM没有开源版本;但我们联系了SEREUM的作者,并获得了他们的评估结果以进行比较。对于同一数据集,SEREUM将10278个交易标记为重入攻击,其中2732个交易也被TXSPECTOR标记。对于剩下的7,546个交易,我们发现其中7,271个交易由于超时而没有在我们的数据集中出现结果1。有625个交易被TXSPECTOR标记,但没有被SEREUM标记。虽然我们的人工检查表明它们导致了不一致的状态,但我们不明白为什么它们没有被SEREUM识别。
Comparison with SECURIFY 我们与静态分析工具SECURIFY进行了比较。请注意,SECURIFY旨在检测智能合约的重入漏洞,而我们的重点是交易。我们首先提取了数据集中所有交易的接收方智能合约,然后在这些智能合约上应用SECURIFY。在我们的数据集中,接收器智能合约的总数为105,535个。对于被TXSPECTOR标记的3,327个交易,只有30个接收方智能合约。我们在105,535个接收方智能合约上运行开源版本的SECURIFY。分析每个合约的超时阈值被设置为60s。其中1315个合约由于超时而没有完成;6226个合约由于运行时错误而没有结果。对于剩下的97994个智能合约,SECURIFY将其中的1196个合约标记为重入,没有一个合约被TXSPECTOR标记。
在阅读了SECURIFY的源代码后,我们发现它定义了两种重入,"依赖gas的重入"和 “恒定gas的重入”。但在我们的定义中,我们检查的是不一致的状态,这需要在调用后进行状态更新。因此,我们得出结论,TXSPECTOR导致了与SECURIFY不同的检测结果,因为它们有不同的检测重入的标准。检测规则也可以被定义为使用TXSPECTOR来检测这些类型的重入攻击(详情见A.4节)。值得注意的是,SEREUM也提到,“Securify为重入检测定义了一个非常保守的违规模式,禁止在外部调用后进行任何状态更新”,因此导致了 “非常高的假阳性率”。
Comparison with VANDAL 我们使用VANDAL的开源版本进行比较。分析每个合约的超时阈值被设置为60s。当分析105,535个接收方智能合约时,其中1206个(1.1%)没有在60秒内完成;225个(0.2%)由于一些运行时错误而没有结果。对于剩下的104,104个智能合约,VANDAL将85,721个(82.3%)标记为重入,这显然是不合理的。我们随机选择了一些检测到的智能合约,发现它们都是假阳性。由于被标记的合约数量巨大,我们无法对所有的合约进行人工检查。
通过检查VANDAL提供的规则,我们发现其规则比我们的要宽松的多。根据他们的论文,"如果一个调用转发了足够的gas,并且没有受到突变器的保护,那么这个调用就被标记为可重入。因此,任何有足够气体且没有锁的调用都会被VANDAL标记为可重入,这是一个更宽松的标准。在30个被TXSPECTOR标记的智能合约中,27个也被VANDAL标记。对于剩下的3个,由于超时,VANDAL没有完成对它们的分析。因此,TXSPECTOR优于VANDAL,因为它导致了低假阳性率。
Comparison with GIGAHORSE GIGAHORSE没有开源版本,但有一个网站供用户查询GIGAHORSE的结果。我们从他们的网站上提取了"重入"的所有结果。在我们的数据集中的105,535个接收智能合约中,有3,310个(3.1%)被GIGAHORSE标记为可重入。在TXSPECTOR检测到的30个智能合约中,有18个也被GIGAHORSE标记;其余12个不被GIGAHORSE认为是有漏洞的。根据FAQ页面上的解释,只有当 "合约进行外部调用,而该调用本身可以在第一次调用更新存储之前重新进入合约"时,智能合约才会被GIGAHORSE认为是可重入的。因此,它的标准比TXSPECTOR定义的不一致状态要严格的多。对于被GIGAHORSE标记的3292个智能合约,但没有被TXSPECTOR标记,主要原因是在我们的数据集中没有显示不一致状态的交易。
Case study of the DAO contract 尽管TXSPECTOR在检测某些类型的重入攻击方面有所不足(与SEREUM相比),但我们表明它在检测最突出的攻击方面仍然有效,例如DAO攻击。DAO合约是重入攻击的最初发生地。它是重入式攻击的头号受害者。价值超过5000万美元的以太币从DAO被盗。为了避免损失,以太坊社区决定对区块链进行硬分叉,以归还被盗资金,这导致了以太坊区块链的分裂。
为了检查可能攻击DAO合约的交易,简单地提取以DAO为接收方的交易是不行的,因为恶意合约不是接收方。相反,我们扫描了由Trace Extractor收集的从区块0到区块7,200,000的每一笔交易的原始痕迹,只保留了那些跟踪包含DAO合约地址的交易。在这个过滤过程之后,还剩下98,914个交易。
我们在98,914个交易上应用TXSPECTOR与重入性检测规则,看DAO合约被攻击了多少次。我们使用之前提到的相同过程来生成逻辑关系,并应用重入检测规则。由于重入交易比普通交易更复杂,我们将逻辑关系超时阈值提高到200s,检测规则超时阈值提高到60s。在这个过程中,仍然有3,665个交易因为超时而没有结果。在剩下的95249个交易中,TXSPECTOR将其中的2108个标记为重入攻击。
为了比较TXSPECTOR和SEREUM,我们检查了这98,914笔交易中有多少是被SEREUM标记的。特别是,SEREUM标记了2,112项交易,其中2,108项也被TXSPECTOR标记。也就是说,所有由TXSPECTOR检测到的攻击也被SEREUM标记了。有4个交易被SEREUM标记,但没有被TXSPECTOR标记。我们手动检查了这4个交易,看看为什么TXSPECTOR没有标记它们。我们检查了SSTORE、SLOAD和JUMPI的逻辑关系,看看是否存在TXSPECTOR遗漏的依赖关系。经过检查,我们确认在这4个事务中,有一对(SLOAD, SSTORE)操作相同的存储地址。然而,这些对具有相同的深度,意味着它们不符合不一致状态的条件。因此,TXSPECTOR没有将它们标记为重入攻击。
C. Results of UncheckedCall Attacks
接下来,我们介绍UncheckedCall攻击的检测结果。在对我们的数据集应用UncheckedCall检测规则后,323,772个交易(3.4%)由于超时而没有完成。在有结果的9,337,821(96.6%)个交易中,TXSPECTOR将178,303个交易标记为UncheckedCall,并且有1,430个相关的接收者合约。其中216个是开源的,它们与28377个交易有关。我们手动检查了这216个智能合约,发现其中213个确实存在UncheckedCall的漏洞。我们进一步调查了TXSPECTOR为什么错误地标记了剩下的3个合同。我们发现,这3个合同对外部调用有检查,但在交易中,由于"out of gas"故障,没有进行检查,所以TXSPECTOR标记了它们。这3个被错误标记的智能合约只与4个交易有关。值得注意的是,对于剩下的1214个闭源智能合约,我们无法对合约进行人工分析。但我们确实确认在他们的痕迹中发现了至少一个{CALL POP},或者他们没有使用至少一个调用返回值,这表明根据我们的检测规则,他们确实是攻击。我们还将结果与SECURIFY和VANDAL的结果进行比较。比较结果总结在表3中。
Comparison with SECURIFY 我们使用SECURIFY检测了我们数据集中105,535个接收方智能合约中滥用UncheckedCall漏洞的攻击。当分析这些智能合约时,其中2,993个(2.8%)没有在60秒内完成。此外,另外3,501份(3.3%)智能合约的分析由于一些运行时错误(例如,索引超限)而没有完成,所以没有结果。经过处理,SECURIFY生成了99,041份(93.9%)智能合约的结果,并将其中2380份标记为存在UncheckedCall漏洞。
我们进一步调查了由TXSPECTOR标记的178,303个交易。我们提取了这些交易的接收方合同(共1404份),并与SECURIFY的检测结果进行比较。有1,183个(84.3%)被TXSPECTOR标记的接收方合同也被SECURIFY标记;另外有142个(10.1%)被TXSPECTOR标记,但SECURIFY由于超时或运行时错误而没有完成执行;有79个智能合约被TXSPECTOR标记,但被SECURIFY标记为安全。在执行这79个合约所产生的字节码级别的痕迹中,调用返回值是由调用者合约弹出的,所以痕迹中没有JUMPI-CALL依赖。我们猜测,SECURIFY没有标记这些合同的原因可能是与它使用的符号执行方法有关的问题。
有大约1200个智能合约只被SECURIFY标记,但没有被TXSPECTOR标记。我们检查了其中一些,发现智能合约中存在UncheckedCall漏洞,但有漏洞的函数并不包括在交易中。因此,TXSPECTOR没有检测到它们。
Comparison with VANDAL 当使用VANDAL分析105,535个接收器智能合约时,其中1151个(1.1%)没有在60秒内完成。对于被TXSPECTOR标记的1403个智能合约,其中1367个(97.4%)也被VANDAL标记;其余36个没有被VANDAL识别。通过我们的人工检查,我们发现这36个智能合约有UncheckedCall的漏洞。其中一个例子是合约0x99ECA32。在这个合约中,transfer()函数的返回值没有被检查,这表明有UncheckedCall漏洞。因此,TXSPECTOR能够识别被VANDAL遗漏的脆弱智能合约。
VANDAL将另外91,012份智能合约标记为UncheckedCall漏洞。TXSPECTOR没有检测到这些智能合约,原因是覆盖范围:有漏洞的功能不包括在我们数据集的交易中。
D. Results of Suicidal Attacks
最后,我们展示了自杀式攻击的检测结果。在对我们的数据集应用自杀式检测规则后,327,208个交易(3.4%)由于超时而没有完成。在有结果的9,334,385(96.6%)个交易中,TXSPECTOR将23个交易标记为自杀式攻击。其中,只有18个接收方智能合约,因为其中5个交易的接收方地址为0x0,这意味着它们在创建后立即被杀死。我们无法研究它们的源代码,因为它们的字节码和存储在被杀死时从区块链上被删除了。从这23个交易的痕迹中,我们确认没有对调用者(msg.sender)进行权限检查。因此,TXSPECTOR并没有产生假阳性。我们还将其结果与VANDAL和GIGAHORSE的结果进行了比较。表3中总结了比较结果。
Comparison with VANDAL 我们在数据集中的所有105,535个智能合约上运行VANDAL,以检查其中有多少有自杀式 漏洞。VANDAL将其中349个标记为脆弱。在18个被TXSPECTOR标记的智能合约中,有13个也被VANDAL标记。对于没有被VANDAL标记的5个智能合约,VANDAL由于运行时间错误和超时而未能分析它们。对于只被VANDAL标记的336个智能合约,主要原因是这些智能合约有自杀性漏洞,但它们还没有被杀死(即没有调用函数)。因此,TXSPECTOR没有检测到它们。
Comparison with GIGAHORSE 为了与GIGAHORSE进行比较,我们从他们的网站上检索了他们的"可访问自毁"的结果。在我们的数据集中的105,535个接收器智能合约中,GIGAHORSE标记了383个智能合约,其中只有一个与TXSPECTOR的结果相同。GIGAHORSE没有标记其他17个合约的原因是,它们的字节码在GIGAHORSE部署时被杀死后丢失了,所以GIGAHORSE无法分析它们。对于只被GIGAHORSE标记的智能合约,我们发现他们有一个更宽松的标准:只要SELFDESTRUCT可以从公共入口点到达,它将被标记为True,即使有检查。然而,TXSPECTOR 只检测完全没有检查的自杀式漏洞,这比GIGAHORSE更为严格。
E. Comparison with Other Tools
除了这三个基于Datalog的工具外,我们还与其他三个静态分析工具进行了比较。MYTHRIL,OYENTE,和 MAIAN。对于MYTHRIL,我们在所有三个漏洞上与TXSPECTOR进行了比较;我们在重入性上与OYENTE进行了比较,在自杀上与MAIAN进行了比较。结果总结并列于表4。总结一下,不同工具的检测结果差别很大。主要原因是没有检测这些漏洞的黄金规则,不同的工具使用不同的检测规则。除了开放我们的源代码外,我们还公布了我们的比较结果,以便社区中的其他人可以使用这些数据进行研究。
F. Timeout Analysis
Timeout due to generating logic relations 在生成逻辑关系时,有1082个交易未能在60秒内完成。我们手动检查了这些交易以了解超时的原因。我们发现这些交易大部分都卡在了逻辑关系生成器(S6)中,该生成器将基于跟踪的EFG转换为IR,并对实值进行算术操作。算术运算有时可能过于复杂而无法即时计算(例如exp(a,b)),这就是逻辑关系生成器导致超时的主要原因。一个例子是交易0xf9de18,它有6,945个操作码。而且,它有一个指数很大的exp操作,这导致了超时。
Timeout due to applying Detection Rules 我们分析了应用UncheckedCall检测规则时超过超时阈值的交易。我们发现,大多数交易在寻找调用返回值和JUMPI之间的依赖关系时被卡住了。假设在一个事务追踪中,JUMPI的数量是m,CALL的数量是n,那么将有m∗n个(CALL, JUMPI)对。对于每一对,TXSPECTOR试图通过寻找它们之间的依赖关系来检查调用的返回值是否被JUMPI使用,可能通过许多中间变量。当m∗n很大且跟踪很长时,验证所有m∗n对的依赖关系会花费很多时间。一个例子是交易0xb513f5,它有11664个JUMPI和299个CALL。对于大约350万个(CALL, JUMPI)对,TXSPECTOR需要翻阅所有潜在的中间变量来确认是否存在依赖关系,这是无法忍受的。
Optimizations 为了优化逻辑关系的生成,我们可以从以太坊节点获取这些中间结果,因为它们应该在交易执行过程中存在。为了加快检测规则的应用过程,我们可以添加更严格的修剪规则,以过滤掉那些不可能有依赖关系的配对,然后再试图找到它们。另外,我们可以添加一些辅助函数来存储某些节点的依赖关系,这样就不必每次都重新计算。我们将这些优化作为我们未来的工作。
IX. Application
在本节中,我们展示了TXSPECTOR如何用于对针对以太坊的攻击进行取证分析。
A. Forensic Analysis of Re-entrancy
首先,我们专注于被TXSPECTOR标记为重入攻击的3327个交易。我们仔细检查了这些交易,以研究以下方面:
The number of inconsistent state (NIS) 我们将事务中的NIS定义为TXSPECTOR在查询结果中报告的不一致状态的数量,即在一个事务中操作同一存储地址的不同(SLOAD, SSTORE)对的数量。图9显示了3,327个被标记的事务中的NIS的分布。在这个图中,X轴表示NIS的数量,Y轴表示NIS落在相应范围内的事务数量。我们可以看到,有3245个交易的NIS小于15。超过15个交易有超过100个不一致的状态;有一个交易有575个NIS。
Victim smart contracts 在研究了现实世界中涉及重入攻击的交易后,我们发现典型的攻击工作流程是这样的。(i) 一个外部拥有的账户(A)调用智能合约(B)中的一个函数。A和B都是由攻击者控制的;(ii)B调用另一个智能合约©,它是受害者;(iii)C调用B的回退函数。
在这个工作流程中,B是恶意的智能合约,而C是受害者的智能合约。在以太坊中,攻击交易的发送方(从地址)是A,接收方(到地址)是B。因此,交易的接收方不是受害者;实际的受害者©隐藏在B的字节码或存储中。这一发现略显反直觉。
为了深入研究我们的发现,我们构建了一个新的检测规则来提取处于不一致状态的存储地址的所有者的合同地址。对于一个交易,如果它包含了多个智能合约的字节码,而这些智能合约的状态不一致,那么检测规则就会分别报告这些智能合约的地址以及它们的NIS号码。在查询之后,攻击检测器报告了318个独特的受害者地址,其中前5名显示在表5中。排名第一的受害者智能合约占了4,803个NIS。前2个智能合约加起来贡献了超过8600个NIS数,这超过了所有318个受害者智能合约加起来的NIS数的一半。
Case Study – The DAO contract 一个著名的重入攻击是DAO攻击。因此,我们对DAO智能合约进行了案例研究,重点是检测到的攻击时间和它们的NIS号码。如第8.2节所述,有98,914个与DAO有关的交易。TXSPECTOR将其中2,108个标记为重入攻击。这2,108个交易的分布如图10所示。从这个图中可以看出,大部分对DAO的重入攻击都发生在2016年夏天,这与新闻报道中臭名昭著的DAO攻击[27]一致。我们进一步研究了针对DAO合约的重入交易的NIS。NIS的分布如图11所示。毫不奇怪,DAO上的再入事务的NIS数要大得多。我们可以看到,有超过1700个交易的NIS大于100;超过850个交易有超过500个不一致的状态;甚至有512个交易的NIS大于1700。
B. Forensics Analysis of UncheckedCall
TXSPECTOR将178,229笔交易标记为利用UncheckedCall漏洞的攻击,它们有1404个独特的接收者地址和4125个独特的呼叫者地址。
接收者地址。我们在表6中列出了UncheckedCall交易的前5个接收者地址。排名靠前的接收方智能合约占了超过30,000笔交易。排名前三的接收方智能合约合计贡献了所有UncheckedCall交易的50%。排名第三的智能合约属于HybridExchange,它是一个加密货币钱包,也是一个交换平台。
呼叫者地址。我们在表6中列出了UncheckedCall交易的前5名呼叫者地址。排名第一的呼叫者负责发送了超过24,000个UncheckedCall交易。排名第二至第五的呼叫者各发送了超过7,500次UncheckedCall交易。
C. Forensic Analysis of Suicidal
TXSPECTOR将23笔交易标记为自杀性交易。我们首先调查了这23个自杀性交易背后的原因。回顾一下,Suicidal的主要要求是在SELFDESTRUCT之前没有权限检查。经过调查,我们发现这些原因可以分为两类。
- No permission check at all 交易中根本没有检查。20个交易属于这些类别,意味着它们可以被任何人杀死,正如预期的那样。
- Mistakes in checks 交易中有检查,但它没有检查msg.sender。我们发现有3个事务中含有检查,但它们只检查起源,而不是msg.sender,这实际上是一个Misuse-of-origin漏洞。因此,它们可以被任意的调用者杀死。
接下来,我们检查了被标记的23个交易,以研究呼叫者地址和攻击的受益者。
- Caller Address 我们根据呼叫者地址对这些交易进行聚类,以检查这些交易是否是由同一个呼叫者触发的。有4组具有相同调用者地址的交易,每组中分别有3、2、2、2个交易。我们进一步检查了每一组中智能合约的字节码,并确认它们实际上是相同的。每组中的合约都是由同一个创建者创建的。
- Beneficiaries 当一个智能合约(A)执行SELFDESTRUCT的时候,它需要指定另一个账户(B)的地址。A的剩余以太币将被转入B。因此,我们称B为受益人。我们进一步检查了这23笔交易的受益人地址,常见的受益人如表7所示。有3组交易分别有相同的受益人;最上面的一组是3个Suicidal交易的受益人。
X. Discussion
Time cost 如S8.1所示,生成一个交易的逻辑关系平均需要1.03秒。考虑到以太坊的交易量,实时处理它们将是非常具有挑战性的。TXSPECTOR被设计为交易的取证分析框架,但不打算作为以太坊的实时攻击检测工具。尽管如此,在生成逻辑关系时,有几种方法可以提高TXSPECTOR的性能。例如,可以应用多线程来并行生成多个交易的逻辑关系,因为交易之间不存在依赖性。
Storage cost 存储逻辑关系数据库需要大量的空间。为了节省空间,TXSPECTOR可以采取措施缩减逻辑关系DB的大小。例如,在生成逻辑关系时,可以使用标准的序列化或压缩库(如gzip)。此外,TXSPECTOR可以选择一个操作码子集,只为这些操作码生成逻辑关系,而不是所有的操作码,如果在通过逻辑关系生成器之前知道感兴趣的操作码。
Transaction vs. bytecode 研究交易的好处是,交易包含智能合约如何相互作用的信息。然而,交易的字节码级跟踪只包含智能合约的部分信息;它只涉及在这个交易中被调用的函数。如果一个智能合约中的某个函数从未被其他人调用,就没有与之相关的交易。在这种情况下,交易不能揭示与智能合约的这个功能有关的任何漏洞。尽管如此,如果一个智能合约从未参与任何交易,那么这些漏洞也不会被利用。因此,对于取证分析,分析交易比研究智能合约字节码更有意义。
Reactive approach vs. proactive approach 作为一个攻击检测和取证分析工具,TXSPECTOR检查交易,这在本质上是被动的,这意味着攻击只能在以太坊区块链上发生后才能被检测到。然而,与检测智能合约中可能永远不会被触发的漏洞的主动方法(如静态分析)不同,研究交易可以揭示过去发生的真实攻击,并从取证的角度来学习它们。另一方面,静态分析工具是对TXSPECTOR的补充,因为TXSPECTOR只能看到智能合约字节码的一部分。在TXSPECTOR从交易中发现攻击后,静态分析工具可以用来研究受害者的智能合约,以确定其他潜在的攻击面,以及攻击者的智能合约,以了解攻击机制。
Efforts needed to design new rules TXSPECTOR只能用于对已知的攻击/漏洞进行取证分析。为了想出检测规则,用户需要对她想检测的攻击/漏洞有一定的了解,以及对构建检测规则的基本认识。在TXSPECTOR的开源版本中,我们提供了现有漏洞的规则供用户选择,这样他们就不必重新发明车轮。此外,为了最大限度地减少用户在开发自定义检测规则时需要付出的努力,我们还提供了一个API列表,以及文档/READMEs来帮助用户上手。
Other applications 在本文中,我们表明TXSPECTOR可以用来检测6种不同的攻击。然而,TXSPECTOR的应用超出了检测攻击和漏洞的范围;它可以用来对交易中的许多其他方面进行取证分析。例如,如第9.1节所示,TXSPECTOR可以用来检查一个特定的地址是否参与了交易,如果是的话,对交易进行某些分析。另外,它还可以用来检索某些中间结果以了解交易失败的原因。
XI. Conclusion
我们提出了TXSPECTOR,这是第一个通用的、开源的、逻辑驱动的框架,用于在字节码层面研究以太坊交易。TXSPECTOR支持用户定义的自定义检测规则,以检测以太坊攻击。我们介绍了TXSPECTOR的设计和实现,并演示了用于检测交易中攻击的检测规则的构建。我们的评估表明,TXSPECTOR是有效的。我们还演示了如何使用TXSPECTOR来对交易进行取证分析。