Awesome-POC/os/Windows CryptoAPI欺骗漏洞 CVE-2020-0601.md
2022-02-20 16:14:31 +08:00

8.2 KiB
Raw Blame History

Windows CryptoAPI欺骗漏洞 CVE-2020-0601

漏洞描述

1月15日微软发布了针对CVE-2020-0601的安全补丁该漏洞是微软在实现椭圆曲线加密算法数字证书验证时产生可被利用于伪造来自可信任来源的签名或证书并且因其业务特性会衍生出多种攻击向量具有极高的可利用价值和极大的潜在破坏力Win 10和windows server 2016 & 2019也都在其影响范围内。该漏洞由美国NSA国家安全局发现后汇报给微软公司也被认为是第一个NSA公开披露的软件系统漏洞当然也有可能存在其特殊的战术目的。绿盟科技M01N攻击安全研究团队对此漏洞原理进行了详细分析并复现了多种可能的攻击方法提出了详细的检测及整改方案。

漏洞影响

带有指定参数的ECC密钥的证书的Microsoft Windows版本
依赖于Windows CryptoAPI的应用程序

漏洞复现

CVE-2020-0601的根源是微软的加密库crypt32.dll中椭圆曲线加密算法的实现问题首先我们来了解一下椭圆加密算法的基本原理。

要形象地理解椭圆曲线加密算法可以结合图形来看以下是一个符合椭圆曲线的方程y2 = x3 + ax + b图像如下

img

椭圆曲线具有的一些独特的性质使它适合用于加密算法:

  1. 椭圆曲线关于x轴对称

  2. 任何一条非垂直的线与曲线最多有三个点相交

  3. 曲线是光滑的,即曲线的所有点都没有两个或者两个以上的不同的切线

在椭圆曲线上任意两点A、B若A、B重合则作A的切线作直线交于椭圆曲线另一点C过C做y轴的平行线与椭圆曲线交于C点定义A+B=C。椭圆曲线的加法符合交换律和结合律。

img

如果A、B是同一个点则过A作椭圆曲线的切线以同样的方法得到对应的结果 C=2A 。

img

接下来是椭圆曲线加密相关的重点如果对多个A进行累加,则可依次累加连线得到nA的值 。

起点为A终点D=3A阶为3 。

img

起点为A终点G=4A阶为4。

img

椭圆曲线加密

img

考虑K=kG其中K、G为椭圆曲线Ep(a,b)上的点n为G的阶。k为小于n的整数。则给定k和G根据加法法则计算K很容易只要逐次求解但反过来给定K和G求k就非常困难。因为实际使用中的ECC原则上把私钥k取得相当大n也相当大且椭圆曲线不再连续而是在实数内离散的值要把n个解点逐一算出几乎是不可能的。这就是椭圆曲线加密算法的数学依据 。

  • 点G称为基点

  • k(k<n)为私有密钥

  • K为公开密钥

椭圆曲线加密算法ECC和RSA一样是一种公开密钥加密技术对原始数据以公钥加密以私钥解密即便攻击者获取密文和公钥也无法在合理的时间或代价下解密获取明文。

同样的椭圆曲线加密算法ECC也被用于数字签名以私钥加密生成签名以公钥解密验证签名如果和原文一样则签名验证成功。

公开密钥加密之所以可靠是因为它们利用了公钥密码领域的单向函数原理,正向操作非常简单,而逆向操作非常困难。

由G基点出发进行k私钥次变换只要按部就班地计算就能很容易地得到终点K公钥的值。

img

已知起点G基点和终点K公钥要逆推得到移动次数k私钥则是一个很难的问题最佳算法也达到了全指数复杂度

img

相比传统RSA加密算法椭圆加密算法具有着天生的优势椭圆加密算法的逆向过程相比RSA有着更大的时间复杂度。在密钥长度相同的情况下椭圆加密算法相比RSA具有更好的安全强度。一般认为160比特的椭圆曲线密钥即可提供与1024比特的RSA密钥相当的安全强度。

较短的密钥也意味着更少的存储空间、更快的加解密速度和更少的带宽消耗正因为椭圆加密算法的这些优势它被用于Windows的签名系统、https的证书、比特币系统和中国的二代身份证系统中。

虽然椭圆曲线加密算法具有着许多优势,纯算法角度破解难度极大,微软对此算法的实现的缺漏却给漏洞利用提供了可乘之机。回到椭圆曲线加密最基本的等式 K=kG首先需要明确的是虽然对于给定的基点G和公钥K要求解私钥k很困难但是如果可以任意指定基点G要构造一对k和G使等式成立却极其简单最简单的情况令基点G=K则私钥k=1这样一对基点和私钥可以使等式成立也是有效的解。

在正常的标准椭圆曲线算法中基点G并不是随意指定的而是有固定的值标准的作用便是对基点G等参数的选择做出规定例如在secp256r1版本的椭圆曲线算法中基点G应当为标准规定的固定值如果对参数不加验证使得用户可以自定义传入的基点G的值作为函数的参数上面的私钥k=1的特殊解即可成立。

在有漏洞版本的crypt32.dll中验证使用ECC算法签名部分的函数恰恰是这个情况原先的函数未加参数验证参与计算的基点G的内容由被验证的证书随意指定使未授权的证书能够构建私钥k=1的特殊解来成功通过椭圆加密算法的签名验证的过程。

让我们以CVE-2020-0601的一个POC为例来解析虚假密钥的构建过程

require 'openssl'

raw = File.read ARGV[0]     # 读取使用ECC算法的证书文件
ca = OpenSSL::X509::Certificate.new(raw)    # 读取使用ECC算法的证书
ca_key = ca.public_key  # 从证书中提取公钥ca_key

ca_key.private_key = 1  # 设置私钥为1使得公钥K==1*基点G的等式成立
group = ca_key.group 
group.set_generator(ca_key.public_key, group.order, group.cofactor)
group.asn1_flag = OpenSSL::PKey::EC::EXPLICIT_CURVE
ca_key.group = group        # 利用构建的假基点G和假密钥k设置新group
File.open("spoofed_ca.key", 'w') { |f| f.write ca_key.to_pem }  # 将新的group写入文件

img

修补后的Crypt32.dll中椭圆曲线加密算法的函数已加入了参数验证的部分解决了由自由指定参数G导致的构造第二个特殊的有效密钥的问题。

一处验证机制的失误导致信任链的连锁反应。

img

现代的安全系统中存在着“信任链”的概念,信任链的上下级存在一种类似单向担保的关系,子级证书的可靠性由签名其的父级证书担保。签名时由根证书开始一级级向下签名,验证时则逐层溯源验证,直到找到信任的根证书文件,构成了一条信任链。位于整个“信任链”最上方的是最为重要不需要自证身份的根证书。根证书一般随系统附带或由管理员安装在系统内。

这个漏洞的存在则使得构造的无效签名通过了验证机制,使本应断裂的信任链被利用,逐级担保继续下去,最终使非法内容获得了证书所有者的合法签名身份。

漏洞POC

https://github.com/ollypwn/CurveBall

参考文章

cve-2020-0601-windows-cryptoapi欺骗漏洞分析