深入解析Windows CryptoAPI漏洞:参数验证绕过与ECDSA攻击

本文详细分析了Windows CryptoAPI中椭圆曲线参数验证漏洞的机制,展示了攻击者如何通过操纵ECDSA参数伪造证书,突破TLS与代码签名信任链,并提供了修复建议与安全启示。

利用Windows CryptoAPI漏洞 - Trail of Bits博客

Ben Perez
2020年1月16日
密码学, 漏洞利用

周二,NSA宣布在Windows 10和Windows Server 2016/2019的证书验证功能中发现了一个严重漏洞。此漏洞允许攻击者在多种场景(如HTTPS和代码签名)中破坏信任验证。担心吗?请访问 https://whosecurve.com/ 获取重要详情并检查您的系统是否受影响。然后返回本文继续阅读,了解该漏洞的具体原理和工作方式。

如果没有logo,为什么要打补丁? https://whosecurve.com

从高层次看,该漏洞利用了Crypt32.dll未能正确检查提供的根证书中椭圆曲线参数是否与Microsoft已知参数匹配的事实。有趣的是,该漏洞并未利用椭圆曲线特有的数学属性——同样的漏洞本可能出现在普通的DSA签名验证库中。因此,我们首先回顾如果Crypto32.dll使用普通DSA,此漏洞会如何发挥作用。

攻击的简化版本

DSA的安全性依赖于离散对数问题在素数模整数群中的难解性。考虑以下方程:

1
b = g^x mod p

如果只知道p、g和b,则很难找到x。为了设置DSA,用户需要指定一个素数p和一个生成元g。使用这两个参数,他们可以创建私钥x和公钥pk = g^x mod p。这些密钥允许生成只能由私钥创建但可用公钥验证的签名。签名伪造的难度与离散对数问题相当(非常困难)。

然而,诸如DSA之类的数字签名算法本身并不十分有用,因为它们没有提供用户信任与特定实体关联的公钥的机制。这就是X.509证书的用武之地。X.509证书是一个明确声明“此公钥属于此人”的文件,由其他人(可能是该公钥的所有者)签名。这些证书可以链式连接,从“根”证书开始,证明根证书颁发机构(CA)的身份。根CA签署中间证书以证明中间CA的身份,中间CA签署其他中间证书,依此类推,直到最后的“叶子”证书。

每个证书包含有关签名算法和所用参数的信息。Microsoft的证书可能如下所示(高度简化):

1
2
3
4
5
6
7
证书颁发机构:Microsoft  
名称:Trail of Bits  
公钥信息  
算法:DSA  
生成元:g  
素数:p  
公钥:pk

当Windows用户收到X.509证书链时,他们会检查其根部的CA是否是Microsoft信任的。但是,如果Windows仅检查相关证书的公钥是否与受信任实体匹配,而不检查相关的系统参数,会发生什么?换句话说,当攻击者可以更改与给定公钥pk关联的p或g值而Windows未察觉时,会发生什么?实际上,省略此检查会完全破坏DSA的安全性。

利用此漏洞的一种潜在方法是简单地将g = p设置并使私钥x = 1。这允许攻击者签署任何消息,就像他们是pk的合法所有者一样,因为他们现在知道私钥(它是1)。然而,事情可能变得更有趣:我们可以选择一个新的私钥y,并将恶意生成元设置为g’ = y^–1 * pk,而不是简单地将新生成元设置为目标的公钥。这意味着证书仍然有效地拥有一个密钥,但只有攻击者知道,而不是原始颁发者。

重要的是,此攻击无需任何人解决离散对数问题即可工作。本质上,如果未建立与给定公钥关联的参数的真实性,攻击者可以选择任何他们想要的私钥。此利用场景最初由Vaudenay在2004年概述,并被称为域参数移位攻击,但直到现在才在野外看到。

实际漏洞

利用Crypt32.dll中的漏洞需要调整先前的攻击,使签名者使用椭圆曲线变体ECDSA而不是DSA。实际上,您不需要对椭圆曲线了解太多即可理解其工作原理。您只需要知道椭圆曲线在数学上大致等同于整数模p,除了不是乘以数字,而是在几何上操纵曲线上的点。在本文中,曲线点用粗体大写字母表示,将点P自身加n次写作n * P。

图1. 您见过无数次的展示椭圆曲线加法的图表

椭圆曲线与点加法创建了另一个离散对数问题难解的结构。此外,与普通DSA情况一样,ECDSA需要在生成私钥/公钥对之前选择一组公共参数,包括生成元。通常这些参数通过命名曲线来指定,例如椭圆曲线secp256r1 (1.2.840.10045.3.1.7),但用户可以手动指定它们。在这种情况下,用户必须提供定义椭圆曲线(A,B)的常数、进行算术运算的素数p、群的生成元G以及有关该群大小的信息(阶、辅因子)。对于此攻击,我们只关心G。

既然我们对椭圆曲线有了一些背景知识,不难看出攻击的工作原理与DSA基本相同:更改指定ECDSA的参数,使其具有您知道的私钥对应的生成元,但具有与您试图欺骗的证书颁发机构相同的公钥。通过编辑参数,我们可以控制证书的有效密钥,并使用它来证明我们想要的任何身份。

在现实生活中,参数验证绕过也稍微复杂一些。Microsoft确实检查大多数证书中使用的参数是否有效,但当呈现其已缓存的根证书时,如果证书使用椭圆曲线密码学且公钥与缓存匹配,它将跳过参数验证。这意味着对于大多数用户可能见过的常见根CA,我们的攻击是可行的。实际上,这意味着我们可以为几乎任何网站生成有效的TLS证书,绕过代码签名限制,或伪造文件和电子邮件的签名。出于解释目的,让我们看看如何中间人攻击到某个网站的https流量。

构建伪造证书

首先我们需要选择一个受信任的根证书。Microsoft在此处维护列表。出于我们的目的,我们选择了Microsoft EV ECC Root Certificate Authority 2017。这是一个secp384r1证书,因此公钥是由secp384r1曲线给出的参数定义的曲线上的一个点。

图2. 带有公钥的受信任证书

接下来,我们需要为我们的恶意证书生成一个新的私钥,该证书定义在不同的曲线上,使用显式参数。此对象具有特定的ASN.1密钥编码,我们使用OpenSSL生成。请记住从前一节中,我们希望保持公钥与私钥相同以绕过验证。一旦我们有了新证书的公钥和私钥,我们就可以使用它们来计算一个生成元,使它们对应。更准确地说,我们需要计算G’ = x^{-1} * P,其中x是我们的私钥标量,P是来自MS证书的公钥点(这对应于前一节中的第二个攻击场景)。

现在我们有了一个新的变异密钥,我们可以用它来生成CA证书:

图3:我们坏根的解析视图

生成该证书后,我们可以使用它为任何我们想要的东西签署叶子证书。此密钥/证书仅由“坏”根签名——不需要自定义参数或任何魔法。

图4:whosecurve.com的证书

最后,我们确保在TLS连接中发送“完整链”。在正常TLS中,您发送叶子证书以及任何中间证书,但不发送根本身。在这种情况下,我们需要发送该根以触发缓存错误。瞧!

图5. 非真实TLS证书

修复漏洞和经验教训

对Microsoft来说幸运的是,修复此错误只需要在签名验证期间添加一些检查以确保ECDSA参数是真实的。对其他人来说不幸的是,此漏洞绝对是毁灭性的,要求所有运行Windows 10的系统立即打补丁。在此之前,攻击者可以伪造TLS、代码、文件或电子邮件的签名。

在密码学上,此错误是一个很好的例子,说明增加参数选择的数量如何增加系统脆弱性。多年来我们知道显式指定曲线(与命名曲线相对)是一个坏主意,这是又一证据。它也是一个很好的提醒,处理数字签名时需要验证所有密码学信息。

虽然我们未提供此漏洞的PoC,但我们强烈建议人们打补丁,因为公共利用代码已于今天可用!我们还为任何有兴趣检查其系统是否未打补丁的人建立了一个网站:去找出 Whose Curve Is It Anyway?

参考文献

  • HN推测
  • Scott的CT绕过建议
  • Saleem Rashid的PoC

如果您喜欢这篇文章,请分享: Twitter LinkedIn GitHub Mastodon Hacker News

页面内容 近期文章 使用Deptective调查您的依赖项 系好安全带,Buttercup,AIxCC的评分回合正在进行中! 使您的智能合约超越私钥风险 Go解析器中意外的安全陷阱 审查首批DKLs的23个库来自Silence Laboratories的经验教训 © 2025 Trail of Bits. 使用Hugo和Mainroad主题生成。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计