作者:天宸@蚂蚁安全实验室
原文链接:https://mp.weixin.qq.com/s/ZxrBHgZ5a_IuU0nNySIlwA

南方科技大学教授张殷乾:

智能合约是运行在区块链的具备图灵完备行的分布式应用程序,在以太坊、EOS等区块链上有广泛的应用。然而,随着智能合约的普及和应用,针对智能合约的攻击事件屡见不鲜。为了帮助人们更好地理解这些漏洞的原理,蚂蚁安全实验室“智能合约安全系列 -- 举一反三总结篇”文章总结了以太坊和EOS区块链上智能合约的一些已知攻击的原理。

  • 文章首先介绍了智能合约基本概念,以及智能合约运行机制。它从隔离机制、停机机制、以及跨合约调用三部分出发,阐明了智能合约本身的工作原理以及安全机制。其次,文章按照漏洞在各个平台上的通用性高低程度,对于已经发生的若干种漏洞进行分类总结。漏洞类型分为五类:1.基础漏洞类型 2.函数接口漏洞类型 3.平台特性漏洞类型 4.充值场景漏洞类型 5.经典攻击手法区块链上再现。

  • 由于智能合约漏洞常引发大规模的经济损失,智能合约的漏洞得到了人们的广泛关注,不少相关机构都已经提出类似的漏洞总结文章。蚂蚁安全实验室这篇文章从三个新角度带给人们一些启发性:(1)以太坊和EOS两条区块链上漏洞的共性和差异,(2)从平台适用性角度对这些漏洞的分类总结,(3)针对充值场景漏洞类型的几种分类。

蚂蚁安全实验室还发表了其他关于智能合约漏洞的文章总结,其中对于每一种漏洞的介绍更为具体详细。感谢蚂蚁安全实验室为区块链平台做出的努力和贡献,也由衷希望区块链在这些安全机构的帮助上能更加安稳、蓬勃发展。

本文是智能合约安全分析系列的最后一篇文章。在智能合约安全系列 -- 运行平台科普篇智能合约安全系列——百万合约之母以太坊的漏洞攻防术(上集)智能合约安全系列——百万合约之母以太坊的漏洞攻防术(下集)智能合约安全系列——EOS菠菜应用篇之后,本文希望对智能合约安全分析系列做一个总结。

1 引言

智能合约安全分析系列已接近尾声了。本系列前几篇文章着重分析了以太坊和 EOS 的智能合约漏洞。分析这两个平台一方面是因为这两个平台的漏洞数量多,损失重,另一方面是因为这两个平台是很多联盟链的借鉴对象,比如摩根大通开发的 Quorum 联盟链就是从以太坊演变而来,比如多个区块链平台都支持的回滚特性也是受这两个平台的影响。

分析这两个平台的合约漏洞对分析其他平台的合约漏洞有借鉴意义。本文的主要目的是总结这些漏洞类型,举一反三地看其他平台的智能合约安全。

2 平台安全机制总结

智能合约的运行离不开平台支持。平台本身也提供了一些安全机制,为智能合约的运行提供了有限的安全性。

2.1 隔离机制

图灵完备的智能合约意味着可以编写并执行任意的逻辑,包括恶意代码。如果智能合约能直接在区块链节点的宿主系统上运行,恶意代码可以破坏宿主系统的自身数据。因此智能合约必须放在一个隔离的沙盒环境中运行。对于这个问题,每个智能合约的合约平台都提供了虚拟机。

· 以太坊有 EVM

EVM 是内存隔离的。在每次消息调用开始,EVM 都要执行清除内存操作。EVM 虚拟机能够保证不同的合约之间,内存是隔离的。

· EOS 有 EOS-VM

EOSVM 是内存安全的。EOSVM 设定合约运行环境为32位机器,但是EOSVM运行环境需为64位。也正是通过这个机制,可以高效地控制内存使用。首先,EOSVM使用mmap方法为合约映射一块略大于4G大小的虚拟地址,作为合约可访问的数据边界。当合约内访问某个地址的数据时,实际上是访问基于该虚拟地址的偏移。因为合约的运行环境设定是32位,所以不存在越界访问的问题。

2.2 停机机制

智能合约的执行是需要消耗资源的,在区块链世界里,资源非常珍贵,不能任由智能合约无限执行,必须要让合约在一定的条件下停下来,也就是要解决停机问题。停机问题(halting problem)是逻辑数学中可计算性理论的一个问题。通俗地说,停机问题就是判断任意一个程序是否能在有限的时间之内结束运行的问题。

· 比特币

比特币提供的比特币脚本执行机制只能算作智能合约模型,因为比特币脚本的指令有限,且非图灵完备。也得益于这些限制,尚未发现利用比特币脚本实现的攻击案例。因为比特币脚本是非图灵完备的,且没有循环或者复杂流控制功能,不能创造无限循环或其它类型的逻辑炸弹,所以比特币脚本是可停止的。

· 以太坊

以太坊提供了图灵完备的智能合约执行机制,提供的 Solidity 语言和 EVM 能够满足开发者开发出复杂的业务逻辑。相应的,就会面临停机问题。以太坊的应对方式是计价器方式,引入了 gas 机制,在以太坊中,每一个操作都需要消耗一定量的gas,如果gas消耗完了,程序就可以停止下来。以太坊在发起每笔交易时,都会预设一定量的gas limit,如果在执行过程中,gas被消耗完,合约就会停止执行。

· EOS

EOS 和以太坊一样,也提供了图灵完备的合约执行机制,但是 EOS 解决停机问题的方式跟以太坊不同。EOS 采用了计时器方式,EOS 利用控制执行时间来解决停机问题,即用户须要预先抵押 EOS 换取 CPU 资源(以时间为单位,如300ms),在合约执行前在关键位置注入 checktime(),执行时不断检查时间,若已超出时间上限,则停止执行。

· 联盟链

联盟链有很多,如 fabric,quorum,mychain。联盟链的停机问题更多的是靠约束参与者本身达成的。如 Fabric 使用 docker 虚拟机,可支持多种编程语言,但是 Fabric 没有计价器也没有计时器,更多的是依赖审核联盟成员的身份来约束成员不作恶。Quorum 和 Mychain 都参考了以太坊的实现,尽管保留 gas 本身,但是删除了以太坊中 gas 的定价,即 gasPrice = 0,转账时没有矿工费。gas 也就不能作为停机的依据。这两个链也是默认联盟成员自身不作恶。

2.3 跨合约调用控制

跨合约调用必须是确定性的静态调用——在运行前即知晓被调用合约的地址,且调用结果是确定性的。需要注意两个方面:

2.3.1 上下文的切换

上下文切换常发生在跨合约调用中:合约A调用合约B时,上下文应是合约A还是变更为合约B。典型例子,在Solidity中,msg.sender和storage是上下文相关的,语言提供了call, delegateCall 来适应不同的上下文切换的需求。

在EOS中,跨合约调用的上下文永远是被调用合约,由于调用action需要显式指定调用者账户(类似solidity中的msg.sender),且storage与上下文分离,因此没有以太坊那样的问题,使用起来更加简单。

2.3.2 权限控制

权限控制是跨合约调用中不可避免的问题,它关系到用户的数据安全。我们来看下面这个例子:

Bob 调用合约 A 的 hi 方法,hi 中包含一个跨合约调用,形如 B.hello(),调用了合约 B 的 hello 方法。

那么问题来了:Bob 只是希望调用合约 A 的方法,而合约 A 是否有权利以 Bob 的账户调用合约 B?

EVM实际并没有考虑权限的问题。开发者在编写合约时可自行选择 call, delegateCall 中任意一种方式调用其他合约。这么做似乎并没有太大的影响,因为以太坊合约部署后任何人不能修改,也无法升级,只要用户确认了代码是符合要求的,那调用的后果就应由用户自行负责。然而,合约开发者们还是担心资产安全问题,主流的资产标准(如ERC20)都提供了授权的接口,即只有被授权的合约才能进行转移用户的相应资产,以此实现权限的控制。

而在EOS中,由于合约代码可以升级,情况则大不一样:假如合约A的运营方在某次合约升级中(或被黑客攻击)悄悄把hi方法中对合约B的合约调用改为transfer,把Bob的 xxx Token转账给自己。Bob可能并不能及时知晓代码更新,则很有可能在后续调用中触发转账操作,丢失资产。为了解决这一情况,EOS提供了两种方法:

· Bob创建新的权限,并授予合约的eosio.code权限,并指定合约A的hi和合约B的hello,如此Bob账户只能用于调用A.hi与B.hello。

· 新增了require_receipt的通知方法,它使用合约A账户而非Bob账户调用合约B,并修改上下文变量以指明来源方。

以上问题是平台为智能合约提供的最基础的安全机制。这些安全机制为智能合约的执行提供了最基本的保障,然而,事实证明,这些安全机制仍然不足以保障智能合约的安全,和现实世界中涌现出的大量的智能合约漏洞。

3 合约漏洞类型总结

真实世界的智能合约反反复复被攻击,合约漏洞层出不穷,攻击方式多种多样,由攻击导致的损失已超百亿美元。这些漏洞的背后是否有迹可循呢?如此巨大的代价换来的经验是否可以服务于其他链平台的合约安全?本小节试图讨论一下这个问题。我们按照漏洞在各平台的通用性的高低把漏洞类型分为 5 个类型,分开讨论。

3.1 基础类型漏洞类型

基础类型漏洞最为通用,每个平台都会涉及。

3.1.1 整数溢出/Integer Overflow

整数溢出发生的原因是因为寄存器能表示的数值位数有限,当存储的数值大于能表示的最大范围后,数值发生溢出,或称为反转。最大值溢出会变成最小值,最小值溢出会变成最大值。

C++ 语言经常会发生整数溢出问题。看一个具体的案例:

if (p->size() < x*y) {
            return COORDICATE_IS_ILLEGAL;
 }

左右滑动查看完整代码

if (p->size() < x * y) 语句中 x * y 可以溢出,溢出后为一个很小的数,可以绕过 if 语句的检测,进入到后面的逻辑。这种情况在任何平台都可能发生,是平台通用性最高的一类。

3.1.2 随机数可预测/Predictable Random

随机数的使用场景非常广泛,如彩票,游戏,签名算法。虽然随机数很重要,但是在区块链中实现一个基本的随机数并不是一件想象中那样简单的事情。在传统互联网中,随机数是密码学与隐私安全的基础。传统的伪随机数生成算法或多或少与单台机器的物理状态或运算状态相关,这在区块链上是行不通的。对于不熟悉区块链的人而言,这可能有些难以理解:毕竟大多数编程语言都有生成随机数的功能。区块链是一个分布式的系统,它要求各个节点的运算结果是可验证、可共识的,这就使得在区块链上进行随机数生成有了天然的限制。

为了保证每个节点的运算结果是可验证,可共识的,平台通常使用链上的公开信息作为随机源。每个平台都有一些特有的属性,如以太坊有 block.number,block.timesteamp;EOS 有 ref_block_num,ref_block_prefix。这些属性是公开可得的,但是也可以被操控或者预测。如果使用这些属性作为随机数的种子,那么随机性会被破坏。以太坊和 EOS 上有非常多被攻击的真实案例。

其他平台的合约里如果使用了可预测的随机种子,那么合约的安全性也会受到威胁。如波场TVM提供了以下特殊函数来获得链上公开信息:

· block.number (uint): 当前区块号

· block.timestamp (uint): 自 unix epoch 起始当前区块以秒计的时间戳

· block.coinbase (address):当前区块的出块SR地址

由于这些区块基础信息具有一定的随机性,很多合约开发者使用以上的三种特殊函数来进行随机数的生成。其中就包括著名的Fomo3D,seed的计算依赖于区块数据,比如timestamp, difficulty, coinbase, gasLimit, number等字段,一旦这笔交易被打包进区块,这些值就已经确定了。因此,攻击者可以构造如下的合约,在合约内提前对结果进行测算,当结果符合预期时再进行对Fomo3D目标合约的调用,成功破解了Fomo3D。

3.2 函数接口漏洞类型

函数接口相较于基础类型复杂性有所提升。其他平台上也会有函数调用类问题。

3.2.1 外部调用注入/External Call Injection

外部调用在各种语言和平台都极为常见。外部传入的参数可控,攻击者就可以通过控制参数改变合约的执行逻辑。

· 以太坊

以太坊里有 2 个特殊的外部调用传参的入口:

· call:普通调用,执行上下文是外部合约的上下文,会更改调用者为实际调用者。

· delegatecall :代理调用,执行上下文是本地合约上下文,传入的参数会影响本地的数值。

利用这 2 个接口改变调用者或上下文的特性,攻击者传入精心设计的参数可以控制合约的执行逻辑,可以绕过访问控制,修改敏感的状态数据,如账户余额等。具体案例可查看百万合约之母以太坊的漏洞攻防术(上集) call 注入漏洞和 delegatecall 漏洞小节。

· EOS

EOS体系是以通讯为基本的,Action 就是EOS上通讯的载体。EOSIO 支持两种基本通信模型:

· 内联(inline)通信,如在当前交易中处理 Action。

· 延迟(defer)通信,如触发一笔将来的交易。

Inline 通信使用原始交易相同的 scope 和权限作为执行上下文,并保证与当前 action 一起执行。Deferred 跟 inline 通信相比,执行上下文是相同的,不同的是会延迟执行。

跟以太坊 call 调用不同,EOS 体系不改变调用者或者上下文。是否不改变调用者和上下文就免受外部调用注入的影响呢?事实并非如此。

2020 年 10 月 8 日就发生了一起通过精心设计传入的参数进行外部调用注入的攻击。imToken 发推表示,用户报告称 31 万枚 DAI 被盗,这与 DeFi Saver Exchange 漏洞有关。攻击者通过调用 DeFi Saver 的 swapTokenToToken 函数传入 _exchangeAddress,_hide,_dest 为 DAI 合约地址,选择 _exchangeType 为 4,并传入自定的 _callData。通过构造这些参数,成功的控制了合约的执行逻辑,盗取了31万 DAI。

所以,不管是否利用call/delegatecall这类特殊接口的特性,只要合约存在外部调用的接口,都可能受到外部调用注入的影响。

3.2.2 未检查返回值/Unchecked Return Value

未检查返回值在任何平台都可能出现。具体案例可查看百万合约之母以太坊的漏洞攻防术(下集)未检查返回值小节 。

· gasless send 问题:未检查 send 和 call 的返回值,导致双方账目不一致。

· exception disorder 问题:未检查 call 调用的返回值,导致异常不能向上传递,发生逻辑错误。

EOS 尚未发现由未检查返回值引起的安全问题,但是 EOS 也会受此问题的影响。

任何合约不检查返回值,都导致非常严重的后果,如以下代码:

  for (int i = 0; i < static_cast<int>(admin_addresses.size()); ++i) {
    bool exists = CheckAccount(admin_addresses[i]);
    (void)exists;
  }
  CheckAccount(admin_core_contract_id);

左右滑动查看完整代码

CheckAccount() 检查了帐号有效性,但是未对返回值做处理,导致无论帐号是否有效都可以进入到后续的逻辑。这种问题在任何平台都可能出现,要全力避免。

3.2.3 未校验参数合法性/Illegal Parameter

跟外部调用注入不同之处在于,这一类别内外部接口的参数都需要校验。参数的合法性包括:参数个数,参数大小,参数结构,参数有效期等等。如果接口没有校验参数合法性,则会存在较大安全风险。

短地址漏洞 (short address) 成因之一是因为函数中没有校验传入的参数的 size 是否正确。攻击者调用其他合约的时候,特意选取以 00 结尾的地址,传入地址参数的时候省略最后的 00,EVM 在解析数量参数时候对参数错误的补 0,导致超额转出代币。如果校验了传入参数的合法性,则此问题可以避免。此漏洞发现后已很快被修复。

3.2.4 权限控制问题/Access Control

权限控制问题是安全设计类问题,每个平台都需要安全设计。若缺少安全设计,则合约会更容易暴露在攻击者面前。

3.2.4.1 合约对用户无权限控制

敏感函数完全没有权限控制。敏感函数可以是转账类函数,修改全局变量类函数,查询敏感信息类函数。敏感函数无访问控制的问题平台无关,任何合约运行平台都可以存在。

如 2020 年 7 月 1 日,VETH 合约遭遇黑客攻击。此次攻击主要利用 Vether 合约中 changeExcluded 函数的可见性为 external 且未有权限限制,用户可以直接进行外部调用为攻击创造了必要的条件,最终盗走巨额的 VETH。

3.2.4.2 合约对用户权限控制不当

这类问题比起完全无权限控制问题,提升了攻击门槛,但是处境并无差别,因为依然可以被攻破。属于设计上有安全设计,但是实际实现上没有达到预期。

· 以太坊一般使用 msg.sender 验证身份,如果错误地使用了 tx.origin 验证身份,就可以被绕过。具体案例可以查看百万合约之母以太坊的漏洞攻防术(下集)权限控制漏洞小节。

· EOS 的入口函数 apply 里通常要验证发送者的身份,如果错误地使用了 if (code == receiver ) 判断,认证可以被绕过。具体案例可查看EOS菠菜应用篇合约对用户权限控制不当 — apply 函数小节。

权限控制不当的表现方式多种多样,如2020 年 8 月 5 日,Opyn 合约遭遇黑客攻击。原因在于 vaultToExerciseFrom 的校验存在缺陷。此检查未校验 vaultToExerciseFrom 是否是调用者自己,而只是简单地检查是否创建了 vault,导致攻击者可以任意传入已创建 vault 的地址来通过检查。

3.2.5.3 合约滥用用户权限

滥用权限是指合约申请了用户的授权,然后合约自主更新成恶意版本,拿着用户的授权做恶意操作。如果合约升级没有任何管控,合约创建者可以自主升级,用户无感知。这就存在合约创建者可能升级为恶意合约的问题,是一种安全隐患。

EOS 有eosio.code 权限的问题,eosio.code 权限是 dawn4.0 后新增的内部特殊权限,用来加强 inline action 的安全性。inline action 简单来说就是action 调用另外一个 action,具体来说就是一个智能合约调用另外一个智能合约。

inline action 需要向用户申请 eosio.code 权限。用户只有授权 eosio.code 权限给合约之后,合约才可以以用户身份调用另一个合约。任何申请用户 active 权限的场景。由于合约可以升级,即使授权版本的合约经过审计,也无法保证后续升级合约不作恶。

DeFi 中也存在滥用权限的问题。DeFi 合约可能会为了“能更方便地操纵你的资产”的目的,向你申请授权,那这个授权调用又是个什么样子呢?看一个具体的示例:

https://etherscan.io/tx/0x419d17e216cda75dd9635a752e9aedb8f43ed4bfe31a6f75ed8923779c73eb6e

这笔交易表示【0x3693】 这个地址授权给【Uniswap V2: Router 2】合约无限动用自己全部 USDT 的权力。这就带来一个巨大的隐忧,在合约 owner 作恶,或者合约存在漏洞的情况下,用户【0x3693】可能失去全部的 USDT。

因为滥用授权,即便用户没有或者只向合约转入了很少的资产,如果合约有授权转账相关的漏洞,则所有授权过的用户钱包内的资产都将面临风险。即便是没有漏洞,合约开发者也具备更新代码后拿走授权用户钱包内全部资产的权力。所以,用户测要小心授权,避免授予无限权限,定期回收不需要的权限,项目测要最小申请用户权限,避免申请无限权限。

3.3 平台特性漏洞类型

特性比函数调用更为复杂。一些特性会引起安全问题。支持此特性的平台需留意。

3.3.1 利于重入(Re-entrancy)的特性

fallback 特性利于重入漏洞,重入漏洞最先在以太坊平台上被发现。具体案例可查看百万合约之母以太坊的漏洞攻防术(上集)重入漏洞小节。虽然只要有外部调用就存在重入的可能,但是正常情况下,1、调用外部函数的时候会进行代码审计,2、外部函数不会提前预见到调用者的函数名。所以仅利用外部调用进行重入攻击是有难度的。但是以太坊平台还支持 fallback 特性,在两种情况下会调用到 fallback 函数:

1.未找到函数名,默认会调用 fallback。

2.转账操作默认会调用 fallback。

利用 fallback 特性可以大大降低重入的难度。

除了 fallback,目前还发现一种特性也利于重入——合约升级。审计版本是正常合约,但是后来悄悄升级成恶意合约,实施重入。如果其他平台支持合约无感升级,那么重入的风险会非常大。

3.3.2 提前交易(Front-running)特性

提前交易是指攻击者发现有利可图的时候就通过一些手段让自己的交易优先执行。关于提前交易的具体案例可以参考百万合约之母以太坊的漏洞攻防术(上集)提前交易漏洞小节。

交易顺序通常由 3 种方式决定:

手续费

手续费高的优先执行,手续费低的延后执行。以太坊的交易手续费可以决定交易被打包的顺序。所以攻击者发现有利可图的时候就通过增加手续费的方式让自己的交易优先执行。这种方式的代价就是额外付出手续费,技术层面不需要。

先进先出

先接收到的交易优先执行。典型的例子是 EOS。这种情况下,普通攻击者很难通过经济手段让自己先执行,除非通过技术手段攻破出块节点,操控交易顺序。但这个难度是相当高的。

共识节点排序

现在越来越多的公链引入了 PBFT 共识协议或者由 PBFT 衍生的同类协议。使用这种共识协议时,交易顺序通常由主节点决定,如果主节点存有私心,选择了有益于自身利益的顺序,也会产生提前交易问题,且主节点的偏袒不会被发现。普通攻击者无法通过经济手段操纵顺序,通过技术手段,或者社交工程学手段可以达成目的,但是这种手段目前没有发现真实案例。

总结来看,如果交易顺序可以被人为操控,那么会受提前交易漏洞的影响。

3.3.3 延迟交易(Delayed-transaction)特性

EOS 支持延迟交易的特性,通过设置 delay_sec 让交易在指定的时间触发执行。如果 delay_sec = 0,由于EOS执行交易采用FIFO策略,这些延迟交易肯定在其他交易之前执行。大量 delay_sec = 0 的延迟交易可以发起 DoS 攻击。具体案例可以参考EOS菠菜应用篇DoS漏洞/交易延迟漏洞小节。

目前,我们仅了解到 EOS 支持延迟交易,但是可以推断的是支持此特性的平台都会受此漏洞影响。

3.3.4 回滚(Rollback)特性

回滚是一种特性,当函数执行失败的时候回滚到初始状态,一般区块链平台都会支持这个特性。

在以太坊平台有以下方式可以触发回滚:

· require();

· assert();

· revert();

在 EOS 平台有 eosio_assert() 可以触发回滚。

攻击者可以使用这些方式在不符合自己利益的时候把交易回滚,就可以达到一直盈利的目的。比如,彩票合约中,不中奖就回滚。以太坊平台和EOS平台回滚特性的具体案例可以参考百万合约之母以太坊的漏洞攻防术(上集)EOS菠菜应用篇的回滚漏洞小节。

支持回滚特性的平台都会受回滚攻击的影响。例如波场的多个 Dapp 都遭受过回滚攻击:SPOKpark、DappRoulette、Divitron、DiceGame、TRONtopia_ultimate_dice、Wheel Of Fortune DApp,等等。

3.4 充值场景漏洞类型

一个场景可以用到多个特性。充值场景多发生在向交易所或者游戏项目方充值的场景。目前已经发现了 3 类漏洞:假充值,假通知(fake transfer notice),假代币(fake token)。这些漏洞都是利用交易所/项目方校验逻辑存在漏洞, 0 成本获得充值。

3.4.1 假充值漏洞

假充值漏洞是指易所给用户充值的余额和交易所自己实际收到的余额不符,通常情况下是实际收到的金额少于给用户充值的金额,导致交易所损失。

这类漏洞的主要成因是交易所没有正确的校验充值结果。

· 以太坊的假充值的原因是错误的认为交易回执中 status = true 就是充值成功,实际上 status = true 是指没有抛出异常,即使充值执行失败函数 return false,交易回执中的 status 也会是 true。仅判断 status 是否为 true 是不正确的。具体案例可以查看百万合约之母以太坊的漏洞攻防术(上集)假充值漏洞小节。

· EOS 的假充值也是类似的问题。项目方只是对交易是否存在作出了判断。但是交易可能执行失败,交易状态变成 hard_fail。hard_fail 的交易也可以在链上出现记录。所以,把交易是否存在作为充值成功的依据是不正确的。具体案例可以查看EOS菠菜应用篇的假充值漏洞小节。

如果交易所仅仅通过校验中间结果,而不去校验实际收到的金额,就会出现假充值问题。

举一反三来看,USDT 假充值漏洞,XRP 假充值漏洞,门罗币假充值也是类似的原因。门罗币交易所没有检测真实钱包收账余额,而是仅仅通过 show_transfers 来确认用户充值金额,show_transfers 指令并没有跳过重复的交易,每笔重复交易的转账金额也会被计算在内,并最终输出出来,便会出现假充值漏洞,导致交易所给用户充值的余额和交易所自己实际收到的余额不符,攻击者假充值成功后可以进行消费或提款。

所以,涉及到充值场景时,交易所/项目方要仔细辩证校验逻辑是否合理。

3.4.2 假通知漏洞

假通知漏洞可以用 EOS 平台上发生的攻击案例解释,具体见EOS菠菜应用篇的假transfer通知小节。EOS 上可以用 require_receipt(someone) 向任何一人发送转账通知。那么攻击者在自己控制的帐号之间转账,但是把转账通知发送给项目方。项目方没有校验接收方是否是自己,默认为攻击者做了充值操作。

目前以太坊上还没有发现假通知漏洞,但是不代表这种漏洞就不存在。交易所如果没有校验交易回执中的 to 字段是否是自己,也会被攻击者攻击。

在项目方一侧要全面的校验当前交易回执是否跟自己有关。

3.4.3 假代币漏洞

顾名思义,假代币就是拿假钱当真钱花。EOS 平台已经出现了真实的攻击案例,具体见EOS菠菜应用篇假EOS代币小节,以太坊平台目前还没有发现。假代币发生的原因是交易所/项目方没有验证币的真伪,使得攻击者可以用假币换真币。

假代币的问题在其他平台也存在。波场 DApp tronbank 于2019年4月11日凌晨1点曾遭受假币攻击。该次假币攻击事件主要原因在于合约没有严格验证代币的唯一标识符代币 ID,错误地将攻击者自己发行的无价值代币识别为价值85万元的BTT代币,从而造成了损失。

如果平台发行了代币,那么需要告知交易所/项目方,进行真伪代币的校验。

3.5 经典攻击手法的再现

一些经典的攻击手法,如重放攻击,DoS,跟平台特性结合之后,可以引起新的安全问题。

3.5.1 重放/Replay

经典重放攻击的基本原理就是把以前窃听到的数据原封不动地重新发送给接收方。很多时候,网络上传输的数据是加密过的,此时窃听者无法得到数据的准确意义。但如果他知道这些数据的作用,就可以在不知道数据内容的情况下通过再次发送这些数据达到愚弄接收端的目的。

重放攻击在币圈被热谈,是以太坊硬分叉的时候。我们知道以太坊硬分叉出现了ETH和ETC两条链,两条链上的交易数据结构是完全一样的,因此一笔交易在ETH上是有效的, 那它在ETC上同样会被接受,反之亦然。

因为没能提前识别重放攻击的威胁,以太坊分叉时几乎所有交易所也都没意识到这个问题,更没有提前做ETH和ETC分离, 这时候只要有人从交易所提取ETH币,就有可能得到同等数量的ETC币。许多人利用这个漏洞,不断在交易所充币和提币(ETH), 从而获取额外的ETC。

分叉链都面临相似的问题。BCH硬分叉成 BCH 和 BSV 两条链时,也面临相似的问题。在BSV链上交易时,由于相同的地址、算法和交易格式,拿到BCH链上去重新广播,就有可能会被BCH链承认有效,从而进行相同的交易操作。

除了链层面,合约层面也会受重放攻击的影响。如攻击者在以太坊合约上重放用户的签名信息,具体案例可查看以太坊(下集)重放漏洞小节,在 EOS 合约上重放中奖消息,具体案例可查看重放漏洞 — 重放中奖消息小节。重放攻击是一种攻击思想,任何平台任何环节都可能被影响。

3.5.2 拒绝服务/DoS

经典攻击手法里的拒绝服务攻击(英语:denial-of-service attack,简称DoS攻击)是一种网络攻击手法,其目的在于使目标电脑的网络或系统资源耗尽,使服务暂时中断或停止,导致其正常用户无法访问。

DoS 攻击思路用于以太坊可以有多种形式,攻击以太坊的多种特性都可以让以太坊无法提供服务。

· king of ether 代表的 DoS 漏洞类型。这种 DoS 漏洞类型是:依赖外部调用的进展,如果外部调用执行失败,后续的操作也就无法执行,导致拒绝服务。

· GovernMental 骗局代表的 DoS 漏洞类型。这种 DoS 漏洞类型是:依赖外部可以操作的数据,如数组或映射,如果外部操作改变了数据,修改后的数据使得后续的操作因超时或者 out of gas 无法执行,导致拒绝服务。

· freezing ether 代表的 DoS 漏洞类型。这种 DoS 的漏洞类型是:依赖外部的合约库。如果外部合约的库被删除,那么所有依赖库的合约服务都无法使用。

EOS 的 DoS 攻击是利用延迟交易特性和执行交易的先进先出特性。攻击者可以在正常交易里嵌入大量 delay_sec=0 的恶意延迟交易,由于EOS执行交易采用FIFO策略,这些延迟交易在其他交易之前执行。只要这些恶意延迟交易足够多,正常交易会被一直阻塞,无法提供正常服务。

针对这几个特性,分析一下其他平台是否会受影响。

· king of ether 类:要看平台是怎么处理合约调用的,合约 A 调合约 B,如果 B 执行失败,会不会影响 A 的执行。如果影响,那么就跟以太坊一样存在 DoS 攻击。如果不影响 A 的执行,那么就不存在 DoS 攻击。

· GovernMental 类:如果平台有停机机制,如 gas,会受影响。如果平台可以无限执行指令,那么不会受影响。

· Freezing ether 类:要看平台是否支持合约地址可指定。如果合约地址向以太坊一样不可指定,那么合约一旦被删除将无法恢复,会受影响。如果合约地址可指定,那么即使被删除,也可以恢复,不受影响。

· 延迟交易类:支持延迟交易的平台会受影响,反之,不受影响。

DoS 是一种攻击思路,平台尽可能防范,但无法完全避免。

4 小结

本文按照由简单到复杂的顺序排列漏洞类型。越简单基础的类型,平台通用性就越强,如整数溢出,权限控制不当,在任何平台都有可能出现。对于平台特性类,平台支持某个特性,就会受相应特性的影响,进行安全设计时,需要考量平台特性带来的安全风险。一个场景涉及多个特性,场景中比较特殊的是充值场景,有涉及到充值场景的项目方需提高警惕。最后,经典的攻击手法重放攻击、DoS 攻击可能被应用到任何一个环节,出其不意地引发各式各样的安全问题。

本文是智能合约漏洞系列总结篇,主要目的是回顾已经发生的漏洞,总结漏洞根因,并预测其他平台是否会受影响。前车之鉴,后事之师。

参考文献

https://bbs.pediy.com/thread-250801-1.htm

https://bcsec.org/index/eventsearch/name/attack_type/value/回滚攻击/page/1/tag/1

https://www.8btc.com/books/834/blockchain-security/_book/09.html

https://xz.aliyun.com/t/3316

https://www.chainnews.com/articles/558348165334.htm?spm=ata.21736010.0.0.32931a73wOg8Mk

https://www.chainnews.com/articles/180110076539.htm?spm=ata.21736010.0.0.32931a73wOg8Mk


Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1646/