链闻 ChainNews:

本文针对 XVG 的数字货币币价翻涨的攻击事件进行分析,攻击者主要利用自己算力的优势伪造时间戳,使得自己挖矿难度不断降低,从而保持自己最长链的优势,让正常的矿池挖到的都是孤块,通过「作弊」挖矿的方式去获利。根据 XVG 黑皮书的描述多种加密算法可以预防 51% 算力攻击,通过这次安全事件我们知道世事无绝对,攻击者根据代码逻辑构造的一系列组合攻击利用还是可以进行成功攻击的,目前而言,51% 攻击对于 pow 机制的加密货币来说还是一个逃不开的话题。

来源 | 巴比特技术指南

作者 | 360 埃癸斯实验室

1. XVG 挖矿异常事件简要回顾

最近几天,一个叫 XVG 的数字货币在普遍都不太好的币市中大出风头,短短几天之中,币价翻了好几倍,但前段时间,一起安全事件让 XVG 的开发团队和持有它的矿工出了一身冷汗。 4 月 4 日,bitcointalk 论坛上 ID 为 ocminer 的用户发帖反馈 XVG 遭到 51% 攻击。据反馈自 XVG 区块高度 2007365 之后,出块时间变得很快,在某些块高度上面,甚至是一秒出一个块,并且这些区块都是利用 scrypt 算法挖到的,如图一所示。


(图一)

在 ocminer 发帖之后的一段时间里,起初 XVG 的开发团队并不认为这是一次攻击事件,并安抚大家不要太焦虑,但是随着事件的发酵,很多矿池已经无法正常工作了,开始不断挖到孤块,正常情况下,矿池一天挖到的孤块数量不会太多,连续挖到孤块的情况更不太可能发生,如下图所示,自从 4 月 4 号 13 点 54 开始,挖到的块都是孤块,因此矿池决定暂停 XVG 的挖矿服务。随后 XVG 开发团队开始响应这次事件,对于代码进行了两次 patch,这样才让这次安全事件渐渐平息下来。


(图二)

虽然这次事件并没有让 XVG 的币价一蹶不振,但是背后还是有些问题值得安全从业者去研究的,其一,这到底是不是一次有预谋的攻击事件?其二,如果是,攻击者到底如何实施攻击的?其三,XVG 的团队为何要进行两次 patch?下文会一一为大家解答这几个问题。

2. XVG 简介和挖矿安全事件分析

2.1 加密货币 XVG 简介


(图三)

XVG 又名 verge,是基于比特币技术的开源加密货币,主打匿名性,与门罗,达世类似。XVG 最初是在 2014 年推出的,由于快速增长的社区,这个数字货币迅速流行,并不断吸引世界各地的用户前来投资。 XVG 的挖矿算法有 Scrypt/X17/lyra2rev2/MYR groestl/blake2 s 这 5 种,并且出块时间仅为 30 秒,大大小于比特币的 10 分钟。因为支持的算法多,可以使更多矿工能够挖到 XVG 币,从而确保每个人都有平等的机会获取 XVG 币。 在生成新块的时间戳校验方面,XVG 是借鉴了旧版的比特币的代码。在 checkblock() 和 acceptblock() 函数当中调用了时间戳校验的算法,与 bitcoin0.8.2 相同,分别是按照如图四中的函数执行流程获得调用。


(图四)

2.2 XVG 难度的调整

XVG 的难度调整算法会向前回溯取相同算法 12 个区块的难度平均值做一个计算,如果实际出块时间间隔小于理想出块时间间隔(12150)的三分之一则调整难度为均值的三倍,如果大于理想出块时间间隔(12150)的三倍则调整难度为均值的三分之一。这样做的目的是通过对比实际出块时间间隔和理想出块时间间隔进行动态难度调整,如果出块慢了,那么就降低难度,如果出块快了,就增加难度,但是调整的时候使用上下限进行限制,这是为了慢慢的调整难度值,防止难度过快的变化,相关代码如图五所示。


(图五)

2.3 下面是我们对攻击流程的分析

首先攻击者拥有很大的算力,攻击者很快使得自己的挖到的区块所在的链成为了主链。因为区块链的一个特点就是最长的链就是主链。同时修改时间戳,让其他矿工所挖的区块通过不了时间戳范围校验,成为孤块。这就造成了只有攻击者可以挖到块,而其他正常矿工不能在攻击者控制的主链上挖到块。

2.3.1 初始阶段
通过观察发现,攻击者最开始修改时间戳的块是 2007355,并不是 ocminer 所说的 2007365,如图六 :


(图六)

从区块 2007355 到 2007363 可以看出,攻击者修改出块时间为每五分钟一个 (上图紫色颜色的时间),当然实际上攻击者的出块时间比显示的时间戳快很多。由于伪造的时间戳间隔远远大于正常出块时间的 30 秒,XVG 会降低难度,通过这 9 个块,XVG 的挖矿难度已经下降了一半 . 正常情况下,9 个块的时间 45 分钟左右相当于攻击者将正常挖矿时间提前了 40 分钟左右。在 Checkblock() 中,因为攻击者伪造了时间戳,使得 pindexPrev->GetMedianTimePast() 的值增大,即前 11 个区块的时间戳中值,使得正常时间戳出的块无法满足大于前 11 个块时间戳中值的条件 , 从而正常时间戳出的块无法添加到攻击者挖掘的这条长链上。图七是 Checkblock() 中校验时间戳的代码。

file

(图七)

下面对代码做简单解释。图七代码的意思为只要新块的时间戳大于之前 11 个 block 的时间戳的中位数,且不超过当前网络时间 2 个小时,则这个块都被认为有效。GetBlockTime() 只是简单返回位于 class CBlockHeader 中的 nTime 成员,也即本块生成的时间戳。GetMedianTimePast() 的实现如图八 :


(图八)

新块的时间戳需要大于前 11 个 block 的时间戳的中位数这个容易理解,但是 nMaxClockDrift 为什么要设置为 2 个小时,将 nMaxClockDrift 宽泛的设置成 2 个小时主要是考虑到 blockheader 上的时间戳并不像 NTP 服务器那样是一个严格准确的时间,新块的开采也需要一定的时间,比方说对于比特币可能是 10 分钟左右才能创建成功一个新块,极端情况下可能会远远超过 10 分钟,一 旦 nMaxClockDrift 时间设置的过小,后续合法产生的新块的时间戳,由于时间戳过大,很可能没办法满足时间戳校验条件,造成区块链没有办法再添加的区块。

2.3.2 控制修改时间戳,使得难度不至于快速下降

经过第一阶段,攻击者已经基本上可以独占挖矿了,难度也已经下降了很多,这一阶段攻击者已经不需要很大的算力了,因为这时候,正常矿工挖出的块,已经不能通过时间校验了,攻击者可以不受干扰的挖矿,自己也完全可以控制挖矿难度。同时为了不至于难度下降过快,从而导致其他矿工发现和其他问题出现,攻击者有意的控制了出块的时间戳。 第一种方式:故意交替的改变时间戳,大约差值为一个半小时 (从 2007365 块高度开始) 当前一个块减去后一个块时间为正的一个半小时,这时候难度是下降的 当前一个块减去后一个块时间戳为负的一个半小时,这时候难度是增长的。 如下表所示

第二种方式 : 每隔 9 个块,使得连续的三个块时间间隔改为 1 秒 (图二红色字体) 很显然根据难度调整的函数,我们知道这里出块的速度非常的快。导致这里难度极速回调。 下表是红色的块的难度情况。

当然总体的趋势是难度不停的下降,如图九所示。


(图九)

综上所述,攻击者就可以独占自己的这条长链进行挖矿,根据最长链原则(把累计了最多难度的区块链作为主链,在一般情况下,也是包含最多区块的那个链),这条链就变成了主链,并使得自己挖矿难度不断降低,从而保持最长链的优势,某些区块的难度被攻击者降为接近于 0。由于 XVG 每 30 秒就出块一次,它的分叉会比特币频繁很多。同样挖掘第 2007433 高度的块,正常矿工和攻击者挖矿难度不同,因此正常矿工所挖的分叉链远远没有办法同攻击者的最长链做竞争,导致最后都成为了孤块。


(图十)


(图十一)

图十是矿池挖到的孤块,图十一是攻击者挖到的区块,两图对比可以看到高度 2007433 上的一个孤块在挖掘的时候同攻击者独占挖掘的最长链不是同一个链。并且难度也不同。

3. XVG 团队的对代码的修改

3.1 first patch

XVG 团队第一次的 patch 可以在这里看到。https://github.com/vergecurrency/VERGE/commit/a3dd53f40aaedd28bd4d0fc720f034492f7ded81
patch 改动如下三图。


(图十二)


(图十三)


(图十四)

Patch 的代码是以区块高度 2040000 为限,小于等于 2040000 高度的区块时间戳检查依然采用之前的逻辑,即新块的时间戳要大于之前 11 个 block 的时间戳的中位数、且不超过当前网络时间 2 个小时。而大于 2040000 高度的区块时间戳检查变更为,新块的时间戳要大于前一个区块的时间戳,并且不可以超过当前网络时间 20 分钟。很明显第一次 patch 的第一个条件 if (GetBlockTime() < pindexPrev->GetBlockTime())return error(“AcceptBlock() : block’s timestamp is too early”); 会大量误伤矿工合法产出区块。因为即使是正常出块,下一个区块的时间戳也不能总是保证比上一个块的时间戳大。由于 XVG 是 30 秒左右出一个块,不同于比特币 10 分钟的出块时间,后一个条件设置将 nMaxClockDrift 由 2 小时缩短到 20 分钟也不会造成 XVG 区块链无法接续的情况。XVG 团队的第一次 patch 主要是想通过缩短 nMaxClockDrift 的值加强时间戳校验,阻止例如 2007385 和 2007386,2007387 和 2007388 这样的间隔超过 20 分钟的区块产生。缩短 nMaxClockDrift 的值以及校验前后区块时间戳顺序从表面上看似可以阻止攻击,但是如果使用大算力的 scrypt 继续挖矿,攻击者依然可以按照相应的时间戳判定逻辑伪造时间戳,并绕过代码检测。

3.2 xvg 团队的第二次 patch

由于 XVG 支持 5 种算法竞争挖矿,正常挖矿的情况下,取 10 个区块如下截图。


(图十五)

由于竞争关系,可以看到 10 个区块不全都是一种算法产生的。而在被恶意挖矿的区块,截图情况如图十六,scrypt 算法在这段时间垄断了出新块的挖矿。

(图十六)

第二次 patch 的在如下链接
https://github.com/vergecurrency/VERGE/commit/80c81aef63272231fc39c2af4b8db9f3f2e9


(图十七)


(图十八)

如图十七和十八,代码主要的改动是将上次改动的判断时间戳校验的代码进行恢复,nMaxClockDrift 由 20 分钟调整回 2 小时。修复了代码 if (GetBlockTime() < pindexPrev->GetBlockTime())return error(“AcceptBlock() : block’s timestamp is too early”);可能误伤正常产出的区块的问题。


(图十九)


(图二十)

为了阻止单一算法独占挖矿,新加入 CheckPreAlgo() 函数,如图十九和图二十所示,从块高度 2042000 开始,在执行 checkblock() 函数的时候开始调用新加入的检查逻辑。自当前区块共向前回溯 10 个区块,检查单一算法挖出的区块数,当每 10 个区块有 6 个或者 6 个以上的区块是由单一算法挖出的时候,新出的这个区块不会被链接受,从而缓解单一算法独占挖矿引发的安全问题。

  1. XVG 「算力优势+时间劫持」攻击事件总结

根据 ocminer 的论坛反馈和我们的区块高度统计,攻击者的两次独占挖矿造成其他矿工的损失统计如图二十一,有 15000 左右个区块被攻击者恶意挖矿。


(图二十一)

两次攻击可疑的收币地址如下图所示,更详尽的信息可见 https://pastebin.com/K7L1G5FE

就这次攻击分析来看,攻击者主要利用自己算力的优势伪造时间戳,使得自己挖矿难度不断降低,从而保持自己最长链的优势,让正常的矿池挖到的都是孤块,通过「作弊」挖矿的方式去获利。根据 XVG 黑皮书的描述多种加密算法可以预防 51% 算力攻击,通过这次安全事件我们知道世事无绝对,攻击者根据代码逻辑构造的一系列组合攻击利用还是可以进行成功攻击的,目前而言,51% 攻击对于 pow 机制的加密货币来说还是一个逃不开的话题。

最后希望我们的声音能让更多人去关注区块链安全!

更多精彩内容,关注链闻 ChainNews 公众号(id:chainnewscom),或者来微博@ 链闻 ChainNews与我们互动!转载请注明版权和原文链接!

来源链接:www.8btc.com