以太坊智能合约的安全问题

版权声明:本文为博主原创文章,转载请注明来自 http://blog.csdn.net/hello2mao https://blog.csdn.net/hello2mao/article/details/80093877

一、背景

本周基于ERC20的BEC和SMT出现重大漏洞:
(1)4月22日,BEC出现异常交易情况,从短短两天之间,65亿市值迅速归零,一夜之间倾倒。
(2)4月25日,SMT遭到与BEC类似溢出攻击,凭空产生了无限量的SMT代币。
这两次攻击都是利用了智能合约中的溢出漏洞。
来自新加坡和英国的几位研究者表示,他们采用某工具分析了近100万个智能合约,其中3.42万个存在漏洞,有2365个来自不同的项目。这就意味着,约有3.4%的智能合约很可能因存在漏洞而遭到攻击或失败。易受攻击的这部分合约包含近4905个以太币,价值达到了440万美元。

二、已知的漏洞和陷阱

2.1 私钥泄露

使用不安全的私钥是一个常见的用户错误案例,甚至比漏洞还要常见。
最常见的情况是在产品中使用生成地址(例如用在测试工具中的,如Ganache /TestPRC)。这些地址是由公众所知的私钥生成的。一些用户甚至在不知不觉中将这些密钥导入钱包软件,通过原始种子生成秘钥。
攻击者检测这些地址,任何转移到这个在以太币网络主地址的金钱都会立即消失。

2.2 可重入性和竞争条件

如果一个功能模块在完成前调用了很多次,可能会出现意想不到的行为,在某些意想不到的行为中可能存在可重入性漏洞。
这里写图片描述
call.value()函数可以导致合约外部的代码执行。我们可以把调用者假设成一个数字加密货币钱包软件,也可能是其它合同。
如果这个访问者是另一个合约,这就意味着这个合约的fallback函数被执行了。fallback函数的目的就是收到资金。
一个流氓合约,实现一个叫做payout()的回调函数,在余额设置为0之前,再次递归调用payOut(),从而获得比现有余额更多的资金。
这个问题的解决方案是使用可代替的函数sent()或者transfer()。通过为基本核算支付足量的gas,任何再次调用payout()的尝试都将会失败,从而可以防止递归调用。另外(或者是再者),操作的顺序可能被翻转,例如在金钱交易之前把余额设为0。

2.3 下溢/上溢

以BEC漏洞为例:
这里写图片描述
BEC 智能合约中的 batchTransfer 批量转账函数存在漏洞,攻击者可传入很大的 value 数值,使 cnt * value 后超过 unit256 的最大值使其溢出导致 amount 变为 0。什么意思呢?这个漏洞完全可以不需要转出你钱包的币,而自动会生成一个天量币到对方的钱。

2.4 交易顺序依赖合约

交易顺序依赖就是智能合约的执行随着当前交易处理的顺序不同而产生差异。例如,有两个交易T[i]和T[j],两个区块链状态S[1]和S[2],并且S[1]状态处理完交易T[j]后才能转化为状态S[2]。那么,如果矿工先处理交易T[i],交易T[i]调用的就是S[1]状态下的智能合约;如果矿工先处理交易T[j]再处理交易T[i],那么由于先执行的是T[j],合约状态就转化为S[2],最终交易T[i]执行的就是状态S[2]时的智能合约。

攻击方法举例:
攻击者提交一个有奖竞猜合约,让用户找出这个问题的解,并允诺给予丰厚的奖励。攻击者提交完合约后就持续监听网络,如果有人提交了答案的解,此时提交答案的交易还未确认,那么攻击者就马上发起一个交易降低奖金的数额使之无限接近0。当矿工处理这两个交易时,当前交易池就有两个待确认交易:一个交易是提交答案,一个交易是更改奖金数额。如果矿工先处理的是敌手更改奖金的交易,而敌手可以通过增加交易费用让矿工先处理自己的交易,那么等到矿工处理提交答案的交易时,答案提交者所获得的奖励将变得极低,敌手就能几乎免费的获得正确答案。

2.5 时间戳依赖性

矿工处理一个新的区块时,如果新的区块的时间戳大于上一个区块,并且时间戳之差小于900秒,那么这个新区块的时间戳就是合法的。这是以太坊协议所规定的。时间戳依赖顾名思义就是指智能合约的执行依赖当前区块的时间戳,随着时间戳的不同,合约的执行结果也有差别。

攻击方法举例:
如果有一个抽奖合约,要求由当前的时间戳和其它可提前获知的变量计算出一个“幸运数”,与“幸运数”相同的编码的参与者将获得奖品。那么矿工在挖矿过程中可以提前尝试不同的时间戳来计算好这个“幸运数”,从而将奖品送给自己想给的获奖者。

2.6 误操作异常

在以太坊中,一个合约调用另一个合约可以通过send指令或直接调用另一个合约的函数。然而在调用过程中可能会出现错误,调用的合约就会回退到之前的状态。那么这个异常就可能无法很好地被调用者获知,这取决于调用方式。例如,通过send指令调用的合约应该通过检查返回值来验证合约是否被正确执行。

攻击方法举例:
有个名叫KingOfTheEtherThrone(KoET)的智能合约:用户可以通过一定数量的以太币成为“以太币国王”,支付的数额由现任国王决定。很显然,当前国王可以通过买卖国王获得利润。当一个用户声称为国王后,合约就发送赔偿金给现任国王,并指定这个用户为新的国王。然而,这个合约并没有检查支付赔偿金的交易的结果。 这样一旦合约在执行过程中产生了异常,现任国王就有可能同时失去王座和赔偿金。
可能的攻击方式就是敌手故意超出调用栈的大小限制。以太坊虚拟机规定调用栈的深度为1024。敌手在攻击之前,首先调用自身1023次,然后发送交易给KoET合约,这样就造成了合约的调用栈超出了限制,从而出现了错误。合约出错后,因为这个合约没有检查合约的返回值,那么如果合约在发送赔偿金给现任国王的过程中出现了异常,那么现任国王极有可能失去王座和赔偿金。

三、如何写安全的智能合约

(1)以太坊智能合约安全监测工具 Oyente
(2)富士通推出新技术检测以太坊智能合约漏洞
(3)smart-contract-best-practices:https://consensys.github.io/smart-contract-best-practices/

四、参考

【1】https://www.securityartwork.es/2018/03/20/security-of-blockchain-based-smart-contracts-ii-known-vulnerabilities-and-pitfalls/
【2】http://tech.ifeng.com/a/20180423/44965575_0.shtml

猜你喜欢

转载自blog.csdn.net/hello2mao/article/details/80093877
今日推荐