Nytro Posted November 12, 2014 Report Posted November 12, 2014 MS14-066 schannel.dll SPVerifySignature (Windows 2003 SP2)/*Summarizing the most likely conditions here these bugs occur:Code Execution: Heap smash via qmemcpy() if CryptDecodeObject() returns more than 40 bytes in pcbStructInfoVerification Bypass: A failed decode results in a positive return value. This is not STATUS_SUCCESS, but a caller that is checking <0 vs == 0 would think verification succeeded on a bad decode.*///----- (7676F6D4) --------------------------------------------------------int __stdcall SPVerifySignature(HCRYPTPROV hProv, int a2, ALG_ID Algid, BYTE *pbData, DWORD dwDataLen, BYTE *pbEncoded, DWORD cbEncoded, int a8){ signed int v8; // esi@4 BOOL v9; // eax@8 DWORD v10; // eax@14 DWORD pcbStructInfo; // [sp+Ch] [bp-3Ch]@11 HCRYPTKEY phKey; // [sp+10h] [bp-38h]@1 HCRYPTHASH phHash; // [sp+14h] [bp-34h]@1 BYTE *pbSignature; // [sp+18h] [bp-30h]@1 char pvStructInfo[40]; // [sp+1Ch] [bp-2Ch]@11 phKey = 0; phHash = 0; pbSignature = 0; if ( hProv && a2 ) { // Allocate cbEncoded bytes on the heap for the signature pbSignature = (BYTE *)SPExternalAlloc(cbEncoded); if ( !pbSignature ) { // Exit early if the allocation failed v8 = -2146893056; goto LABEL_18; } // Import the key and create the hash, bailing out if it fails if ( !CryptImportKey(hProv, *(const BYTE **)a2, *(_DWORD *)(a2 + 4), 0, 0, &phKey) || !CryptCreateHash(hProv, Algid, 0, 0, &phHash) ) goto LABEL_12; // Verify that CryptHashData or CryptSetHashParam succeeds (but how is a8 being set?) v9 = a8 ? CryptHashData(phHash, pbData, dwDataLen, 0) : CryptSetHashParam(phHash, 2u, pbData, 0); if ( !v9 ) goto LABEL_12; if ( *(_DWORD *)(*(_DWORD *)a2 + 4) == 8704 ) { // Indicate that we have 40 bytes to decode the signature value pcbStructInfo = 40; // CryptDecodeObject() states that pvStructInfo can be larger than pcbStructInfo // Bail out if the decode fails /* BOOL WINAPI CryptDecodeObject( _In_ DWORD dwCertEncodingType, (X509_ASN_ENCODING) _In_ LPCSTR lpszStructType, (X509_DSS_SIGNATURE) _In_ const BYTE *pbEncoded, (Caller) _In_ DWORD cbEncoded, (Caller) _In_ DWORD dwFlags, (0) _Out_ void *pvStructInfo, (40 byte stack variable) _Inout_ DWORD *pcbStructInfo (in:40, out:arbitrary) ); pcbStructInfo [in, out]: A pointer to a DWORD value specifying the size, in bytes, of the buffer pointed to by the pvStructInfo parameter. When the function returns, this DWORD value contains the size of the decoded data copied to pvStructInfo. The size contained in the variable pointed to by pcbStructInfo can indicate a size larger than the decoded structure, as the decoded structure can include pointers to other structures. This size is the sum of the size needed by the decoded structure and other structures pointed to. */ if ( !CryptDecodeObject(X509_ASN_ENCODING, X509_DSS_SIGNATURE, pbEncoded, cbEncoded, 0, &pvStructInfo, &pcbStructInfo) ) {LABEL_12: // this might be the signature bypass vector if the caller incorrectly checks <0 vs STATUS_SUCCESS GetLastError(); v8 = 3; goto LABEL_18; } v10 = pcbStructInfo; // This is likely our RCE vector, if pcbStructInfo > cbEncoded qmemcpy(pbSignature, &pvStructInfo, pcbStructInfo); // Changes cbEncoded to the (possibly bad) returned value of pcbStructInfo cbEncoded = v10; } else { ReverseMemCopy((unsigned int)pbSignature, (int)pbEncoded, cbEncoded); } v8 = CryptVerifySignatureA(phHash, pbSignature, cbEncoded, phKey, 0, 0) != 0 ? 0 : -2147483391; } else { v8 = -1; }LABEL_18: if ( phKey ) CryptDestroyKey(phKey); if ( phHash ) CryptDestroyHash(phHash); if ( pbSignature ) SPExternalFree(pbSignature); return v8;}Sursa: https://gist.github.com/hmoore-r7/3379af8b0419ddb0c76b Quote