前言

最近一直在研究挖矿方面的攻击手法,看了一下时间戳方面的内容,在这里记录一下其中的利用点

一些思考

在进入正题之前我们不妨先一起来想一想,可以看作是个引子吧。

如果你对区块链有所了解,那么我想你一定听说过51%攻击,一般来说也就是攻击者掌握了51%的算力时他就有能力掌控区块的走向,然后就能实施双花攻击等等攻击手段来牟取利益,当攻击者算力足够时这种攻击几乎一定是可以成功的,当然了,前提也是你的算力足够,一般而言这是比较困难的,尤其是在区块链发展的早期,还没有出现专业的ASIC矿机的时候,大家的挖矿设备可能还是CPU和GPU,不过现在随着ASIC矿机的快速发展以及大型矿池的出现,挖矿已经开始趋于集中化了,很多新兴的币种其实已经暴露在危险之下了,尤其是那些挖矿算法简单继承自比特币的代币,很容易就会受到来自比特币挖矿大军的直接攻击,目前采用pow共识的代币很多都会将自己的挖矿算法进行调整以对抗来自矿机的威胁,就像六个月分叉一次的门罗币,以拒绝矿机对算力的垄断,也有些代币在这样的对抗里直接死亡,因为它的算力几乎都来自于矿机,一修改算法直接算力接近清零,难度无法调整,从而宣告死亡。不过到这里貌似已经有点扯远了,事实上随着各种攻击手法的展现,要完成51%攻击的条件并不需要你一开始就掌控51%的算力资源,比如大矿池通过自私挖掘理论上可以凭借33%的算力慢慢增长到51%,这是在挖掘策略上做文章,下面我们主要是分析一下如何挖矿时在时间戳上做文章。

我们知道在比特币的区块头中是包含了时间戳以及区块难度等信息的,而比特币的难度调整机制实际上就是基于区块头里的时间戳的,这时候心思宽泛的小伙伴可能就会想了,我何不自己分叉出一条链,伪造一下时间戳,将难度调整到最低,挖块的速度是秒级的,这样很快就能追上实际出块时间为10分钟的主链,然后把这条链广播出去,按照最长链原则的话自己挖的这条链将成为主链,这样所有的挖矿奖励都是自己的,而且打包什么交易也是自己说了算,岂不美哉。不得不说这个想法确实挺不错的,要是在比特币运行的初期说不定是可以取得成功的,因为最开始的比特币实际上遵循的还是字面上的最长链原则,这就意味着当出现了一条区块比主链多的分叉链时是可以取代主链的,不过很快比特币就对这进行了修复,之后节点对于链的选择是基于该链累积的困难度的,也就是投入算力最多的那条链,这样你在低难度下快速挖到的一条长链是不被节点承认的,那你又会说我能不能篡改一下区块里记录的难度信息呢,可惜的是这样的hash头是无法通过难度校验的,因为hash值前面的含0的位数是对不上的。

那么是否我们的这种想法就失去价值了呢,当然没有,利用我们所制造的这一串低难度的链我们可以对网络上的节点进行Dos攻击,因为你将这条链广播给节点后节点是需要先对其进行验证再决定要不要抛弃的,虽然无法让这条链取代主链,但是我们可以占用节点的资源,当我们同时发送很多这样的低难度链时就可以占满对应节点的计算资源,如果把网络上的诚实节点都D掉了那我们就可以干很多坏事了,这也是个很邪恶的想法啊。(事实上比特币中可利用的Dos攻击类型非常多,这里就不涉及这么多了,比特币也在不断加强安全措施以进行限制)

所以很快比特币也推出了应对措施,在代码中添加了checkpoints,即检查点机制,也就是在代码中直接硬编码一组区块及其hash,当节点同步区块进行检查时会直接检查checkpoints是否能对得上,如果对不上就可以确定这条链是不诚实的,直接丢弃即可,一般检查点都设得离当前区块比较远以避免冲突,这样的话检查点前面的区块就是不可篡改的,你也就没法从头构建一个低难度的链了,如果你选择从检查点开始挖的话那么你就得承受该区块对应的挖矿难度,而比特币的难度调整又是如此缓慢,除非你手上的算力资源非常大,仍然可以进行高速挖矿并进行时间戳修改,不过这样的成本是比较高的,尤其是随着检查点的不断更新,难度也越来越大。

不过随着比特币安全机制的不断完善,比如设定最小累计工作量,硬编码一个工作量,让取得的链需满足累计的难度大于这个值,这其实跟检查点的思想差不多,但是检查点机制更加死板,必须认准前面就得是那些链,而且它的存在也影响了同步时进行签名验证的优化速度,所以在去年的一次更新中它开始被assumevalid即假定有效块的机制所取代,事实上检查点自身也很久没更新了,最终定格在了295000块上。至于假定有效块就非常灵活了,如果你知道一个有效区块并提供给客户端,那么在同步检查时节点就会跳过对有效区块前区块的签名检查,从而加速验证过程,如果你不知道任何有效区块,客户端里也默认了一个有效区块,取的是453354,同时如果头铁想进行全节点验证也可以将assumevalid置0。

前面也提到assumevalid机制比较灵活,这一点还体现在它并不强制该有效区块的存在,如果你能提供一条链满足验证条件却不含该区块,节点也会承认该链,你可能会想,啊,这岂不是很危险,万一有牛b的攻击者挖出了一条分叉的长链那岂不是可以取代主链,没错,如果真有这么大算力的攻击者存在那他确实可以逆转比特币,不过面对这种实力的攻击者你确实也是没啥办法,所以讨论这也没啥意义。有人可能又会想了何不干脆对已确定的区块来个锁定机制,将目前达成共识的区块的前一部分区块设定为不可篡改,比如6个之前或者10个之前的区块,这样不就高枕无忧了,嗯,又是一个很正常的想法。

说实话这种想法应该是有点偏离比特币的初衷的,也就是说没那么自由,在代码层面的强硬干预有点过多了,此外还得考虑一个严重的问题就是一旦节点间出现了隔离的情况,比如中国的节点和美国的节点出现了隔断,这样就会出现各自挖矿,产生了两条链,也就是分叉,这时节点间突然通了你不妨想想会发生什么,在你前面所想的对区块进行锁定的情况下,对,这将出现硬分叉,这可不是大家愿意看到的情况。你可能会说这么多节点怎么会出现这样的隔离,确实一般情况下是很少会这样,但是如果有攻击者进行操纵那么就有一定的可能了,比如日蚀攻击这样的巧妙攻击,所以这种想法虽然挺好却并不那么理想。

貌似扯的有点多了,前面已经说到了挖掘一条低难度的链条已经没什么大用了,那么是不是时间戳的欺骗就没有价值了呢,当然不是,通过对区块中时间戳的修改我们可以干扰节点的时间从而对其进行欺骗,下面我们就进入正题

Time warp attack

实际上对应的时间戳欺骗的攻击想法在很早就被提出来了,详情可见此博客,Timejacking & Bitcoin

这种攻击的中心思想跟前面一样,就是提供错误的时间戳,在挖矿时选择给节点提供时间提前的时间戳,这样会有什么后果呢。

因为节点对于提交的区块的时间戳是有选择的,它将拒绝比它的网络时间要晚2小时的区块以及比过去的11个区块的时间戳的中位数要早的区块,那么我们就一直发送时间戳提前的区块,一旦我们的算力足够,让过去的11个中的中位数是我们发送的恶意区块,比如我们发送的恶意区块都将时间戳提前了10分钟,那么这十一个区块的分布可能就是这样

[0,+10,0,+10,+10,0,+10,0,+10,+10,0]

你可能会说了这样的话岂不是会出现后一个块比前一个块的时间戳还要早的情况,没错,这种情况在实际中也是完全存在的,毕竟这是个去中心化的系统,没有使用什么第三方的时间,就是用大家各自的时间,这种情况是完全可能存在的

上面这11个块的时间戳的中位数就是+10,也就是提前了十分钟,其中的0表示的是诚实区块,这样后面的区块那些诚实矿工提交的区块就会被节点拒绝,看起来似乎要成功攻击我们需要的算力有点大,得一半以上了,不过这里只是最一般的情况,本身其他矿工的网络时间可能就是存在差别的,另外通过这样的攻击降低了区块的挖掘难度对参与的矿工都是有利可图的,你完全可以去拉拉同盟甚至收买一波。

其实在前面那篇博文里我们也能看到,还可以选择对矿工发送时间戳增加较少的块,这样的块会被节点拒绝却会被矿工所接受,从而进一步削弱诚实矿工的数量,增加自己的掌控力,这样每一轮难度调整都会让挖矿难度下降一档,长时间的攻击后就可以收获大量的挖矿奖励,当掌控的算力足够时还可发动双花攻击。

不过一般而言这样的攻击的发动是比较慢的,尤其是对于比特币这样的算力庞大,区块难度调整周期又比较长的系统,而且这样的攻击也很容易会被侦测到从而被节点的管理员予以打击。

攻击比特币确实是非常困难,那么其他的代币又如何呢,其实有很多代币都曾被担忧过会遭受这样的时间戳劫持攻击,连门罗币都差点被爆出遭受该攻击,当时的震动可见这里的分析,What the Hell is Going on with Monero?

虽然最后并没有发生什么,不过还是值得大家警醒,然而今年还是发生了一起典型而巧妙的时间戳修改攻击,严格意义上也可以说是多起,不过手法没什么区别

XVG的案例

今年4月份报告了一起利用时间戳攻击的攻击事件,黑客攻击了一个相对较小的加密货币verge(XVG),其实这样的事件会发生完全是verge的开发人员自身的不注意,虽然它的一部分代码继承自比特币,比如时间戳的检查上,但是在挖矿算法以及难度的调整上跟比特币的机制有着较大的区别,所以攻击过程有点不一样。

多个挖矿算法

XVG支持了多达五种挖矿算法来进行挖矿,分别为Scrypt,X17,lyra2rev2,MYR groestl,blake2s,在他们的考虑里这主要是为了防止单一算法下对应的ASIC矿机的算力垄断,因为采用同样挖矿算法的其他代币的大矿主一旦进场可能就会取得区块的控制权,所以所以你采用上述五个算法中的任何一个都能参与挖矿,而且这五个算法的挖矿难度是分别计算的,这一点非常关键。

DGW难度调整

然后是XVG的难度调整算法,跟比特币每2016个区块才调整一次不同,XVG采用了称为Dark Gravity Well的难度调整算法,该算法在每个区块都会调整一次难度,它会取前面12个采用相同挖矿算法的区块难度计算平均值,然后看出块时间与理想出块时间之比,理想出块时间为30s一个块,对于单个算法而言也就是30s*5,若大于理想出块时间三倍以上则将区块难度降为平均值的三分之一,对应的相反则增大三倍,看起来这样似乎是没有必要的,这种难度的频繁变化看着似乎不太靠谱,不过对于小型代币这大概也算是无奈之举,采用这种算法可以避免大量算力撤离或者说离线,比如在某些算法调整的硬分叉之后挖矿陷入停滞的情况,因为挖矿难度没法调整,所以仅存的算力可能难以维持一个难度调整的周期,从而导致大量交易堵塞,代币死亡。

错误的最长链原则

其实verge犯的最大的错误在这里,前面我们提到比特币最早期也使用了错误的最长链原则,单纯将最长的链视为主链,而verge在这里竟然犯了同样的错误,这一点确实是有点不可思议了,这就在后面给了攻击者利用自己的长区块的链挤掉诚实矿工的分叉链的机会

简析攻击手法

详细的情况我就不展开讲了,可以参考360的这篇分析,传送门,下面主要是做一个概括的分析

在首轮4月的那场攻击中攻击者主要是利用了五种挖矿算法里的scrypt,我们前面已经提到了五种算法是分开计算的,也就是说我们使用其中一种挖矿算法时只用跟其他同样使用该算法的矿工竞争,想一想我们前面提到的利用时间戳攻击时当掌控50%以上算力时成功率会比较高,那么假设这五种算法的算力是均等的,我们跟某一个算法上的矿工竞争的话就相当于只需要整个系统的10%算力,实际上若只是准备发动攻击需要的更少。

攻击者凑足了这部分算力后便展开针对verge的攻击,他将挖到的区块的时间戳提前,不断发送这些恶意区块,然后就如我们前面所描述的,节点所选择的中位数时间戳被攻击者所占据,同样使用scrypt算法挖矿的诚实矿工的区块被拒绝,攻击者慢慢取得scrypt这个算法上的区块控制权,同时区块难度也在不断下降,攻击者为了不那么引人注目也有意控制了一下下降的趋势,因为攻击者所在的scrypt的挖矿难度下降到非常小,中间一段过程甚至下降到接近0,这使得攻击者甚至做到了一秒一个块,从而使自己挖出的链的长度迅速增长,而其他四个挖矿算法上的矿工相比之下出块速度就非常慢了,这样他们的挖出的块便都成了孤块,因为verge使用了单纯的最长链原则,攻击者在这个过程中便收获了大量的挖矿奖励,在被发现攻击行为时已经是赚的盆满锅满了,之后潇洒走人。

该事件发生后verge赶紧进行了修补,本来他们是直接进行了一刀切,要求后一个区块的时间戳必须大于前一个区块,而且不能超过节点的网络时间20分钟,不过这样会造成大量的误伤,因为前面那种情况是很可能存在的,所以后面又进行了修改,转为阻止单一算法的挖矿,当当前区块的前10个区块有六个及以上由相同算法挖出时便拒绝该区块。

不过显然这样的修改是欠缺考虑的,这样其实只是简单提高了攻击者要完成攻击需要的算力,之前他只需要掌控一条链,但现在他需要掌握两条链,然而攻击手段呢,其实并没有本质上的区别。于是在五月份verge再次遭受了黑客的攻击,这次黑客同时选择了Scrypt和Lyra挖矿算法,他要做的只是在利用时间戳修改逐步掌握两条链的区块权后交替发送区块,先发五个Scrypt的区块,再发五个Lyra的区块,这样刚好可以满足verge的限制条件,最终攻击者成功再次将挖矿难度降低至0,拿走大量的挖矿奖励

经历了这次攻击之后verge再次进行了修复,强制新区块的时间戳不可超过节点网络时间10分钟,教训算是比较惨痛了

如何解决

真要说起来最简单暴力的修复方法就是verge一开始选择的方案,不允许后一个块的时间戳早于前一个块,也就是不允许链上存在时间倒流的情况,这样攻击者提前的时间戳只能不断增加,最终超过节点的时间限制,攻击失败,不过前面也提到这样会造成大量的误伤

一般而言较稳妥的还是收紧节点的验证时间限制,不允许区块的时间戳超过节点网络时间多少分钟,这样攻击者每一轮难度调整可以下降的难度就非常有限,对应的威胁也将没那么大,像verge这样一个块调一次难度还把限制时间设置成两小时这么大的确实是不怕死

写在最后

现在确实越来越感觉区块链世界的庞大以及自己的渺小,黑客的攻击手法越来越巧妙,等着自己去学习的还有很多很多,这篇文章前面扯的闲话可能有点多了,另外水平有限可能理解有不到位的地方还希望师傅们多多指教。

参考链接

Timejacking & Bitcoin

Network Attack on XVG / VERGE

XVG恶意挖矿事件透视——“算力优势+时间劫持”攻击案例分析

The Verge Hack, Explained

Let’s Do the Time Warp Again: The Verge Hack, Part Deux

源链接

Hacking more

...