一个概念验证 (PoC) 代码演示了 Java 中新披露的数字签名绕过漏洞,已在线共享。
有问题的高严重性缺陷CVE-2022-21449(CVSS 分数:7.5)影响 Java SE 和 Oracle GraalVM 企业版的以下版本 -
问题在于 Java 对椭圆曲线数字签名算法 ( ECDSA ) 的实现,用于对消息和数据进行数字签名,以验证内容的真实性和完整性。
背景:ECDSA 签名
ECDSA 代表Elliptic Curve Digital Signature Algorithm,它是一种广泛使用的标准,用于签署各种数字文档。与旧的 RSA 标准相比,椭圆曲线密钥和签名往往要小得多,以实现同等安全性,因此它们被广泛用于大小非常宝贵的情况。例如,用于两因素身份验证的 WebAuthn 标准允许设备制造商从广泛的签名算法中进行选择,但实际上,迄今为止制造的几乎所有设备都仅支持 ECDSA 签名(一个值得注意的例外是使用 RSA 的 Windows Hello签名;大概是为了与旧的TPM硬件兼容)。
无需过多了解技术细节,ECDSA 签名由两个值组成,称为r和s。为了验证 ECDSA 签名,验证者检查包含r、s、签名者的公钥和消息哈希的等式。如果等式两边相等,则签名有效,否则拒绝。
等式的一侧是r,另一侧乘以r和从s导出的值。因此,如果r和s都为 0,这显然是一件非常糟糕的事情,因为那时您将检查 0 = 0 ⨉ [a一堆东西] ,无论 [a一堆东西]的值如何,这都是正确的]!那一堆东西就是消息和公钥之类的重要部分。这就是为什么 ECDSA 验证算法的第一个检查是确保r和s都 >= 1。
一个交互式 jshell 会话,显示易受攻击的实现接受一个完全空白的签名作为对任意消息和公钥有效:
| Welcome to JShell -- Version 17.0.1
| For an introduction type: /help intro
jshell> import java.security.*
jshell> var keys = KeyPairGenerator.getInstance("EC").generateKeyPair()
keys ==> java.security.KeyPair@626b2d4a
jshell> var blankSignature = new byte[64]
blankSignature ==> byte[64] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... , 0, 0, 0, 0, 0, 0, 0, 0 }
jshell> var sig = Signature.getInstance("SHA256WithECDSAInP1363Format")
sig ==> Signature object: SHA256WithECDSAInP1363Format<not initialized>
jshell> sig.initVerify(keys.getPublic())
jshell> sig.update("Hello, World".getBytes())
jshell> sig.verify(blankSignature)
$8 ==> true
// Oops, that shouldn't have verified...
请注意,“InP1363Format”限定符只是让演示错误变得更容易。ASN.1 DER 格式的签名可以以相同的方式被利用,您只需要先对编码做更多的调整,但请注意 JWT 和其他格式确实使用原始 IEEE P1363 格式。
缓解措施
首先,如果使用的是 Java 15 或更高版本,请更新到最新版本以修复此问题。
一般来说,正确实现密码代码非常棘手,而公钥签名算法是最棘手的。ECDSA 本身就是最脆弱的算法之一,即使是一个随机值中的微小偏差也可以完全恢复私钥。
如果正在设计认为需要使用数字签名的协议或应用程序,请考虑是否真的这样做了——是否可以使用更简单的机制来代替?与签名方案相比,像HMAC这样的简单 MAC 算法很难搞砸。如果真的需要签名,请考虑使用像EdDSA这样的现代算法来避免 ECDSA 的一些陷阱。
更多详情请访问Oracle官网4月份补丁更新:https://www.oracle.com/security-alerts/cpuapr2022.html
原文始发于微信公众号(祺印说信安):研究人员针对 Java 加密漏洞发布 PoC
评论