在上周二,十大高市值数字货币均遭遇两位数的暴跌,最小跌幅也超过了20%,虚拟货币的始祖比特币在17日也跌破了10000美元,几乎较去年12月的近20000美元的水平腰斩。而在股市,区块链概念股也经历了过山车般的暴涨和暴跌。区块链无疑成为近几年来的热点之一,除了代币以外,各大公司也在寻求区块链在真实场景的应用,例如金融服务,供应链等;同时也不乏各种各样的公司推出区块链应用,这些应用看起来并没有真正去提高效率或者解决什么颠覆性的难题,看起来更像是蹭热点。当然,这些都是题外话,今天我们也蹭一下热点,来聊聊区块链的安全性。
我们先从比特币说起,一个经典漏洞CVE-2010-5139, Bitcoin wxBitcoin和bitcoind 0.3.11之前版本中存在整数溢出漏洞。攻击者可利用该漏洞通过特制的Bitcoin交易绕过限制并创造比特币(无限造币漏洞)。这比交易发生在2010年8月15日,根据bitcoin.it的wiki中的简介,当天有人在比特币区块链的第74638块上发现了一条让人惊愕的交易,这笔交易里竟然出现了184,467,440,737.09551616个比特币,其中各有922亿个比特币被发送到两个比特币地址。据技术人员分析,黑客是通过利用大整数溢出漏洞,绕过了系统的限制,成功实现了这次攻击。
所幸的是,在发现这一异常现象后的不到半天的时间,比特币核心开发人员就开发完成了比特币补丁版本,并启动了硬分叉,在第74691块,带补丁版本的比特币块链终于追赶上并且超越了原有的出现漏洞的块链,最终是有惊无险地解决了这次比特币历史上最为重大的危机事件。 那么这次攻击事件是怎么形成的呢?
根据bitcoin.it的wiki中提供的资料可以找到当时的这比交易(当然目前这比交易在比特币硬分叉后已不被承认,也在当前的比特币区块链中无法找到)。
CBlock(hash=0000000000790ab3, ver=1, hashPrevBlock=0000000000606865, hashMerkleRoot=618eba, nTime=1281891957, nBits=1c00800e, nNonce=28192719, vtx=2) CTransaction(hash=012cd8, ver=1, vin.size=1, vout.size=1, nLockTime=0) CTxIn(COutPoint(000000, -1), coinbase 040e80001c028f00) CTxOut(nValue=50.51000000, scriptPubKey=0x4F4BA55D1580F8C3A8A2C7) CTransaction(hash=1d5e51, ver=1, vin.size=1, vout.size=2, nLockTime=0) CTxIn(COutPoint(237fe8, 0), scriptSig=0xA87C02384E1F184B79C6AC) CTxOut(nValue=92233720368.54275808, scriptPubKey=OP_DUP OP_HASH160 0xB7A7) CTxOut(nValue=92233720368.54275808, scriptPubKey=OP_DUP OP_HASH160 0x1512) vMerkleTree: 012cd8 1d5e51 618eba Block hash: 0000000000790ab3f22ec756ad43b6ab569abf0bddeb97c67a6f7b1470a7ec1c Transaction hash: 1d5e512a9723cbef373b970eb52f1e9598ad67e7408077a82fdac194b65333c9
通过上面的交易我们可以看到在2010年的8月矿工打包一个区块至少可获得50个比特币,其中50个比特币为币基交易,而剩下的则为其他用户在转账过程中产生的手续费。该比交易在向两个账户中分别转移92233720368.54275808个比特币,但是转出的账户上仅有的0.5个比特币,这比交易怎么可能会被其他节点检测通过并打包至区块中呢?问题在于,当时的比特币系统仅检测了转出的比特币总额是否小于未花费输出,而没有检测每一笔转出是否小于未花费输出,而这两笔交易加起来则发生了大数溢出漏洞,两笔交易加起来为一个负数。那么我们用一个易于理解的表达式来看一下上面的交易:
50.51=币基交易+打包区块奖励
=币基交易+(输入金额-总输出金额)
=币基交易+0.50btc-(54275808+92233720368.54275808))
=50btc+(50btc-(-0.010btc))
=50.51btc
根据https://github.com/bitcoin/bitcoin/commit/d4c6b90ca3f9b47adb1b2724a0c3514f80635c84#diff-118fcbaaba162ba17933c7893247df3aR1013我们来追溯一下,btc是如何修复该漏洞的呢?可以看到该段程序新加了些逻辑判断:即输出总和不得小于0,每一笔交易不得大于21000000。
当然除了该漏洞以外,比特币系统还出现过其他高危漏洞,例如CVE-2010-5138,攻击者通过构造包含有大量的OP_CHECKSIG命令的交易(每笔交易应该只有一个该命令),这导致每个节点都做了额外的不必要的工作,并且可能被用作拒绝服务攻击;而CVE-2010-5140,攻击者在不付手续费的情况下发送大量的低于0.01的交易,比特币网络对于这种交易是永远不会被确认的(网络对于这种小额交易需要额外的费用),这些无效的交易会污染接收这类交易的钱包,即产生了拒绝服务攻击的效果。
除了比特币,我们再来聊一聊关于以太坊的漏洞。近日博客http://blog.talosintelligence.com/2018/01/vulnerability-spotlight-multiple.html中通报了几个关于以太币CPP Ethereum客户端的漏洞,例如CVE-2017-12112 和CVE-2017-12119描述了多个授权绕过漏洞;CVE-2017-12119,这是CPP-Ethereum JSON-RPC实现时存在的问题,特制的json请求会导致未处理的异常导致拒绝服务。当然最著名的一次当属The DAO被盗事件,北京时间2016年6月17日,由于发布的智能合约系统存在着重大缺陷,区块链业界最大众筹项目The DAO遭到攻击,导致百万级别的以太币资产被分离出The DAO资产池。
攻击者组合使用了两个漏洞进行攻击,一个是递归调用splitDAO函数,一个是DAO资产分离后可以防止从The DAO资产池中销毁。根据这两个漏洞攻击者就可以达到两个目的:第一个目的是能够持续运行这个合约,即迭代运行。 第二个是转钱到自己的账户,且不会因为交易费机制而终止交易,即交易成本要低于转账总额。
完整代码下载地址https://github.com/TheDAO/DAO-1.0,通过对源码分析,可以还原的其过程:
提出一个split然后等待直达表决期限到期。(sol, createProposal)
运行split。(sol, splitDAO)
让DAO发送一份额的代币到新DAO(splitDAO -> TokenCreation.sol, createTokenProxy)
确保DAO在(3)之后在更新你的余额之前尝试发送给你收益。(splitDAO -> withdrawRewardFor -> ManagedAccount.sol, payOut)
当DAO在步骤(4)时,以与(2)相同的参数再次运行splitDAO 。(payOut -> _recipient.call.value -> _recipient())
DAO将会发送给你更多的子代币,并在更新你的余额之前撤销对你的收益。(sol, splitDAO)
返回(5)
让DAO更新你的余额。因为(7)返回到(5),所以这将不会发生。
除此之外还有很多区块链的应用在实际业务的实现上出现过严重问题,例如一个基于以太坊区块链技术的彩票系统(https://github.com/slotthereum/source/issues/1)因为两个实现上的错误而导致提前预测彩票结果。虽然区块链技术以密码学为基础,但是其安全性依然依赖于具体业务的实现,以及传统的基础安全来保障。
参考地址:
https://en.bitcoin.it/w/index.php?title=Value_overflow_incident&redirect=no
http://blog.talosintelligence.com/2018/01/vulnerability-spotlight-multiple.html
https://github.com/bitcoin/bitcoin/commit/d4c6b90ca3f9b47adb1b2724a0c3514f80635c84
http://vessenes.com/more-ethereum-attacks-race-to-empty-is-the-real-deal/
http://www.8btc.com/thedao-expolit-analysis