利用Windows CryptoAPI漏洞:从DSA到ECDSA的参数伪造攻击

本文深入分析了Windows CryptoAPI中椭圆曲线参数验证漏洞的机理,通过DSA类比揭示ECDSA签名伪造原理,并演示如何构造恶意根证书绕过TLS/代码签名验证,强调参数验证在密码学中的关键作用。

利用Windows CryptoAPI漏洞 - The 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

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