今年4月起,一些Token相继开始自我膨胀,一夜间供应量突破上限千亿倍,虽是发行者无心之失,并且挽救妥当,没有直接损失,但此举却激起持币者的恐慌,币价大跌。
始作俑者是黑客,他们瞄准的是这些项目中的溢出漏洞,试图一击必杀。
一、什么是溢出漏洞?
溢出漏洞的全名是缓冲区溢出漏洞,缓冲区是内存里的一块地皮,用来存放变量,变量是运算过程中变来变去的值,比如当前时间,每过一毫秒,这个变量就更新一小下。
如果把缓冲区看成一副背面向上的麻将牌,那么变量就是翻出来的牌,变量值就雕刻在上面。计算过程变化,变量也会变化,比如发财会变成红中、甚至两个东风。
但是,不管变量怎么变,程序预先给变量设定好的边界永远不变。
比如上图,如果运算中变量变得特别大,超出预先设定好的边界(黄框),那么计算机就把超出边界的值写到黄框外面(發的右边),但是,等其他程序要去调用这个变量时,读到的只是黄框内的数值。这样,不单读取的数值会出错,而且可能覆盖其他变量的值,导致五迷三道的错误。
明白这个原理,我们很容易理解面对漏洞的黑客是如何下手的。
转账金额由转账者输入,传给变量,在代码里,只要设置转账金额小于代币总量即可,本来不会出现溢出。但为提高操作效率,很多项目都有多人转账(或批量转账)的功能,即:转账总额 = 人数 × 每人转账金额,同时,判断能否转账的条件是:转账总额小于账户余额。
那么,漏洞来了:
黑客把每人转账金额设置成天文数字,那么由于溢出漏洞的存在,正确的数字溢到边界外,导致系统看到的转账总额很小,误以为转账总额小于账户余额,执行转账,最终给目标账户按每人转账金额入账,但程序自己却无法意识到每人转账金额是一个天文数字。
上面一段没有看懂的话,举例说明你肯定能懂:假设账户余额是10。在没溢出前,转账总额计算结果是9999,10小于9999,代码判断不能转账;如果转账的总额为10001,但此时就溢出了,计算机不会拿10001去和10比较,而是拿0001去比较(也就是1)去和10比较,1小于10,所以执行转账,溢出的悲剧就此发生!
那是不是溢出漏洞没法救呢?
不是的,只要增加一行代码,做溢出判断即可。但是,在千万种加密数字货币中,总有一些项目开发者因为token上市太激动而疏忽了溢出判断代码,也总有个别项目会被专业漏洞挖掘师傅戳中,悲剧由此而生。
那么,我们能不能从根本上防止溢出漏洞呢?
答案是不能。
二、溢出漏洞的根源很多项目发行时使用以太坊网络,而以太坊网络使用的是账户余额模式,即用最新数据覆盖原有数据,账户余额模式本身没有问题,但它的复杂性却是溢出漏洞的根源。因为所有交易以结果为准,而过程却需要人为控制,也就是说,万一代码里藏着致命漏洞,作恶者可以利用它操纵交易,最终让项目雷暴。
没有雷暴的项目不代表必然没有溢出漏洞,可能问题现在刚好还没被发现。
与以太坊账户余额系统相对应的模式是UTXO,UTXO的核心是基于交易记录,模型保证每笔交易的进出总量相等,而且可追溯,UTXO注重交易本身的安全性,天然没有这道裂痕,这就是比特币在安全性上高出以太坊一个头的原因:比特币使用UTXO模式。
以太坊的创始人Vitalic曾出于安全上的考虑,想把以太坊从账户余额模式换成UTXO模式,但执行下去发现,一旦为了安全性使用了UTXO模型,就很难兼顾以太坊性能,换句话说,也就是慢。
UTXO必须得靠以太坊底层的支持,但是随着开发的逐步深入,Vitalic发现其困难深不见底,不单数据库较大,而且很难实现接口程序,相当于一场大规模变性手术,需要几年时间和几亿元的投入,不知道V神在拿起刀的一刻还有没有信心。
那么,我们能否借鉴主流操作系统的经验来杜绝溢出漏洞呢?答案是很难。
三、不可避免的裂缝我们平时使用的操作系统(如:Windows、MacOS、Android和iOS等)处处都有缓冲区,本来会有很多溢出漏洞,但由于大量工程师多年的维护,主流操作系统的溢出漏洞少之又少。可是,新发行的代币代码中,溢出漏洞被发现的概率天然很高。
代币的安全漏洞分两类:
第一类底层公链的漏洞,即以太坊的漏洞,一旦被黑客抓包可能血流成河。这方面以太坊防守得很好,这两年扛起很多币的ICO,没有发生过事故,不过这只代表过去,随着以太坊的升级之路往前走,新代码随时会伴随新漏洞。
第二类是代币本身的漏洞,开篇的漏洞就属于这种,黑客攻击起来最多损伤一种币,其他币不受影响,需要开发者自己小心。
考虑到底层公链以太坊没有使用UTXO结构,随着升级漏洞难免会出现,基于以太坊发行的新币流通时间较短,所以可以判定,溢出漏洞不会停止,甚至会圈养一批职业漏洞挖掘者。
职业漏洞挖掘者天然会遭遇职业交易所的抵抗,比如延长提币时间,回滚非法交易等,直接损失也许能止住,但从那刻起,持币者心里的恐惧就会像蘑菇云一样腾起,久久不散。
结语人们最早发现溢出漏洞是在1972年,而后十多年的时间里没人把它当回事。1988年有个叫莫里斯的大一新生码了一篇99行的代码,利用溢出漏洞造成6000多台计算机宕机昏迷、损失过亿,最终把自己送进大牢。
而后有很多人依靠溢出漏洞潜入学校系统,修改成绩。因为损失有限而且难以察觉,在很多行业里,溢出漏洞并不是什么大不了的事。但是,金融领域却完全不一样。
金融机构会对代码进行安全审计,避免低级失误摧毁高级项目。但是,在加密数字货币世界,一盆代码刚写完只需拍一下回车就能往市场里端,很多投资人背着双手听完项目发布会,心潮澎湃面带绯红地下单,而对审核代码这件事却缺乏重视,悲剧自然不可避免。
没有人可以保证开发人员都拥有完美的逻辑,没有人可以保证测试人员可以测出所有的错误,但代码审计可以在一定程度上保证合约安全性,否则你必须承担像某些项目那样的风险:在代码里隐隐地藏着一些白皮书里没写的帝王级功能:随意发行、冻结或销毁任何人账户里的Token。
代码审计需要专业度,甚至出高价请专业的审计机构检查都显得很便宜。但即使这样,也很难避免藏在深处的其他漏洞。而对于我们这些非专业人士而言,最好的代码审计员是时间,只有时间才能帮我们筛出没有裂缝的项目。
所以,不用着急,慢慢来。
附:
某个被溢出攻击的项目代码,看完这篇文章后,耐心读读,你一定能读懂。
那些有问题的代码产生了有问题的结果,被以太坊的网络一直记录着,永远没有办法抹去。
本文来源于:白话区块链