Smart-Contract-VulnerabilitiesVulnerable-Does-Not-Imply-Exploited

slug
series-status
status
summary
date
series
type
password
icon
tags
category

原文链接

Smart Contract VulnerabilitiesVulnerable Does Not Imply Exploited

题目

智能合约漏洞:易受攻击并不意味着就会被利用

摘要

近年来,在智能合约的漏洞领域受到了大家广泛的学术兴趣和实践兴趣,特别是那些针对以太坊区块链开发的智能合约。大家的关注点普遍都放在了这些智能合约如何容易受到攻击,但是在这篇文章中作者将关注点放在了这些易受攻击的智能合约究竟有多少真得被利用了。在作者调查的近期的六个学术项目报告的23327 份脆弱的智能合约中,虽然确实涉及了数目十分巨大的金额,但是自这些合约部署以来,目前只有 1.98%的智能合约真的被利用了,相当于大概8487ETH(约合 170 万美元),或者说仅仅占了300 万 ETH (约合6 亿美元)的0.27%,在本文中作者将通过证明大部分资金实际上集中在了很少数的智能合约中来解释这个数据结果,并证明这些合约在实际的应用中不可利用。

引言

一般来讲,涉及到软件安全性的时候,其漏洞的利用率都是非常难以估计的,然而区块链中的公链,由于其一致性、易访问性以及智能合约具有的可重复访问的日志,为作者的研究提供了技术基础。作者基于此,将以太坊区块链上智能合约报告中的漏洞与合约中实际利用情况做了对比。

贡献

数据记录公式

提出了基于数据日志的模拟方法,用于对以太坊虚拟机(EVM)进行跟踪分析。作者通过这种高度可伸缩的方法去分析了从以太坊区块链到研究漏洞分析的 2000 多万笔交易。值得注意的是,这种方法是基于提取到的事实和定义的文中所述漏洞的规则y自动运行的。

开发的试验评估

文中分析了最近发布的六项学术研究报告中所讲的漏洞,并得出了尽管易受攻击的合约数量和风险金额非常高,但实际开发的金额要低几个数量级的结论。

建议的解释

文中提出了一种假设,造成这种巨大的差异的原因在于与可以开采的 Ether 数量和被标记为易受攻击的 Ether 相比起来数量其实非常低。事实上,对易受攻击的智能合约及其包含的 Ether 的进一步分析表明,大部分 Ether 仅由少数智能合约持有,这些智能合约上报告的漏洞要么是误报,要么是实际中不可用。作者还确认了,以太坊区块链上的所有合约集与作者的数据集中的财富分布相似。

背景

以太坊平台允许用户在其分布式基础设施上运行“智能合约”。以太坊智能合约是一种定义了一套相关基金管理规则的程序,通常用**图灵完备[1]编程语言Solidity编写。Solidity 类似于 JavaScript,其区别在于solidity 是一种强类型语言,并且具有与以太坊平台交互的内置结构,便于与以太坊的内部数据进行交互。Solidity编写的程序被编译成低级的非类型化字节码,以太坊虚拟机(EVM)将在以太坊平台上执行。其中值得注意的是,Solidity 编写的EVM 合约是可以在不使用可靠度[2]**的情况下进行编写的。
要想执行智能合约,我们必须向合约方发起一笔交易,并支付一笔费用,这里的费用来自于智能合约的计算成本(单位为 gas),每执行一条指令,会消耗一个商定的 gas 值,消耗掉的 gas 值将记入拥有包含交易的区块的矿工的账上,未用掉的 gas 值将返还给用户,为了避免无出口的程序造成系统故障,交易为合约的执行设定了一个 gas 值上限,一旦超过了这个 gas 值,便会抛出 gas 值不足异常。
智能合约本身是有能力调用以太坊区块链上的其他账户的。这个函数是重载的,因为它既用来调用另一个合约中的函数,也用来将以太坊中的基础货币以太币(ETH)发送到一个帐户。以太坊的一个特殊之处是,来自合约内部的调用(也称为内部事务)不会创建新的交易,因此不会直接记录在链上。这意味着在不执行事务的情况下查看事务并不能提供足够的信息来跟踪事务流[3]

智能合约的漏洞

Re-Entrancy (RE) (重入攻击)[5]

当合约 “calls” 另一个帐户时,它可以选择允许被调用方使用一定数量的 gas 值。如果目标账户是一个合约,它将被执行,并可以使用合约所提供的 gas 预算。如果这样的合约是带有一些恶意的,而且提供的 gas 预算足够高,它可以尝试在呼叫者中回电——一个重新进入的 call。如果调用者的实现不是可重入的,例如,因为它没有更新包含余额信息的内部状态,那么攻击者可以使用此漏洞在易受攻击的合约中耗尽资金。此漏洞可以被用于 DAO 漏洞攻击,最终导致以太坊社区决定采用硬分叉回滚到以前的状态。作者在第8节中提供一些有关 DAO 开发的信息。

Unhandled Exceptions (UE)(未做异常处理)

一些在 Solidity 中的低级操作,像 send,用于发送以太币,在失败时不会抛出异常,而是通过返回布尔值来报告状态。如果这个返回值没有被检查,则即使支付失败,调用方也会继续执行程序,这很容易就会导致不一致[4]

Locked Ether (LE)(以太币锁定)

以太坊智能合约可以像以太坊上的帐户一样接收以太币。然而,有几个原因会导致收到的资金可能被永久地锁定在合约中。
一个原因是,合约可能依赖于另一个合约,该合约已被使用EVM的自毁指令销毁,即其代码已被删除,其资金已被转移。如果这是这样一个合约发送Ether的唯一方式,它将导致资金被完全锁定。这就是2017年11月发生的著名的 Parity Wallet 漏洞[7],锁定了价值数百万美元的以太币。作者在第8节中提供了更多的细节
还有一些情况下,合约总会在尝试发送以太币的时候耗尽 gas 值,这可能导致合约资金被锁。

Transaction Order Dependency (TO)(抢跑漏洞)

在以太坊,多个交易包含在一个块中,这意味着合约的状态可以在同一块中多次更新。如果调用同一智能合约的两个事务的顺序改变了最终结果,则攻击者可以利用此属性进行攻击。例如,给定一个期望参与者提交exchange中一个谜题的解决方案以获得奖励的合约,恶意合约的所有者可以在提交交易时减少奖励金额。
事例:SpankChain 事件

Integer Overflow (IO)(整形溢出)

整数上溢和下溢在许多编程语言中是一种常见的错误类型,但在以太坊环境中,它可能会产生非常严重的后果。例如,如果一个循环计数器溢出,产生一个无限循环,合约的资金可能会被完全冻结。如果攻击者能够增加循环的迭代次数(例如,通过注册足够的用户来触发溢出),则可以利用此漏洞。

Unrestricted Action (UA)

合约通常通过检查消息的发送者来执行身份验证,以限制用户可以执行的操作类型。通常,只有合约的所有者才可以销毁合约或设置新的所有者。这样的问题不仅发生在开发人员忘记执行关键的检查,也会发生在攻击者可以执行任意代码,例如通过控制委派调用的地址

分析工具

智能合约通常被设计成操纵和持有以太币计价的基金。这使得它们成为非常诱人的攻击目标,因为成功的攻击可能会让攻击者直接从合约中窃取资金。考虑到智能合约中的许多常见漏洞(在上一节中描述了其中一些),已经开发了大量工具来自动查找这些漏洞。这些工具大多分析合约源代码或其编译的EVM字节码,并查找已知的安全问题,如重入性或事务顺序依赖漏洞。在图 1 中总结了这些不同的工作。第二栏和第三栏分别列出了每篇论文中分析的合约和标记为易受攻击的合约的报告数量。“漏洞”列显示每个工具可以检查的漏洞类型。将在第 2.1 节中介绍这些漏洞,并在第 8.2 节中对这些工具进行更详细的描述。
notion image

定义

本文给出了易受攻击(vulnerable)、可利用(exploitable)和被利用(exploited)三个术语的定义。
vulnerable: 如果合约本身已被静态分析工具标记,则它是易受攻击的。正如稍后将看到的,这意味着一些合约可能会因为误报而受到攻击[8]
exploitable: 如果合约易受攻击并且该漏洞可能被外部攻击者利用,则该合约是可利用的。例如,如果工具标记的“漏洞”位于需要拥有合约的函数中,则该漏洞是易受攻击的,但不可利用。
exploited: 如果合约在以太坊的主网络上接收到触发其漏洞的事务,则会被利用。因此,一个合约可能是易受攻击的,甚至可以在没有被利用的情况下被利用。

数据集

该论文中分析了参考文献中[35]、[31]、[39]、[51]、[24]和[32]六篇学术论文中的易受攻击的智能合约。
本文的数据集由总共 821219 个合约组成,其中 23327 个合约被标记为易受第 2 节中描述的六个漏洞中的至少一个的攻击。虽然论文作者直接从其他学术论文的作者那里得到了数据,但分析的合约数量通常与论文中报告的数据不匹配,如图 1 所示。作者认为这方面的两个主要结果是:其他学术论文的作者在出版后改进了他们的工具,并且那些作者在他们提供给本文作者的数据中没有包含重复的内容。因此,本文作者展示了数据集中的数字,以及图 2 中脆弱的合约的处于危险中的以太币。风险在于将所有标记为脆弱的合约的余额相加。本文作者使用每篇论文发表时的用户余额,而不是当前的用户余额,因为它可以更好地了解可能被利用的Ether的数量。
notion image
  • 分类法 Taxonomy。
    • 作者没有按原样重用现有的智能合约漏洞分类法[11],而是对其进行调整,以适应数据集中工具分析的漏洞。这里不涉及六种工具中至少两种没有分析的漏洞。解决了第2节中描述的六种类型的漏洞:重进入(RE)、未处理异常(UE)、锁定以太币(LE)、事务顺序依赖(TO)、整数溢出(IO)和无限制操作(UA)。由于作者调查的论文对每个漏洞使用了不同的术语和略有不同的定义,因此作者将相关漏洞映射到作者分析的六种漏洞类型之一。作者在图 5 中展示了如何映射这些漏洞。
notion image
  • 重叠漏洞。
    • 在本小节中,作者首先检查数据集中合约之间有多少重叠:有多少合约被多个工具分析过,有多少合约被多个工具标记为易受攻击。作者注意到,除参考文献[35]外,大多数论文都是在同一时期发表的。作者发现,在 821219 份合约中,至少有两个工具分析了 73627 份,但至少有三个工具分析了 13751 份。在图 3a 中,作者展示了一个柱状图,显示了有多少不同的工具分析一个合约。在图 3b 中,作者显示了将单个合约标记为易受任何已分析漏洞攻击的工具的数量。所分析的合约和脆弱合约的重叠相对较小。作者假设其中一个原因是,一些工具处理的是稳定代码,而其他工具处理的是EVM字节码,这使得不同工具之间可用的合约数量不同。
notion image
在对不同工具的分析中,作者也发现了很多矛盾。作者选择重入漏洞来说明这一点,因为作者所分析的三个工具支持这一漏洞。在图 4 中,作者展示了支持重入检测的三个工具之间的一致性。“Total” 列显示 “Tools” 列中两个工具分析的合约总数,其中至少有一个工具将其标记为易重入。Oyente 和 Securify 只同意23%的合约,而 Zeus 似乎不同意任何其他工具。这反映了构建针对 EVM 的静态分析工具的困难。虽然作者没有试图去评估不同工具的性能,但这也给了作者另一个动机,即找出所报告的漏洞的影响因素。

方法论

在本节中,作者详细描述了为检查第2节所述漏洞的利用情况而执行的不同分析。
为了检查潜在的漏洞,作者执行了字节码级别的**交易[9]**分析,在执行特定事务时查看合约执行的代码。作者使用这种类型的分析来检测第2节中介绍的六种类型的漏洞。
为了执行分析,作者首先检索了数据集中所有合约的事务数据。接下来,为了执行字节码级别的分析,作者提取可能影响感兴趣合约的交易的执行跟踪。作者使用EVM的调试功能,这保证了可以实现在跟踪执行的指令时的重入事务操作。为了加快数据收集过程,作者还修补了 Go 以太坊客户端,而不是依赖默认以太坊客户端提供的远程过程调用(RPC)功能。
提取的**跟踪[10]**包含已执行指令的列表,以及每条指令的堆栈状态。为了分析跟踪,作者将它们编码为数据日志表示;数据日志是一种实现递归一阶逻辑的语言,允许简洁地表示有关执行跟踪的属性。作者使用了以下域将有关跟踪的信息编码为数据日志事实,注意:V是程序变量集,A是以太坊地址集。在图7中,作者展示了收集的事实和用于检查可能的攻击的关系的概述。作者强调了,作者的分析全部基于自动运行,而且提取的事实和规则,定义的各种违规行为在后面的章节中会有所描述。
notion image

Re-Entrancy 重入

在EVM中,由于交易之间是独立执行的,因此重新进入问题只会在单个交易中发生。因此,要利用重入性,必须有对外部合约的调用,该调用直接或间接地调用对调用合约的重入回调。因此,作者首先在执行跟踪中查找call指令,同时跟踪当前正在执行的合约。
在执行调用时,可以通过检查堆栈上的值来检索要调用的合约的地址以及要发送的值。使用这些信息,可以记录图7a中描述的call(a1,a2,p)事实。可以注意到,合约还可以使用create创建新合约,并使用它执行重入攻击。因此,作者将此指令视为调用。使用这些以及图7c所示的查询来检索潜在的恶意重入调用。
  • *分析正确性。**对重入呼叫的分析是合理的,但并不完整。由于EVM仅单个线程中执行每个合约,因此重入调用必须来自递归调用。例如,假设A、B、C和D是函数,则可以使用调用路径(如A→B→C→A)来生成重入调用。作者利用工具搜索所有相互递归的调用;它通过使用递归数据规则支持任意长的调用路径,使分析听起来很合理。但是,却无法评估重入调用是否恶意,这可能导致误报。

Unhandled Exceptions 未进行异常处理

当Solidity编译合约时,发送Ether的方法(如send)被编译到EVM调用指令中。在图6中展示了这样一个调用及其指令的示例。如果传递给CALL的地址是一个地址[11],EVM将执行合约的代码,否则它将执行必要的指令将以太网传输到该地址。当EVM执行完毕后,如果调用成功,它会在堆栈上push一个1,否则push 0。
为了检索有关调用结果的信息,可以检查CALL指令,并在调用执行后使用堆栈上推送的值。通过检查跟踪的深度何时返回到执行调用指令时的值,可以很容易地找到调用执行的结束;作者将此信息保存为call_result(v, n)。需要注意的一个重要的边缘情况在于对预编译合约的调用,这些合约通常由编译器调用,不需要检查它们的返回值,因为它们是计算结果,其中0可能是有效值,但也可能是错误的值。由于预编译的合约中有1到10之间的已知地址,作者选择不记录此类调用的call_result记录。
如图6b所示,EVM使用JUMPI指令执行条件跳转。在编写时,这是唯一可用于执行条件控制流的指令。因此,作者将JUMPI中用作条件的所有值标记为in_condition。然后,通过查找调用结果来检查未处理的异常,使用图7c所示的查询,调用结果不会影响条件。
notion image
notion image
  • *分析正确性。**为检查未处理的异常而执行的分析是完整的,但不可靠。在作者记录执行记录时,所有在程序执行过程中失败的调用都会被记录下来。然后,使用递归数据日志规则来检查调用结果是否直接或间接地与某个条件匹配[12]。如果调用结果适用于某个条件,但该条件不足以阻止攻击,则可能会获得误报。然而,鉴于此漏洞最普遍的模式是根本不使用send的结果,并且当使用结果时,通常在require或assert表达式中执行,作者假设此类误报应该非常罕见。

Locked Ether 冻结以太币

尽管合约中的资金被锁定有几个原因,但在这里关注的是合约依赖于不再存在的外部合约的情况,因为这是对以太坊财务影响最大的模式。当一个合约使用另一个合约作为库来代表它执行某些操作时,就会出现这种情况。要以这种方式调用合约,将使用DELEGATECALL指令而不是CALL,因为后者不保留调用数据,例如发送方或值。
下一个重要部分是EVM在试图调用一个已经不存在的合约时的行为。当一个合约被破坏时,它本身并没有被完全删除,但是它的代码对于调用方来说不再是可访问的。当一个合约试图调用一个已被破坏的合约时,该调用是不可操作,但是不是失败,这意味着下一条指令将被执行,并且该调用将被标记为成功。为了找到这样的模式,作者收集了关于每个DELEGATECALL指令之前和之后程序计数器的所有值的数据日志事实。特别地,作者首先将执行调用的程序计数器值标记为call_entry(i_1∈N,a∈a)。然后,使用与Unhandle Exception相同的方法,跳过调用的内容并标记调用返回的程序计数器值为call_exit(i_2∈N)。
如果被调用的合约不再存在,则i_1+1=i_2必须保持[13]。因此,可以使用图7c所示的数据日志查询来检索被破坏的合约地址。
notion image
  • *分析正确性。**对于作者所关注的锁定资金漏洞,作者用于检测锁定资金漏洞的方法是合理和完整的。所有易受攻击的合约都必须有DELEGATECALL指令。如果这个问题存在,并且调用合约确实已被破坏,那么它将必然导致no-op调用。作者的分析记录了所有这些调用,并在执行之前和之后系统地检查程序计数器,从而保证分析的健全和完整。

Transaction Order Dependency 交易顺序依赖

检查事务顺序依赖性利用情况的第一个细节在于,同一个块中必须至少包含同一合约的两笔交易,这样的策略才能成功。此外,如原文参考文献[35]或[51]所示,利用事务排序依赖漏洞需要操纵合约的存储。
EVM只有一条从存储器SLOAD读取的指令和一条写入存储器SSTORE的指令。在EVM中,用于这两条指令的存储器的位置作为参数传递,称为存储器密钥。在调用指令时,此密钥在堆栈上可用。作者遍历了所有合约的交易,每次遇到其中一条指令时,就记录下来tx_ sload(b∈N,i∈N,k∈N)或tx_store(b∈N,i∈N,k∈N),其中b是块号,i是块中事务的索引,k是被访问的存储密钥。
检查事务顺序依赖性问题的规则的本质是寻找至少两个交易包含在同一块中的模式,其中一个事务在存储器中写入一个密钥,另一个事务读取同一个密钥。在图7c中显示了实际的规则。
notion image
  • *分析正确性。**检查事务顺序依赖关系的方法是合理的,但并不完整。根据所使用的定义,要使合约具有事务顺序依赖性,它必须在同一块中有两笔交易,这会影响存储中的同一密钥。作者检查了所有这些情况,因此不可能存在假阴性。但是,如果要确定是否存在事务顺序依赖关系,则需要更多关于如何使用存储的知识,因此作者所用的方法可能会导致误报。

Integer Overflow 整数溢出

EVM是完全非结构化的,用256位字表示所有内容。因此,数据结构完全在编译级别处理,并且在任何执行跟踪中都没有关于原始类型的显式信息。
为了检查整数溢出,作者在两次传递中累积记录。在第一个过程中,作者尝试恢复堆栈上不同值的符号和大小。为此,作者使用了有关Solidity编译过程的已知不变量。首先,可以将SIGNEXTEND或SDIV等指令的结果中的任何值标记为使用is_signed(v)进行签名。此外,SIGNEXTEND是两个补码的常用符号扩展操作,它同时传递要扩展的值和值的位数。这允许检索有符号值的大小。假设任何没有显式标记为有符号的值都是无符号的。为了检索无符号值的大小,作者使用Solidity编译器的另一个功能。
为了解决EVM中缺少类型的问题,Solidity编译器插入AND指令将无符号整数“转换”为正确的值。例如,为了模拟uint8,编译器插入AND value 0xff。在“强制转换”的情况下,第二个操作数m的形式总是m=16n−1,n∈n,n=2p,p∈[1,6]。作者使用这个观察值来标记相应类型的值:uintN,其中N=N×4。大小变量存储为size(v,n)。
在第二阶段,作者使用图7b中所示的inferred_signed(v)和inferred_size(v,n)规则来重新检索有关当前变量的信息。当无法推断出有关大小的信息时,将其近似为256位,即EVM字的大小。利用这些信息,计算所有算术指令(例如ADD、MUL)的期望值,以及EVM计算的实际结果,并将它们存储为数据日志事实。最后,使用图7c所示的查询来查找溢出的指令。
notion image
  • *分析正确性。**作者称在这里对整数溢出的分析既不合理也不完整。这些类型是通过使用启发式的编译器属性推断出来的,启发式应该适用于大多数情况,但可能会失败。例如,如果一个合约包含产生 AND value 0xff但值是uint32的代码,那么作者的类型推断算法会错误地推断这个变量是uint8。类型推断过程中的这种错误可能会导致误报和漏报。
但是,只有当开发人员使用与Solidity编译器生成的掩码类似的位操作时,才会出现这种类型的问题。作者发现这样的模式非常罕见,不会扭曲他们的数据,并在第5.5节中给出了可能遵循这种模式的合约数量的估计。

Unrestricted Action 无限制行动

无限制操作是一类广泛的漏洞,因为它可以包括在不被允许的情况下设置所有者、在不被允许的情况下销毁合约或执行任意代码的能力。由于作者的主要目标之一是检查易受攻击合约的利用情况,因此作者接近于以前的工作[32]给出的定义,并关注使用CALL的无限制以太传输、使用和SSTORE的无限制写入以及使用DELEGATECALL或CALLCODE的代码注入。
首先,需要注意到,与调用数据不同,调用方不能伪造。因此,一个主要的见解是,一个操作是否是受限的,取决于调用方是谁,那么受限操作之前应该有一个执行跟踪,根据调用方的不同,该跟踪会有条件地跳转。这对于自毁来说已经足够了,但是对于其他指令来说就不够了,因为它会标记一行,比如balances[msg.sender] = msg.value变得脆弱。为了对此进行建模,作者跟踪了消息发送者是影响存储密钥还是要调用的地址。最后,对于代码注入,检查传递的数据是否影响DELEGATECALL或CALLCODE调用的地址。
  • *分析正确性。**作者称在这里对无限制行动的分析既不健全也不完整。在执行敏感指令之前,作者采用一种相对简单的方法来检查消息发送方是否影响某个条件。这可能导致误报,因为检查可能执行得不适当,例如在需要时不还原事务,使分析不可靠。此外,在某些用例中,允许任何发送方写入存储是可以接受的,但是作者的分析会标记为易受攻击,从而导致误报。作者将在第5.6节中进一步讨论这些影响。

Analysis of Individual Vulnerabilities

如第3节所述,所有脆弱合约中包含的Ether总量超过300万ETH,价值6亿美元。在本节中,作者逐一介绍了每个漏洞的结果;其结果是使用第4节中描述的方法获得的;目的是显示这些资金中有多少实际处于风险之中。
  • *方法论。**对于每个漏洞,分两步进行分析。首先,直接或通过内部事务获取10200000块之前影响数据集中合约的所有交易的执行跟踪。然后运行作者的工具,自动找到有风险的Ether总量,并报告这个数字。这是作者用于稍后给出所有漏洞的总上限的量。在第二步中,作者手动分析了处于风险中的合约,以获得关于漏洞利用的更多见解,并找到有趣的模式。由于手动分析所有合约都是不切实际的,因此对于每个漏洞,作者手动了分析风险最高的合约,以便更好地理解这些漏洞背后的原因。然后,作者将有趣的发现作为简短的案例研究。
  • *运行时性能。**作者的分析在线性时间和内存中运行,与给定交易执行的指令数有关。指令的数量在不同的交易中差别很大,从几百条到几十万条,平均在100000条左右。作者的工具平均耗时不到10毫秒(stddev。20ms),最大交易的最大时间不超过2秒,这低于作者为单个交易设置的5秒超时时间。

RE: Re-Entrancy

截至 [31, 35, 51] ,共有4337份合约被标记为易再次进入,共有457073笔交易。在对第4节中描述的所有交易进行分析之后,作者发现总共有116个合约包含重入漏洞。为了寻找有风险的货币金额,作者计算了两个包含重入漏洞的交易合约之间发送的金额之和。利用再进入法开采的以太总量为6076 ETH,由于其价值超过1200000美元,这是一个相当可观的数字。
  • *手动分析。**作者根据损失的资金手动分析顶级合约,如图8所示。有趣的是,这三个潜在的开发项目中有一个涉及大量以太币:5881 ETH,相当于1180000美元。最近一些关于重入的研究发现,这个地址很容易受到攻击。看来,作为Maker DAO平台一部分的合约被合约的作者发现易受攻击,他们自己进行了攻击以确认风险。
  • *健全性检查。**作者使用两个不同的合约来进行健全性检查。首先,来看dao攻击,这是最著名的重入攻击实例。作者的工具检测到以下重入模式:恶意帐户调用dao主帐户,dao主帐户调用奖励帐户,奖励帐户将奖励发送给恶意帐户,允许其执行对dao主帐户的重入调用。
作为另一个合理性检查,作者查看了一个名为SpankChain的合约,该合约最近被重新进入攻击所承诺。作者确认了他们的方法成功地将此合约标记为重入攻击的受害者,并正确识别了攻击者的合约。
最后,作者注意到其的工具找到了Sereum[43]提出的所有重入模式,包括委托和基于创建的重入。

UE: Unhandled Exceptions

共有11427份合约被[31,35,51]标记为易受未处理异常的影响,总交易量超过340万笔,这比作者发现的重入问题要大一个数量级。
作者发现总共有264个合约中的失败调用没有被检查,这大约占到了标记合约的2%。下一个目标是找出由于这些未处理的异常而导致的以太风险量的上限。作者将风险资金的上限定义为发生未处理异常时合约余额的最小值和未能发送的总金额。然后,作者对所有问题的上界求和,得到一个总的上界。最终得出了271.89以太币由于UE漏洞处于风险之中。
  • *手动分析。**作者手动分析了顶级合约,并在图9中总结它们的地址和风险金额。其中三份合约都有实体代码。作者确认,在所有情况下,问题都是由于误用了诸如send之类的低级别Solidity函数。
notion image
合约0x7011f3edc7fa43c81440f9f43a6458174113b162未能发送总计52.90以太币,目前在撰写本报告时仍保留69.3以太币余额。经过调查,作者发现该合约是一个废弃的传销[5]。该合约共有21个调用失败,都试图发送2.7以太币,这似乎是在这个时间点上的传销奖励。不幸的是,这份合约的代码没有提供,没有得到进一步的检查,但作者得到的结论是,很有可能是因为一些用户在传销没有获得他们的奖励。

LE: Locked Ether

有7285份合约被[51]、[24]、[39]和[31]标记为易受攻击。这些合约总价值超过140万ETH,价值超过2亿美元。作者通过执行上一节中描述的分析来分析可能被锁定的合约的交易。其工具显示,实际上没有一个合约受到检查的模式的影响,即依赖于已被破坏的合约。注意到,作者所使用的工具目前只涵盖了依赖于一个破坏的合约作为一个原因锁定以太和模式,如无界大规模操作尚未涵盖Parity wallet。受Parity wallet(0x863df6bfa4469f3ead0be8f9f2aae51c91a907b4)错误影响的合约未被作者分析的工具标记,因此不存在于此数据集中。由于这是最著名的locked Ether案例之一,因此作者将在受此bug影响的合约上测试此工具。为了找到合约,只需使用图7c中锁定以太的数据日志查询,并插入奇偶校验钱包地址的值作为参数a。对受奇偶校验错误影响的合约的结果确实与其他人在过去发现的结果相匹配,合约地址为0x3bfc20f0b9afcace800d73d2191166ff16540258,已锁定306276以太币。

TO: Transaction Order Dependency

共有1881份合约被[35]和[31]标记为易受交易顺序依赖性影响。作者对他们的3002304笔交易进行了第4节所述的分析,得到了总共54笔可能受交易订单依赖性影响的合约。为了估计风险以太币的数量,将所有标记的交易期间发送的y以太包括内部交易的总价值加起来,得出总共297.2以太币存在交易订单依赖性风险。
  • *手动分析。**对于每个合约,作者都会找到一个块,在这个块中,交易顺序依赖关系可能发生在余额最高的地方,并在图10中用它们在发布时的余额报告top。作者手动调查了列出的合约,它们都有可用的源代码。作者确认了,在所有合约中,用户有可能在一个块内读写同一个存储位置。作者进一步检查了0x3da71558a40f63b960196cc0679847ff50fad22b,这是一个名为drawchilddao的合约,发现读取内容非常方便用户检查余额,使得问题变得更为严重。
notion image

IO: Integer Overflow

有2472份合约被标记为易受整数溢出影响,总计超过120万笔交易。作者运行第4节中描述的方法来搜索实际发生的整数溢出。值得注意的是,对于整数溢出分析,作者依赖于Solidity编译器的属性。为了确保分析的合约是使用Solidity编译的,作者从Etherscan获取了标记为易受整数溢出攻击的合约的所有可用源代码。在2492份合约中,有945份提供了源代码,而且所有的合约都是以稳定的格式编写的。
  • *Effects of our formulation。**如第4.5节所述,某些类型的位操纵可能导致类型推断启发式失败。作者使用在这里收集的源代码来验证这可能会影响作者的分析。作者发现,位操作本身在稳固性方面已经相当罕见,在作者所收集的2492份合约中,只有244份使用了任何类型的位操作。此外,大多数使用位操作的合约都将其作为位数组来操作变量,并且一次只能检索一个位。这样的模式并不会影响作者的分析。作者发现只有33个区域使用0xFF或类似的值,这实际上会影响分析。这约占可获得源代码的合约数量的1.3%。
组综合发现总共有62个合约的交易可能发生了整数溢出。为了找出风险中的以太币的数量,作者分析了导致整数溢出的所有交易。作者通过对包含溢出的交易期间传入和传出的以太币总量求和来近似该数量。作者发现总的处于危险中的以太币数量是1842以太币。这很可能是一个过近似值,但作者将这个值设为了上限
  • *手动分析。**作者进一步检查了他们获得的一些结果,以便更好地了解什么样的情况会导致溢出。作者发现溢出的一个常见原因是无符号数的下溢。作者将在下面的调查中重点介绍其中一个案例。
notion image
这个合约被作者的工具标记为未处理的异常和整数溢出。经检查,在1364860号块,业主似乎试图降低费用,但未签字的费用价值溢出,成为一个巨大的数字。因为这个问题,合约当时试图发送大量以太币。这会导致失败的调用,而这些调用恰好没有被检查,因此会为未处理的异常设置标志。

Unrestricted Action

共有5163份合约被[32, 39, 51]标记为易受无限制行动影响,共有3871770笔交易。作者使用第4.6节中描述的方法,发现总共有42份合约遭受了不受限制的行为,这些行为都是非限制性自毁行为,但在攻击发生时没有一份是有效的。
Effects of our formulation. 如第4.6节所述,这种分析是不合理的,这意味着作者需要谨慎对待假阳性。合约可以对消息发送方进行检查,检查结果不正确,可以被利用,但不能被标记为不正确。虽然作者假设这是一个边缘的情况,但不能完全排除。然而,为这种检查提供一种自动化方法需要了解程序员的意图,例如通过规范,这超出了本工作的范围。因此,作者决定更详细地检查数据集中的合约,以便更好地了解利用程度。
  • *手动分析。**利用以太币的工具标记可利用的合约,而不是简单的脆弱的合约。因此,这些合约更有可能被利用,并对这些合约进行手动分析。作者获取了以太合约的所有历史余额,并检索在任何时间点持有的最大金额,发现这些总数等于4921以太币。然而,作者发现4867以太币属于48个不同的合约,字节码具有完全相同的字节码,都有相同的交易模式,这是作者在下面的调查中描述的。
notion image
它与本合约共享相同的字节码、创建者和交易模式。合约由0x15f889d2469d1de0e0699632d8d448f2178a7创建,从Kraken接收以太币,交换,发送到0xd1bf1706306c7c667c67fb5c1f76cc76cc7637685bd。作者没有找到关于这些地址更多的信息。作者对合约进行了反编译,以了解合约是如何可利用的,并发现在他们持有资金的几个块中,利用合约就像发送一笔交易与转移资金的地址作为论证。这是一个非常危险的情况,但因为以太币通常在一分钟内被发送到另一个地址,攻击者需要非常积极主动地使用高级工具来利用合约。这很好地说明了合约如何可以被利用,但在实践中几乎没有机会被利用。
  • *理智性检查。**作为理智检查,除了作者使用的测试套件,作者还使用了一个最著名的不受限制操作的实例之一:地址0x863df6bfa4469f3版0ed8f9f2aae51c91a907b4的破坏奇偶钱包库合约。作者分析了交易,并成功在交易0x05f71e1b中找到了一个不受限制的存储指令,可被用于控制钱包。

Summary

作者对所有的发现进行了总结,包括最初标记的合约数量、处于危险的以太币数量以及图11中实际开采的数量。Contracts Explosed列表示标记为Explosed的合同数,%Contracts Explosed是该数字相对于标记为vulnerable的合约数的百分比。开采的Ether列显示了可能开采的Ether的最大数量,下一列显示了该数量与风险总量的百分比。总行只记录一次标记有多个漏洞的合约。
notion image
总的来说,作者发现被利用的合约数量是不可忽略的,在作者的研究中涉及的6个漏洞中,约有2%到4%的脆弱合约仅被利用了4个。然而,值得注意的是,开采的Ether百分比要低一个数量级,最多有0.4%的Ether用于重入。这表明开发的合约通常价值较低。作者将在第7节进一步阐述这个论点。

限制

在本节中,作者介绍了作者的系统中的不同限制,并描述作者是如何尝试减轻这些限制的。
  • *稳健性vs完整性。**对于像这样的大多数工具,会面临着稳健性和完整性之间的权衡。只要有可能,作者就会选择稳健性而不是完整性——作者的分析中有六分之三是具有稳健性的。当作者不能实现健全的分析时,就会更加小心,如果只是遗漏了案件,不太可能产生许多假阴性。换句话说,作者会尽可能减少假阴性的数量,即使这意味着增加假阳性的数量。实际上,作者的系统的主要目标是为作者提供一个可能被利用的Ether量的上限,这使得假阴性是不受欢迎的。此外,作者还手动检查标记为利用的高价值合约,误报不会对最终结果产生重要影响。作为这种权衡的一个例子,对于重入漏洞,作者标记了任何使用重入调用调用的合约,以及在此过程中丢失的资金。然而,在某些情况下,这可能是一种预期行为,资金可能已转移到属于同一实体的地址。
数据集。作者只在数据集中包含的合约上运行了他们的工具,这意味着作者可能遗漏了一些实际发生的攻击。事实上,没有任何合约受到奇偶钱包漏洞的影响,也没有合约受到数据集中DAO黑客攻击的影响。然而,本文的主要目标之一是量化分析工具发现的漏洞在实践中被利用的比例,作者目前的方法允许作者做到这一点。
其他类型的攻击。作者的工具和分析并未涵盖对智能合约的所有现有攻击。例如,有针对ERC-20代币的攻击[42],还有一些其他类型的DoS攻击,如钱包欺诈[24]。此外,一些检测到的“漏洞”可能是蜜罐[50]的结果,但作者的工具不包括此类情况。所有虽然这将是有趣的,也涵盖这种情况下,作者必须对涉猎的范围做出取舍。因此,作者将重点放在文献中最常见的漏洞上,假设这些漏洞代表了这些漏洞的常见程度。

讨论

即使考虑到了作者系统的局限性,很明显智能合约的利用率远远低于预期。在本节中,作者将介绍一些影响智能合约实际应用的因素。
作者认为,报告的脆弱合约的数量与利用的合约数量之间存在差异的一个主要原因是Ether在合约中的分布。事实上,在本文数据集中的23327份合约中,只有大约2000份含有以太币,而且大多数合同的余额低于1Ether。图12a显示了数据集中包含Ether的合约的平衡分布。此外,排名前10位的合约约占总合约的95%。作者在图12b中显示了Ether在含有10个Ether以上的合约中的累积分布。这表明,只要顶级合约无法交割,实际处于风险中的Ether总量就不会接近“脆弱”Ether的上限.
为了确保这一事实可以推广到整个以太坊区块链,而不仅仅是作者使用的数据集,作者获取了所有现有合约的余额。总共有15459193份合约。其中,只有463538份合约有非零余额,仅占所有合约的3%。余额非零的合约中,前10名、前100名和前1000名分别占Ether总量的54%、92%和99%。这表明,作者使用的数据集与整个以太坊区块链的趋势相同:只有极少量的合约持有大部分财富。
  • *高价值合同的人工检查。**作者决定手动检查前6个合约,在撰写本文时的余额方面,这些合约被数据集中的任何工具标记为易受攻击。作者将重点放在了前6位,因为这恰好是目前持有超过10万ETH的合约数量。这些合约总计持有1695240 ETH,占作者使用数据集中所有合约目前持有的2037521 ETH总数的83%。作者在这里给出了调查结果的概述,在附录a中给出了更深入的版本。
Etherscan上没有此合约的源代码。然而,作者发现它是Ethereum基金会的多重签名钱包,它的源代码在GITHUB上是可用的。作者对代码进行检查,发现所有的调用都要求消息的发送者是所有者。这本身就足以防止任何重入者调用,因为恶意合约必须是所有者,这是没有意义的。此外,尽管本文中使用的Oyente版本报告了重入,但该工具的较新版本不再报告此漏洞。因此,作者有把握地得出结论,重入问题是一个错误的警报。
作者还检查了其他5份合同中的4份。地址为0x07ee55aa48bb72dcc6e9d78256648910de513eca的合约是作者唯一找不到任何信息的合约。名单中的第二、第三和第五个合约也是多签名钱包,利用这些合约将再次要求多数所有者是恶意的。例如,要锁定Ether,所有者必须同意添加足够的额外所有者,以便所有者上的所有环路都会导致气体耗尽异常。地址为0xbf4ed7b27f1d666546e30d74d50d173d20bca754的合约称为撤回合约。作者没有发现任何具体的问题,但它确实使用了一个委托模式来解释Zeus标记的锁定以太币漏洞.
作者在附录a中对高价值合约进行了彻底调查。总体而言,作者可以分析得到图13中的所有合约似乎都非常安全,标记的漏洞绝对不可利用。尽管作者在第8节中介绍了一些非常罕见的情况,即高Ether余额的合约被盗,但这些仍然是例外。到目前为止,作者所提供的事实帮助作者确认了以太坊区块链上面临风险的以太币数量远没有声称的那么多。
notion image

相关工作

近年来,在以太坊上发现了一些主要的智能合约漏洞。这些攻击已经被分析和分类,许多工具和技术已经出现,以防止此类攻击。最近的文献也显示了对以太坊的攻击是如何随着时间的推移而演变的。在本节中,作者将首先提供有关两个最突出的历史开发的详细信息,然后介绍旨在提高智能合约安全性的现有工作。

大规模漏洞

The DAO漏洞。DAO漏洞是以太坊区块链上最著名的漏洞之一。攻击者利用了合同的重入漏洞,该漏洞允许耗尽合同资金。攻击者可以在DAO余额减少之前调用函数以重新进入的方式提取资金,从而使资金的自由外流成为可能。共排出350多万Ether。考虑到攻击的严重性,以太坊社区最终同意了硬分叉。
Parity wallet bug。Parity wallet漏洞是以太坊区块链上的另一个突出漏洞,导致价值2.8亿美元的以太坊被冻结在Parity wallet账户上。这是由于一个非常简单的漏洞:Parity wallet使用的库合约没有正确初始化,任何人都可能破坏它。一旦库被毁,任何对平价钱包的调用都将失败,从而有效地锁定了所有资金。

分析和验证智能合约

为了防止此类攻击并使智能合约总体上更加安全,已经做出了很多努力。作者在这里介绍一些在文献中介绍的工具和技术,并在相关时描述它们与作者所做的工作的比较。
分析工具大致可以分为两类:静态分析工具和动态分析工具。使用术语“静态”相当宽松,静态分析工具可以定义为不需要部署智能合约就可以捕获bug或漏洞的工具。运行时分析工具试图通过执行部署的合约来检测这些。作者所用的工具属于第二类。
  • *静态分析工具。**静态分析工具一直是研究的重点。这是可以理解的,因为避免已部署合约中的漏洞是多么重要。这些工具大多通过分析字节码或合约的高级代码以及检查已知的易受攻击的模式来工作。
Oyente 是最早开发的分析智能合约的工具之一。它使用符号执行结合Z3 SMT解算器来检查以下漏洞:事务排序依赖性、重进入性和未处理的异常。
ZEUS 是一个静态分析工具,它在Solidity智能合约上工作,而不是在字节码上工作,这使得它适合于帮助开发工作,而不是分析部署的合约,Solidity代码通常不可用。ZEUS将XACML样式的策略和Solidity合约代码转换为LLVM位代码,并在其上使用约束的Horn子句来检查策略是否得到遵守。
Securify 是一个静态分析工具,用于检查智能合约的EVM字节码的安全属性。它将安全属性编码为用数据日志(如域特定语言)编写的模式,并检查符合性或违反性。Securify从合约中推断语义事实,并通过查询推断的事实来解释安全模式以检查它们是否违反或符合。这种方法与作者所用的方法有很多相似之处,使用数据日志来表示漏洞模式。主要区别在于Securify处理字节码,而作者的工具处理执行跟踪。
MadMax 与Securify有相似之处,因为它还将智能合约的属性编码到数据日志中,但它关注与gas相关的漏洞。它是检测“无界质量操作”的第一个工具,其中循环受到动态属性(如用户数)的限制,从而导致合同总是在通过一定数量的用户时耗尽天然气。MadMax构建在Vandal[15]实现的反编译器之上,性能足以在10小时内分析以太坊区块链的所有合约。
其他一些静态分析工具也已经开发出来,其中一些工具,如SmartCheck,非常通用,包含了许多类型的漏洞,而另一些工具则更具有领域特定性,如Osiris专注于整数过流,Maian专注于无限制操作,Gasper专注于代价高昂的gas模式。最近,ETHBMC的设计还支持合约间关系、加密哈希函数和memcopy风格的操作。
最后,在正式验证智能合约方面也做出了一些努力。[28]是这方面的第一个尝试,并使用Lem[38]定义了EVM,这允许生成定理证明者(如Coq[12])的定义。[25]提出了EVM字节码的完整小步语义,并使用F*证明助手对其进行了形式化[47]。在[27]中也做了类似的工作,使用K框架[44]给出了EVM的可执行形式规范。VerX[41]也是最近的一项工作,它允许用户编写智能合约的属性,这些属性将由该工具正式验证。
  • *动态分析工具。**虽然动态分析工具的研究比静态分析工具少,但近年来出现了一些工作。
这一行的第一个工作是ContractFuzzer[30]。正如它的名字所示,它使用模糊技术来发现智能合约中的漏洞,并能够检测到各种各样的漏洞,如重入、锁定的以太或未处理的异常。该工具生成合同的输入,并使用检测过的EVM检查是否触发了某些漏洞。这种模糊化方法的一个重要限制是它需要合约的应用程序二进制接口,这通常不适用于部署在以太坊主网络上的合约。
Sereum 专注于在运行时通过在修改过的Go-Ethereum客户端中集成检查来检测重入攻击。该工具分析运行时跟踪,并使用污点分析来确保在重入调用中不使用访问合约存储的变量。尽管与作者的工具(也在运行时分析跟踪)有一些相似之处,但Sereum侧重于重入性,而作者的工具更通用,特别是因为漏洞模式可以很容易地用数据日志表示。
teEther 也可以在运行时工作,但与之前的工作不同,因为它不试图保护合约,而是主动为合约寻找漏洞。它首先分析合约字节码以寻找关键的执行路径。关键路径是可能导致资金损失的执行路径,例如通过向任意地址汇款或被任何人破坏。为了找到这些路径,它使用了一种接近Oyente[35]的方法,结合符号执行和Z3来解决路径约束。
TXSPECTOR 是在本文的第一版之后不久发表的,它使用了一种与作者非常相似的方法来检测再次进入、未经检查的呼叫和自杀合约。它们还利用数据日志方法来检测漏洞,但首先将事务跟踪转换为流图,而不是直接向数据日志数据库添加有关跟踪的事实。虽然这确实增加了表达能力,但它使分析变得更加复杂,导致某些事务的分析超时。因此,相信他们的方法可以补充作者所用的方法,并用于消除作者方法的潜在误报。
  • *总结。**静态分析工具通常用于检测易受攻击的合约,而动态分析工具则用于检测可被攻击的合约。唯一的例外是Sereum,它检测使用重入的合约。据作者所知,作者的工作是第一次尝试检测利用各种漏洞的合同。这与其他工作基本上是正交的,可以通过帮助理解野外正在发生的开发类型来支持分析工具的开发工作。

总结

本文调查了最近6个学术项目所报道的23327份较脆弱的合约。作者提出了一个基于数据日志的公式,用于对EVM执行跟踪执行分析,并使用它来分析这些合约执行的总计超过2000万个交易。最终发现,在23327份合约中,最多有463份受到了侵害,但最多有8487 ETH(170万美元),或者说只发现了0.27%的300万ETH(6亿美元)的潜在风险。最后,作者发现大部分Ether仅由少数合约持有,并且报告的这些漏洞要么是误报,要么在实践中不可利用,从而为作者的结果提供了合理的解释。

答疑

[1] 什么叫图灵完备?
[2] 什么叫不使用可靠度?
[3] 什么意思?
[4] 不一致?
[5] 另一种解释:
以太坊智能合约能够调用和利用其他外部合约的代码。合约通常也处理以太币,因此将以太币发送到各种外部用户地址。调用外部合约或将以太币发送到地址的操作要求合约提交外部调用。这些外部调用可以被攻击者劫持,从而迫使合约执行更多的代码(即通过 fallback 回退函数),包括回调原合约本身。所以,合约代码执行过程中将可以“重入”该合约,有点像编程语言里面的间接递归函数调用。在臭名昭著的The DAO事件中黑客使用了这种攻击,最终导致了以太坊的硬分叉。
[6] 什么是以太币和 gas ,二者之间有什么关系?
[7] 待第八章讲解
[8] 因为误报而收到攻击?
[9] 到底是事务还是交易?
[10] 这里的跟踪应该是指指令历史
[11] 这里什么意思?
[12] 为什么调用结果会被用于条件中?
[13] 什么叫保持?

启发

从数据的角度去研究安全问题
改变了大部分人对区块链的成见
区块链的问题:效率与安全的博弈
Loading...

尚未开始
更新中
近期核心
已完结
已弃更

© River 2021-2025