当前位置: 新闻资讯 区块链 区块链生态安全指南:智能合约的攻与防
智能合约是部署在EVM上,最终部署在区块链的公链上的。可以这样说,比特币引领区块链技术,而以太坊复活了智能合约。
其实智能合约的发展是非常稳定的,通过猎豹区块链安全中心在最近一个月的统计中可以看到,智能合约平均每天的增量在2200左右,增长还是非常稳定的。
智能合约现在的应用场景有很多,比如说去中心化的钱包、代币发行、众筹基金,还有现在非常火的FO3D、以太猫之类的区块链游戏,随着智能合约如火如荼的发展,我们也开始看到很多关于智能合约的攻击事件,以下是11年到18年智能合约安全事件统计。
其实智能合约安全事件在所有区块链安全事件的比例非常低,大概只有6%,但是损失统计却达到了12.4亿美元,说明智能合约虽然事件比较少,但造成的后果是非常严重的,因为智能合约对数字货币实时会交易造成非常大的影响,所以我们应该对智能合约的安全高度重视。
我们非常熟悉的The DAO事件,发生在16年的6月,造成了以太坊的硬分叉,接近1/3,近6000万美元的资产损失。
17年7月parity钱包的多重签名漏洞,造成了150万以太币的损失。
18年4月,众所周知的美链事件,使BEC 的10亿的资产在几天内消亡,类似的还有smartmash事件,在溢出和权限控制出现了问题,造成了1.4亿美元的重大财产损失。
伴随着智能合约出现的这些重大事件,让我们不禁思考,在solidity的代码层面到底出现了什么样的问题,又有哪些智能合约的漏洞类型。
我在这里简单的总结了智能合约TOP10的攻击类型:重入攻击、权限控制、整型溢出、未检查的call返回值、交易顺序依赖、时间戳依赖、条件竞争、短地址攻击、可预测的随机处理等。
今天我们就来简单聊一聊三个常见的漏洞类型:
大家都知道汽车的里程表,在里程碑表上的范围是从0到9999的数值,当里程数值达到极限的时候,再增加时就会重新归零,其实这个就是生活中的整型溢出。
而在EVM和智能合约的漏洞当中,EVM的数据位数就相当于我们里程表的1-999999,在EVM上的数据位数是0到255的取值,向上加1就会溢出,造成归零的情况,到了0之后,减1之后,就会产生下溢,然后变成极大值。
数字加法、数字乘法,会出现上溢的问题,减法会产生下溢的问题,这是我们写代码时要注意的。
在著名的美链事件中,大家可以看到第257行的代码,简单的一个amount类型,amount参数等于局部变量CNT的值,这样简单的一个操作,对CNT是进行了验证了的,但是对amount没有进行验证。
所以在实际的攻击过程中,攻击者通过传入了一个非常大value值,导致amount溢出,当这个参数可以使amount的值为零,然后绕过检测,虽然下面他还做了很强劲的检测但是因为amount达到零之后,它绕过了这个检测,所以这个结果就是,黑客获取了非常大value值的数字货币,但是他自己的钱包里却没有支付一分钱。
我们可以通过etherscan来回顾这些攻击事件,仔细分析一下攻击过程到底是怎么产生的。
还有类似的edu事件,也是因为整型溢出,当然它还有数据的权限控制的问题,这两个事件有共同的地方,也有不一样的地方。
下面我们就说一下权限问题,其实权限问题包含种类比较多,这里我只简单的介绍一下构造函数的失控的问题,构造函数其实是智能合约里一个非常重要,也非常特殊的函数,它是用来初始化这个智能合约的所有权的。
智能合约在0.4.2之前的版本中,要求合约名与构造函数名严格一致,如果他们不同,构造函数就可以被其他合约所调用,通过这种方式,恶意的攻击者就可以获取了我们当前智能合约的所有权,然后进行一些其他的操作。
在MorphToken这个合约上,因为两者的大小写不一致,导致了攻击事件的发生。
接下来介绍一下重入漏洞,重入其实就是递归,就是对于一个函数的循环调用和对自身的循环调用。代码合约的withdraw函数其实是可以进行递归操作,存在的问题在于,事先没有进行先判断后转账的操作,所以可以给攻击者产生这样可以成功进行重入漏洞的一个条件。
在靠value的时候可以调用fallback函数,攻击者通过这样的合约,可以循环的调用withdraw,也就是提款操作。
经过这样一个复杂操作,循环之后,攻击者可以通过自己的合约,把整个公共的合约的所有的数字货币转到自己的钱包当中。著名The DAO事件,就是因为这个漏洞产生的,当时也损失了大约1/3,约1.15亿美元当时的市值的财产。
简单了解以上三类漏洞之后,大家会考虑,对于这些漏洞我们应该如何防范? 比如说刚才的这个整数溢出漏洞,我们可以通过对于参数的一个详细控制,或者说利用一些第三方的safemath库来保证数字货币的安全。
还有刚才第二个所说的权限控制,我们要注意编码规范,保证合约名与构造函数名相同。如果现在使用构造函数,我们建议使用constractor来进行一些复合函数的创建。
到最后的重入漏洞,要注意三点,也是刚才说的,金额转移变量之前,一定要先进行进行金额转移操作,而后进行一些相关的循环操作。
再比如刚才的那个call.value它是没有限制gas上限的,我们可以用这个transfer操作的2300的gas上限,来保证这个循环不会递归的发生。
最后我们还可以在代码中的互斥锁来完成这些功能。
在我们统计的数据上,现在的智能合约已经达到20万之多。单纯的从第三方的人工角度审计来说,可能已经跟不上合约发展了,所以自动化审批现在也是大家比较关注的。
自动化审计大约分成三种类型,第一种是基于特征码代码的匹配,第二个就是基于形式化验证自动化检测,第三种就是基于符号执行、符合抽象的自动化审批。
智能合约的特征码匹配与传统的特征代码类似,都是我们对智能合约代码的检测和抽象,通过对检测模块的源码进行匹配。
但是智能合约还存在着一个问题,其实合约在公网上并不都是有明码的,据我们统计,大约只有40%左右是有明码的。所以对于特征码匹配这一块,我们可能要结合一些逆向来进行一些特征码的匹配,所以说具有一定的难度。
再介绍一下基于符号执行和包括抽象的自动化检测,目前这类检测工具比较多,比如比较知名的Mythril、Oyente、Maian,以及其他的符号执行的工具。其实智能合约的源码,是通过SOC的编译器编译成OPcode,也就EVM,类似于汇编执行的这种操作码,然后再通过CFG(控制流程图)的建立,通过这种建模的形式把它转化成图,让我们更能清晰地理解opcode源码逻辑,比如出现了判断的时候,我们能更清晰的分析出这些问题。
到最后我们可能就是符号抽象的分析,就是Securify,在智能合约中与其他源码有一个非常不一样的地方,就是智能合约的这个代码的耦合度是非常低的,我们原来的代码耦合度非常高,所以在智能合约的检测当中,我们可能就针对个别模块进行检测,将各个模块进行建模,然后匹配出他的一些恶意攻击方式。
这是与传统资自动化检测分析不一样的地方,Securify也是通过这种方式,完成自己符号抽象分析的这种检测。
最后我还想再问大家一个问题,从项目安全的角度,如果我们解决了代码问题,就真的能保证一个项目的安全了吗?
我觉得不是这样的,智能合约作为项目很重要的一个点,肯定是不能忽视,但是智能合约还会存在一些其他的问题。一个项目,它不仅仅包含智能合约,还有后面的技术团队、或者白皮书质量,以及社交网络的舆情,这些都可能对我们的这个项目安全产生一定的风险。
比如说技术团队中,我们自己统计过,有很多技术团队造假,这些个人简历其实写的并不真实,或者是白皮书相似度非常高,但是质量却非常低。
再到薅羊毛现象,一个项目的空投,一个地址,可以薅走很多的币。比如说在官方的地址下,输入一些利用这些官方的名字,假装发现一些空投,然后骗取正常用户的以太币的行为。
所以我们觉得,区块链安全是一个多维度的安全,光光从一个智能合约或者代码的角度,并不能保证整个项目的安全,我们应该更多的结合大数据技术、或者是人工智能技术,或者说npl这样的自然语言分析技术,去检测项目的安全性。