密码学团队硬核解析:Wintermute 遭黑客攻击损失 1.6 亿美金背后密码技术详解

2022-09-24

By Max

从最近的黑客攻击说起

9 月 20 日,数字资产做市商 Wintermute 首席执行官 Evgeny Gaevoy 宣布其 DeFi 相关业务遭到黑客攻击,损失约 1.622 亿美元,疑因其合约地址「私钥」遭到暴力破解,引发强烈的市场反响。无独有偶,9 月 19 日,据 The Block 报道,一名黑客利用 Profanity 漏洞从多个通过 Profanity 建立的以太坊地址中盗取 330 万美元资产。

Safeheron 密码学安全团队跟进分析发现,Wintermute 合约地址采用 Profanity 开源库生成,而该类 0x000000 开头的靓号地址,存在被暴力破解的可能性。此前 1inch 曾发文披露该漏洞潜在的安全威胁, 那么,这一系列攻击究竟是怎么发生的呢?靓号地址生成开源库 Profanity 在其中又起到什么作用呢?接下来,Safeheron 团队将从以下几个方面,硬核还原靓号地址被攻击的全技术过程。

  • 什么是靓号地址?

  • Profanity 的靓号地址生成原理

  • Profanity 的理想安全性

  • 直觉上 Profanity 存在的问题

  • Profanity 算法的形式化描述

  • 靓号地址的私钥攻击原理

  • 靓号地址的私钥攻击示例

  • 攻击效率分析

  • 安全改进建议

  • 进一步思考

什么是靓号地址?

所谓靓号地址,是加密货币生态中的很多参与者用以凸显个性的一种方式。具体来说,它是指这样的一些地址:

  • 开头 8 个 8:

0x88888888bc27358faea388cdf91fa9b676068207
  • 开头 7 个 0:

0x0000000925e311792debae85befaa946200ffc67
  • 开头指定单词:

0xdead9b096ec34c35e45b5a2aab5337805916ac1e
  • 镜像地址:

0x5b4d6554bd4d 89dfcd0bb0dcfd98 c3e73ab05f69

这些靓号地址不仅彰显个性,也带来一些其它的好处,比如方便记忆,让人印象深刻,不容易错判等等。

为生成这些靓号地址,开发者们提供各式各样的开源生成工具,而 Profanity 就是以太坊生态中最著名的开源生成工具之一。下面我们将介绍 Profanity 的具体原理。

Profanity 靓号地址生成原理

Profanity 到底是如何生成靓号地址的呢?通过分析 Profanity 开源库,可以整理出原理图:

生成步骤如下:

  1. 获取安全随机数(从硬件或者熵池),作为伪随机数算法 mt19937 的种子。

  2. 利用伪随机数算法 mt19937 生成种子私钥(Seed Private Key),然后计算出种子公钥(Seed Public Key)。

  3. 调用 OpenCL 并行计算平台,调用基于种子公钥(Seed Public Key) 的多轮迭代算法,计算大量的候选公钥(Candidate Public Key) 和 eth 地址(Address)。

  4. 调用指定靓号筛选器,筛出靓号,并输出靓号私钥(Vanity Private Key) 和靓号地址(Vanity Address)。

Profanity 库支持并行计算架构,不仅靓号模式选择多样,而且生成效率极高,受到社区广泛欢迎。然而这也为后面的攻击埋下伏笔。

Profanity 的理想安全性

理想中的 Profanity 安全性有多高呢?我们一起来看看,如果需要暴力破解这样一个靓号地址的私钥,到底需要多长时间。

0x88888888bc27358faea388cdf91fa9b676068207

如果使用并发加速,同时使用 1000 台跟我个人 PC 同配置的机器,则需要日夜不断的跑 16 个月。

看起来,要暴力破解似乎也是非常困难的事情。那么,是否真的安全么?

直觉上 Profanity 所存在的安全风险点

乍一看来,调用安全随机数,也进行大量计算,似乎没有明显的问题。可是黑客毕竟成功开展攻击,那么,问题究竟在哪里呢?

直觉上看,Profanity 至少有两大风险点:

  • 从硬件或者熵池获取随机数时,仅仅获取 32 bit,这是非常短的。要知道,常见私钥的长度是 256 bit,远远超出 32 bit。

  • 在后续的并行多轮计算时,其迭代算法并非使用单向函数(比如 hash 函数)。单向函数会带来更高的安全性,而不使用单向函数则意味着逆向追溯的可能。

单独一个风险点未必会立刻致命,但是当两个一起出现,则可能地覆天翻。

Profanity 算法的形式化描述

在描述攻击原理之前,我们首先将整个 Profanity 原理抽象一下。

Step 1: 计算种子公钥(Seed Public Key)

从随机设备(Random Device)到种子公钥,中间有两步:

  • 获取 32 bit 安全随机数(从硬件或者熵池),作为伪随机数算法 mt19937 的种子。

  • 利用伪随机数算法 mt19937 生成种子私钥(Seed Private Key),然后计算出种子公钥。

如下所示:

Step 2: 计算候选公钥(Candidate Public Key)

迭代算法如下:

其中迭代偏移函数定义为:

迭代计算的结果,备选公钥集合可以定义为:

注意: 根据迭代偏移函数定义,以下等式成立:

Step 3: 过滤靓号

靓号地址的私钥攻击原理

现在我们考虑如何对上图中的私钥机制进行攻击。

Step 1: 穷举种子公钥集合

Step 2: 计算真实靓号公钥

Step 3: 计算候选公钥集合的一个平移集(Shift Set)

如图所示,我们求出平移集合:

Tips:

  • 为什么命名为「平移集合」?这是因为平移集合和候选公钥集合在二维空间中就是两个矩阵,这两个矩阵大小相等,可能相邻,也可能有一部分元素重合。看起来,基于靓号公钥,对候选公钥集合做平移,即可得到平移集合。如下图所示。

Step 4: 集合求交

该命题保证一定能够找到与靓号公钥关联的种子公钥。

注意:集合求交很多不同的实现方法,比如数据量不大是可以直接通过查数据库,如果数据量太大,可以结合一些成熟算法解决。

Step 5: 计算靓号私钥

Step 3Step 4 中的示例,可直接计算靓号私钥:

靓号地址攻击示例

现在我们通过一个具体的例子,来展示如何获取靓号地址的私钥。待破解的靓号地址如下,这是一个以「dead」单词开头的地址:

0xdead9b096ec34c35e45b5a2aab5337805916ac1e

Step 1: 穷举种子公钥集合

Step 2: 计算真实靓号公钥

找到该地址的链上交易,从签名中恢复公钥,如下所示:

0x0488dff9528cc2fc582e11688abce90cd84d8c805424fa3c761f50ad96b877a8cf3c3b0796ec099a05403562a4e0f8ecec9c511265e12ae45793bad11111e11e4d

Step 3: 计算候选公钥集合的一个平移集(Shift Set)

计算平移集合:

Tips:

  • 计算平移集合的过程中,平移集合中所有元素(即公钥点)相对于靓号公钥的偏移也被记录下来。

Step 4: 集合求交

0x04f316acd6890440bb7ed841e9c9d0a69dbd3545ef390947ad55248cd90719ff84e897f3359caf934fc2d6835f02038a00305aa2f823376e963afe42cfa159384c
0x045c2f14d04b94b99f64c1c36e984311d2fdb49c9b39f8aa272b92da72e323e0

Step 5: Calculate the vanity private key

从靓号私钥计算靓号公钥,用于复验,复验正确,至此攻击成功结束。

攻击效率分析

详细分析

攻击步骤一共 5 步,Step 2 Step 5 基本不耗时,Step 1 可以预先计算,所以也可以省去 Step 1 的耗时。最终,主要耗时都在 Step 3Step 4

Tips:

  • 平移集合的设计和计算方式直接影响到攻击的效率。

  • 在实际攻击操作时,由于候选公钥集合是未知,我们可以根据靓号难度来估算候选公钥集合的大小,从而设计平移集合。

  • 平移集合基数可能会略大于候选公钥集合。

实例分析

我们给一些具体的例子,让大家有个直观的印象:

还有不少的情况,平移集甚至只有百万量级大小,更是反手可破。

效率总评

基本结论是攻击效率极高,不用付出太高的代价,就能破解几乎所有的 Profanity 靓号地址私钥。

  • 如果不考虑集合求交的耗时,私钥的破解速度可以逼近靓号地址的生成速度,这是非常可怕的,完全摧毁 Profanity。

  • 用户的不好的使用习惯会大大提升私钥破解的风险,一般来说,Profanity 越早生成出来的地址,搜索空间越小,风险越大,越容易破解。由于大部分用户都喜欢用最早生成出来的几个靓号地址,导致大部分 Profanity 用户的靓号地址私钥随时可能被破解。

Profanity的安全改进建议

主要是两点建议:

  • 从硬件或者熵池获取随机数作为种子时,直接取出 256 bit。

  • 并行多轮迭代计算时,使用单向函数(比如 hash 函数)。

进一步思考

如果 Profanity 算法中生成候选公钥时使用单向函数,算法是否安全?

如果随机种子的长度提高到 64 bit,我们是不是就能用呢?

答案是否定的。64 bit 并不是足够长的随机数,同样存在生日攻击的问题,只不过 50% 碰撞概率的人数提高到大约 50 亿而已,这依然不够安全。

我们还能使用靓号么?

首先,Profanity 生成的靓号地址都存在致命的风险,因此我们强烈建议,所有的 Profanity 用户都应该将资产转移到其它安全的地址上。其次,我们还是可以使用靓号地址的。并不是所有的靓号地址都有问题,如果不一味强调效率,选择用更安全的算法来计算,那么就能生成安全的靓号地址。

关于私钥的安全生成有什么更好的建议?

私钥就是随机数,要想让私钥足够安全,就要足够随机。获取安全随机私钥的方法多种多样。

  • 如果有经过认证的安全硬件,可以使用安全硬件提供的安全随机数。

  • 对于足够复杂的运算平台,包括常见的各类 PC 和移动端,环境噪声足够复杂,使用平台自带的熵池和伪随机数发生器便已足够随机。

  • 如果是简单平台,比某些硬件钱包,则需要从外部导入随机种子。方式包括不限于导入随机文件、环境照片、语音、视频等等。

  • 还有一种更安全的方式,通过 MPC 聚合多平台的随机性。基于 MPC 从多平台生成私钥分片,即便单平台私钥不够随机或者遭遇黑客攻击,整个 MPC 钱包私钥依然是安全的。

最后更新于