主页 > imtoken新版下载 > 揭秘 Ethereum Vanity Generator Profanity 私钥破解漏洞

揭秘 Ethereum Vanity Generator Profanity 私钥破解漏洞

imtoken新版下载 2023-10-19 05:10:34

本文总结了Profanity漏洞产生的原因,同时也分析了破解此类随机问题的可能性。

通过:约翰

近日,Wintermute钱包遭到攻击,损失约1.6亿美元。 盗窃的原因是 Wintermute 使用 Profanity 创建了一个 Vanity 钱包(从 0x0000000 开始)以节省 Gas 费用。 此前,去中心化交易所聚合商 1inch 发布了一份安全披露报告,部分声称通过名为 Profanity 的工具创建的以太坊地址存在严重漏洞。 慢雾安全团队对此次事件进行了深度分析并分享给大家。

以太坊私钥生成过程_以太坊区块生成时间_以太坊私钥生成过程

椭圆曲线密码学(ECC)是区块链领域最常用的加密算法。 ECC是加密算法的一大类,包括多种不同的曲线和加密算法,如secp256k1/secp256r1/ed25519/schnorr等,比特币和以太坊是用secp256k1加密的。

以太坊私钥生成过程_以太坊区块生成时间_以太坊私钥生成过程

在使用以太坊时,我们会先生成一个私人账户(以0x+40个字母开头),具体过程如下:

(1) 生成一个不可预测的随机数种子,通常基于系统的/dev/urandom random generator;

(2)使用随机种子生成私钥(256位,32字节)

(3) 由私钥生成公钥(64字节)

(4) 公钥使用keccak-256哈希算法,取哈希值十六进制字符串的最后40个字母,在开头加上0x,生成最终的以太坊地址。

特别注意这些公钥和私钥的大小,因为这关系到我们后面的计算量。

这里需要提到的一个关键公式:

Q =千克

这是从私钥推导出公钥的核心算法。 从私钥推导出公钥非常简单,但要逆向推导出来几乎是不可能的。

Q是公钥,k是私钥,k由有限域中的大整数组成,大到几乎不可能猜到,G是椭圆曲线上的一个点,默认是固定值, kG是k个G点的相加(椭圆曲线上的点相加不是简单的实数相加,这里不讨论计算方法)。

如果我们要穷举以太坊账户,找到Vitalik的私钥,那么在知道他的公钥后,最多需要进行2^256次(2的256次方)次Q=kG的计算。 对于大数,我们自然是不敏感的,所以我把它换算成工作量,也就是目前一台Apple M1电脑的速率大概是40M/s,所以大概需要的年份是8后面跟62个零。 一万年太久,先战吧。

有一些错误的私钥生成方式,会导致私钥取值范围变成一个较小范围内的值,变得可猜。 常见原因有:

(1) 随机数种子不够随机。 例如,如果使用时间戳作为随机数种子,我们只要穷尽过去一段时间内的所有时间戳以太坊私钥生成过程,就可以找到用于生成公钥的种子和私钥;

(2)软件算法存在缺陷,导致随机强度不够(Profanity存在此类缺陷);

Profanity 的设计目的是帮助人们生成具有特殊视觉效果的帐户,例如以特殊字符开头或结尾的帐户。 可以达到调用智能合约时节省Gas的效果。

Profanity 为了更快的爆破出 Vanity Address,程序一开始只获取了一个随机数,后续的所有私钥都是基于这个随机数迭代扩展的。 我们来看看它的随机数生成算法:

Dispatcher.cppcl_ulong4 Dispatcher::Device::createSeed() {#ifdef PROFANITY_DEBUG cl_ulong4 r; rs[0] = 1; rs[1] = 1; rs[2] = 1; rs[3] = 1; 返回 r; #else // 随机化私钥 std::random_device rd; std::mt19937_64 eng(rd()); std::uniform_int_distribution 分布; cl_ulong4 r; rs[0] = 分配(工程); rs[1] = 分布(英语); rs[2] = 分配(工程); rs[3] = 分配(工程); 返回 r; #万一}

这里random_device用于获取系统提供的随机数。 此随机数来源满足加密要求的强度。 但是当我们注意到变量类型时,我们发现 rd() 返回的是一个 32 位的随机值。 上面我们提到私钥是256位长,所以获取随机数的过程不能填满整个私钥。 密钥,因此 Profanity 使用 mt19937_64 生成随机数来填充整个私钥。 mt19937_64和random_device的随机算法有一个本质区别,mt19937_64是确定性的,它的随机性取决于输入的随机数,不会产生新的随机性。

也就是说,如果rd()传给mt19937_64的值在一定范围内,那么distr(eng)的值也在一定范围内,createSeed函数返回的r值自然也在一定范围内范围。

关键点来了:rd()的所有可能性都是2^32,与私钥的安全性(2^256)相差224个数量级,但是2^32的数量级也相当大,那么需要多少计算才能破解私钥呢?

Profanity在获得第一个私钥SeedPrivateKey后,为了与需要的账户地址进行碰撞,会通过固定算法不断更新私钥,最多200万次(数值来源于1inch披露的文章),本次公开key 的计算方法可以表示为:

PublicKey = kG = (SeedPrivateKey + Iterator)*G

迭代器是一个递增的数字。 当PublicKey已知时,我们可以通过枚举SeedPrivateKey和Iterator得到SeedPrivateKey。 计算量约为2^32乘以200万次。 一台 M1 计算机需要 60 多年。 看到这辈子有希望了:D。 如果我用大量计算能力更大的显卡进行并行计算,几天甚至几个小时能撞出想要的结果就完全没问题了。

恰好以太坊转 PoS 共识最近有大量闲置的显卡算力。 如果矿工用显卡破解这个私钥,不是分分钟就能成功吗? 当然这种阴谋论是没有意义的,我们只是想研究一下破解的可能性。 我们更希望能够用一种不太暴力的方法来解锁私钥。

我们将等式两边稍微移动一下,对上式进行变换得到:

SeedPrivateKey*G = PublicKey - Iterator*G

我们可以考虑另一种攻击方式。 如果先对SeedPrivateKey*G进行预计算,最多需要256G的内存空间来存储计算结果。 在普通服务器上就可以完成,需要计算2^32次,大概几十分钟就可以完成。 然后我们把需要破解的PublicKey代入等式右边以太坊私钥生成过程,然后和Iterator碰撞。 需要的计算量大概是200万次,查表次数有200万次。 所需时间以秒为单位。 这是有趣的。 原来这个32位的随机数是如此的微不足道,任何人都可以在几十分钟内还原出私钥。

至此,我们得出结论,Profanity漏洞的产生原因是256位的私钥未充分随机化,导致私钥的取值范围严重缩减。 同时也分析了破解这类随机性问题的可能性,希望能给大家一些启发。