我们使用Wycheproof在elliptic库中发现了密码学漏洞
Trail of Bits公开披露了elliptic(一个广泛使用的JavaScript椭圆曲线密码库)中的两个漏洞。该库每周下载量超过1000万次,被近3000个项目使用。这些漏洞源于缺失的模约减和长度检查,分别可能允许攻击者伪造签名或阻止有效签名被验证。
其中一个漏洞在2024年10月结束的90天披露窗口后仍未修复,截至本文发布时仍未解决。
我使用Wycheproof(一个设计用于针对已知漏洞测试各种加密算法的测试向量集合)发现了这些漏洞。如果您想了解更多关于如何使用Wycheproof的信息,请查看我发布的这篇指南。
在这篇博客文章中,我将描述我如何使用Wycheproof测试elliptic库、我发现的漏洞如何工作,以及它们如何能够实现签名伪造或阻止签名验证。
方法论
我在Trail of Bits实习期间,为《测试手册》的新密码学测试章节撰写了一份关于使用Wycheproof的详细指南。我决定将elliptic库作为该指南的一个真实案例研究,这使我得以发现相关漏洞。
我按照指南所述,为elliptic包编写了一个Wycheproof测试工具。然后,我分析了Wycheproof提供的各种失败测试用例所覆盖的源代码,将其分类为误报或真实发现。在理解了这些测试用例失败的原因后,我为每个漏洞编写了概念验证代码。确认它们是真实发现后,我开始了协调披露过程。
发现
我总共发现了五个漏洞,对应五个CVE。其中三个漏洞是次要的解析问题。我通过向代码仓库提交公开拉取请求披露了这些问题,并随后请求了CVE ID以进行跟踪。
其中两个问题更为严重。我使用GitHub的安全通告功能私下披露了它们。以下是这些漏洞的一些细节。
CVE-2024-48949: EdDSA签名可塑性
该问题源于一个缺失的边界检查,这在NIST FIPS 186-5的第7.8.2节“HashEdDSA签名验证”中有所规定:
将签名的前半部分解码为点R,将签名的后半部分解码为整数s。验证整数s在0 ≤ s < n的范围内。
在elliptic库中,从未执行检查s是否在0 ≤ s < n的范围内,以验证其未超出生成点G的阶n。此漏洞允许攻击者伪造新的有效签名sig’,但仅限于已知的签名和消息对(msg, sig)。
签名 = (msg, sig) sig = (R || s) s′ mod n == s
需要实现以下检查以防止此伪造攻击:
|
|
伪造的签名可能会破坏协议的共识。一些协议会正确地将伪造的签名消息对拒绝为无效,而elliptic库的用户则会接受它们。
CVE-2024-48948: 针对含前导零哈希值的ECDSA签名验证错误
第二个问题涉及ECDSA实现:有效签名可能无法通过验证检查。
以下是失败的Wycheproof测试用例:
[testvectors_v1/ecdsa_secp192r1_sha256_test.json][tc296] special case hash
[testvectors_v1/ecdsa_secp224r1_sha256_test.json][tc296] special case hash
这两个测试用例都因为一个特制的包含四个前导零字节的哈希值而失败,该哈希值来自对十六进制字符串"343236343739373234"进行SHA-256哈希的结果:
00000000690ed426ccf17803ebe2bd0884bcd58a1bb5e7477ead3645f356e7a9
我们将使用secp192r1曲线测试用例来说明签名验证失败的原因。
负责验证椭圆曲线签名的函数位于lib/elliptic/ec/index.js:
|
|
在消息传递给verify函数调用之前必须进行哈希,这发生在elliptic库外部。根据FIPS 186-5第6.4.2节“ECDSA签名验证算法”,消息的哈希值必须根据椭圆曲线基点的阶n进行调整:
如果log2(n) ≥ hashlen,则设E = H。否则,设E等于H的最左log2(n)位。
为实现这一点,调用了_truncateToN函数,它执行必要的调整。在此函数调用之前,哈希后的消息msg使用new BN(msg, 16)从十六进制字符串或数组转换为数字对象。
|
|
变量delta计算哈希值大小与曲线当前生成元G的阶n之间的差值。如果msg占用的位数多于n,则将其按差值移位。对于这个特定的测试用例,我们使用secp192r1(使用192位)和SHA-256(使用256位)。哈希值应向右移位64位以保留最左边的192位。
elliptic库中的问题在于,new BN(msg, 16)转换会移除前导零,导致哈希值更小,占用的字节更少:
690ed426ccf17803ebe2bd0884bcd58a1bb5e7477ead3645f356e7a9
在计算delta时,msg.byteLength()随后返回28字节而不是32字节。
|
|
这种错误计算导致delta值为32 = (288 - 192) 而不是 64 = (328 - 192)。因此,哈希后的消息没有正确移位,导致验证失败。如果消息哈希包含足够的前导零(概率为2^-32),此问题会导致有效签名被拒绝。
要解决此问题,应在验证函数中添加一个额外参数以允许解析哈希大小:
|
|
持续测试的重要性
这些漏洞作为一个示例,说明了为什么持续测试对于确保广泛使用的密码学工具的安全性和正确性至关重要。特别是,Wycheproof和其他积极维护的密码学测试向量集是确保高质量密码学库的优秀工具。我们建议在您的CI/CD流水线中包含这些测试向量(以及任何其他相关的测试向量),以便在每次代码更改时重新运行它们。这将确保您的库现在和将来都能抵御这些特定的密码学问题。
协调披露时间线
对于披露过程,我们使用了GitHub集成的安全通告功能私下披露漏洞,并使用报告模板作为报告结构的模板。
2024年7月9日:在对elliptic库运行Wycheproof期间,我们发现了失败的测试向量。 2024年7月10日:我们确认ECDSA和EdDSA模块都有问题,并编写了概念验证脚本和修复方案。
关于 CVE-2024-48949
2024年7月16日:我们使用GitHub安全通告功能向elliptic库维护者披露了EdDSA签名可塑性问题,并创建了一个包含我们建议修复方案的私有拉取请求。 2024年7月16日:elliptic库维护者确认了EdDSA问题的存在,合并了我们建议的修复方案,并创建了一个新版本,但未公开披露该问题。 2024年10月10日:我们向MITRE请求了一个CVE ID。 2024年10月15日:自我们私下披露以来已过去90天,此漏洞变为公开。
关于 CVE-2024-48948
2024年7月17日:我们使用GitHub安全通告功能向elliptic库维护者披露了ECDSA签名验证问题,并创建了一个包含我们建议修复方案的私有拉取请求。 2024年7月23日:我们尝试联系,请求将一名额外的协作者添加到ECDSA GitHub通告中,但未收到回复。 2024年8月5日:我们联系请求确认ECDSA问题,并再次请求将一名额外的协作者添加到GitHub通告中。我们未收到回复。 2024年8月14日:我们再次联系请求确认ECDSA问题,并再次请求将一名额外的协作者添加到GitHub通告中。我们未收到回复。 2024年10月10日:我们向MITRE请求了一个CVE ID。 2024年10月13日:Wycheproof测试开发者Daniel Bleichenbacher独立发现并披露了问题 #321,该问题与此发现相关。 2024年10月15日:自我们私下披露以来已过去90天,此漏洞变为公开。