private void InvokeCloseAction(Native.WINTRUST_DATA winTrustData) { Guid action = Native.ActionGenericVerifyV2; winTrustData.StateAction = Native.StateAction.WTD_STATEACTION_CLOSE; Native.WinVerifyTrustWrapper(Native.INVALID_HANDLE_VALUE, ref action, ref winTrustData); if (winTrustData.pFile != IntPtr.Zero) { Marshal.DestroyStructure(winTrustData.pFile, typeof(Native.WINTRUST_FILE_INFO)); Marshal.FreeHGlobal(winTrustData.pFile); } if (winTrustData.pSignatureSettings != IntPtr.Zero) { var signatureSettings = Marshal.PtrToStructure <Native.WINTRUST_SIGNATURE_SETTINGS>(winTrustData.pSignatureSettings); if (signatureSettings.pCryptoPolicy != IntPtr.Zero) { Marshal.DestroyStructure(signatureSettings.pCryptoPolicy, typeof(Native.CERT_STRONG_SIGN_PARA)); Marshal.FreeHGlobal(signatureSettings.pCryptoPolicy); } Marshal.DestroyStructure(winTrustData.pSignatureSettings, typeof(Native.WINTRUST_SIGNATURE_SETTINGS)); Marshal.FreeHGlobal(winTrustData.pSignatureSettings); } }
public override void Analyze(BinaryAnalyzerContext context) { // Uses Windows Certificate Interop BinaryParsers.PlatformSpecificHelpers.ThrowIfNotOnWindows(); PEBinary target = context.PEBinary(); Native.WINTRUST_DATA winTrustData; string algorithmName, filePath; filePath = target.PE.FileName; winTrustData = new Native.WINTRUST_DATA(); if (InvokeVerifyAction(context, filePath, out winTrustData, out algorithmName)) { // '{0}' appears to be signed securely by a trusted publisher with no // verification or time stamp errors. Revocation checking was performed // on the entire certificate chain, excluding the root certificate. // The following digitial signature algorithms were detected: {1} context.Logger.Log(this, RuleUtilities.BuildResult(ResultKind.Pass, context, null, nameof(RuleResources.BA2022_Pass), context.TargetUri.GetFileName(), algorithmName)); } }
public override void Analyze(BinaryAnalyzerContext context) { Native.WINTRUST_DATA winTrustData; string algorithmName, filePath; filePath = context.PE.FileName; winTrustData = new Native.WINTRUST_DATA(); try { if (InvokeVerifyAction(context, filePath, out winTrustData, out algorithmName)) { // '{0}' appears to be signed securely by a trusted publisher with // no verification or time stamp errors. Revocation checking was // performed on the entire certificate chain, excluding the root // certificate. The image was signed with '{1}', a // cryptographically strong algorithm. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Pass, context, null, nameof(RuleResources.BA2022_Pass), algorithmName)); } } finally { if (winTrustData.hWVTStateData != IntPtr.Zero) { InvokeCloseAction(winTrustData); } } }
private bool GetSignerHashAlgorithms( BinaryAnalyzerContext context, Native.WINTRUST_DATA winTrustData, out string hashAlgorithm, out string hashEncryptionAlgorithm) { string failedApiName; CryptoError cryptoError; cryptoError = GetSignerHashAlgorithms( winTrustData.hWVTStateData, out hashAlgorithm, out hashEncryptionAlgorithm, out failedApiName); if (cryptoError != CryptoError.ERROR_SUCCESS) { // '{0}' signing could not be completely verified because // '{1}' failed with error code: '{2}'. context.Logger.Log(this, RuleUtilities.BuildResult(FailureLevel.Error, context, null, nameof(RuleResources.BA2022_Error_WinTrustVerifyApiError), context.TargetUri.GetFileName(), failedApiName, cryptoError.ToString())); return(false); } return(true); }
private CryptoError WinVerifyTrustHelper(IntPtr handle, ref Native.WINTRUST_DATA winTrustData) { Guid action; CryptoError cryptoError; action = Native.ActionGenericVerifyV2; cryptoError = (CryptoError)Native.WinVerifyTrust(handle, ref action, ref winTrustData); return(cryptoError); }
private Native.WINTRUST_DATA InitializeWinTrustDataStruct(string filePath, bool enforcePolicy) { // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa382384(v=vs.85).aspx // which was used to drive data initialization, API use and comments in this code. var winTrustData = new Native.WINTRUST_DATA(); winTrustData.cbStruct = (uint)Marshal.SizeOf(typeof(Native.WINTRUST_DATA)); var fileInfo = new Native.WINTRUST_FILE_INFO(); fileInfo.cbStruct = (uint)Marshal.SizeOf(typeof(Native.WINTRUST_FILE_INFO)); fileInfo.pcwszFilePath = filePath; winTrustData.pFile = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Native.WINTRUST_FILE_INFO))); Marshal.StructureToPtr(fileInfo, winTrustData.pFile, false); winTrustData.pPolicyCallbackData = IntPtr.Zero; // Use default code signing EKU winTrustData.pSIPClientData = IntPtr.Zero; // No data to pass to SIP winTrustData.UIChoice = Native.UIChoice.WTD_UI_NONE; // Disable all UI on execution winTrustData.UIContext = 0; winTrustData.UIContext = Native.UIContext.WTD_UICONTEXT_EXECUTE; // File is intended to be executed winTrustData.UnionChoice = Native.UnionChoice.WTD_CHOICE_FILE; // We're verifying a file winTrustData.RevocationChecks = Native.RevocationChecks.WTD_REVOKE_WHOLECHAIN; // Revocation checking on whole chain. winTrustData.dwProvFlags = Native.ProviderFlags.WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; // Don't reach across the network winTrustData.dwProvFlags |= Native.ProviderFlags.WTD_CACHE_ONLY_URL_RETRIEVAL; winTrustData.pwszURLReference = null; // Reserved for future use winTrustData.StateAction = Native.StateAction.WTD_STATEACTION_VERIFY; winTrustData.hWVTStateData = IntPtr.Zero; // This value set by API call if (enforcePolicy) { var signatureSettings = new Native.WINTRUST_SIGNATURE_SETTINGS(); signatureSettings.cbStruct = (uint)Marshal.SizeOf(typeof(Native.WINTRUST_SIGNATURE_SETTINGS)); signatureSettings.dwIndex = 0; signatureSettings.dwFlags = Native.SignatureSettingsFlags.WSS_VERIFY_SPECIFIC; signatureSettings.cSecondarySigs = 0; signatureSettings.dwVerifiedSigIndex = 0; var policy = new Native.CERT_STRONG_SIGN_PARA(); policy.cbStruct = (uint)Marshal.SizeOf(typeof(Native.CERT_STRONG_SIGN_PARA)); policy.dwInfoChoice = Native.InfoChoice.CERT_STRONG_SIGN_OID_INFO_CHOICE; policy.pszOID = Native.szOID_CERT_STRONG_SIGN_OS_1; signatureSettings.pCryptoPolicy = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Native.CERT_STRONG_SIGN_PARA))); Marshal.StructureToPtr(policy, signatureSettings.pCryptoPolicy, false); winTrustData.pSignatureSettings = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Native.WINTRUST_SIGNATURE_SETTINGS))); Marshal.StructureToPtr(signatureSettings, winTrustData.pSignatureSettings, false); } return(winTrustData); }
private string RetrieveSignatureAlgorithmAndCertInfo( BinaryAnalyzerContext context, Native.WINTRUST_DATA winTrustData, out Native.CERT_INFO certInfo) { string failedApiName; CryptoError cryptoError; cryptoError = GetCertInfo(winTrustData.hWVTStateData, out certInfo, out failedApiName); if (cryptoError != CryptoError.ERROR_SUCCESS) { // '{0}' signing could not be completely verified because // '{1}' failed with error code: '{2}'. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2022_Error_WinTrustVerifyApiError), failedApiName, cryptoError.ToString())); return(null); } return(GetAlgorithmName(certInfo.SignatureAlgorithm.pszObjId)); }
private bool InvokeVerifyAction( BinaryAnalyzerContext context, string filePath, out Native.WINTRUST_DATA winTrustData, out string algorithmsText) { Guid action; CryptoError cryptoError; var badAlgorithms = new List <Tuple <string, string> >(); var goodAlgorithms = new List <Tuple <string, string> >(); algorithmsText = null; action = Native.ActionGenericVerifyV2; uint signatureCount = 1; // First, we retrieve the signature count winTrustData = InitializeWinTrustDataStruct(filePath, WinTrustDataKind.SignatureCount); Native.WinVerifyTrustWrapper(Native.INVALID_HANDLE_VALUE, ref action, ref winTrustData); if (winTrustData.pSignatureSettings != IntPtr.Zero) { var signatureSettings = Marshal.PtrToStructure <Native.WINTRUST_SIGNATURE_SETTINGS>(winTrustData.pSignatureSettings); signatureCount = signatureSettings.cSecondarySigs + 1; // Total count primary + cSecondary } InvokeCloseAction(winTrustData); // First, we will invoke the basic verification on all returned // signatures. Note that currently this code path does not reach across // the network to perform its function. We could optionally // enable this (which would require altering the code that initializes // our WINTRUST_DATA instance). for (uint i = 0; i < signatureCount; i++) { string hashAlgorithm, hashEncryptionAlgorithm; winTrustData = InitializeWinTrustDataStruct(filePath, WinTrustDataKind.EnforcePolicy, i); cryptoError = (CryptoError)Native.WinVerifyTrustWrapper(Native.INVALID_HANDLE_VALUE, ref action, ref winTrustData); switch (cryptoError) { // The SignSecurely check mostly validates signing algorithm strength. The // error conditions are expected in some scan contexts, for example, an // isolated build environment which hasn't been configured to trust the // signing root. Providing a more complex signing validation would require // BinSkim to be significantly more configurable to provide information on // the scan environment as well as the scan targets. case CryptoError.CERT_E_UNTRUSTEDROOT: case CryptoError.CERT_E_CHAINING: case CryptoError.ERROR_SUCCESS: { // Hash that represents the subject is trusted. // Trusted publisher with no verification errors. // No publisher or time stamp errors. // This verification excludes root chain info. if (GetSignerHashAlgorithms(context, winTrustData, out hashAlgorithm, out hashEncryptionAlgorithm)) { goodAlgorithms.Add(new Tuple <string, string>(hashAlgorithm, hashEncryptionAlgorithm)); } InvokeCloseAction(winTrustData); break; } case CryptoError.NTE_BAD_ALGID: { InvokeCloseAction(winTrustData); // We cannot retrieve algorithm id and cert info for images that fail // the stringent WinTrustVerify security check. We therefore start // a new call chain with looser validation criteria. winTrustData = InitializeWinTrustDataStruct(filePath, WinTrustDataKind.Normal); Native.WinVerifyTrustWrapper(Native.INVALID_HANDLE_VALUE, ref action, ref winTrustData); if (GetSignerHashAlgorithms(context, winTrustData, out hashAlgorithm, out hashEncryptionAlgorithm)) { badAlgorithms.Add(new Tuple <string, string>(hashAlgorithm, hashEncryptionAlgorithm)); } InvokeCloseAction(winTrustData); break; } case CryptoError.TRUST_E_NOSIGNATURE: { Notes.LogNotApplicableToSpecifiedTarget(context, MetadataConditions.ImageIsNotSigned); return(false); } default: { string cryptoErrorDescription = cryptoError.GetErrorDescription(); // '{0}' signing was flagged as insecure by WinTrustVerify with error code: '{1}' ({2}) context.Logger.Log(this, RuleUtilities.BuildResult(FailureLevel.Error, context, null, nameof(RuleResources.BA2022_Error_DidNotVerify), context.TargetUri.GetFileName(), cryptoError.ToString(), cryptoErrorDescription)); InvokeCloseAction(winTrustData); return(false); } } } algorithmsText = BuildAlgorithmsText(badAlgorithms, goodAlgorithms); if (goodAlgorithms.Count == 0) { // '{0}' was signed exclusively with algorithms that WinTrustVerify has flagged as insecure. {1} context.Logger.Log(this, RuleUtilities.BuildResult(FailureLevel.Error, context, null, nameof(RuleResources.BA2022_Error_BadSigningAlgorithm), context.TargetUri.GetFileName(), algorithmsText)); } return(goodAlgorithms.Count > 0); }
public override void Analyze(BinaryAnalyzerContext context) { Native.WINTRUST_DATA winTrustData; string algorithmName, filePath; filePath = context.PE.FileName; winTrustData = new Native.WINTRUST_DATA(); if (InvokeVerifyAction(context, filePath, out winTrustData, out algorithmName)) { // '{0}' appears to be signed securely by a trusted publisher with // no verification or time stamp errors. Revocation checking was // performed on the entire certificate chain, excluding the root // certificate. The image was signed with '{1}', // cryptographically strong algorithm(s). context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Pass, context, null, nameof(RuleResources.BA2022_Pass), context.TargetUri.GetFileName(), algorithmName)); } }
private Native.WINTRUST_DATA InitializeWinTrustDataStruct(string filePath, WinTrustDataKind kind, uint signatureIndex = 0) { // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa382384(v=vs.85).aspx // which was used to drive data initialization, API use and comments in this code. var winTrustData = new Native.WINTRUST_DATA(); winTrustData.cbStruct = (uint)Marshal.SizeOf(typeof(Native.WINTRUST_DATA)); var fileInfo = new Native.WINTRUST_FILE_INFO(); fileInfo.cbStruct = (uint)Marshal.SizeOf(typeof(Native.WINTRUST_FILE_INFO)); fileInfo.pcwszFilePath = filePath; winTrustData.pFile = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Native.WINTRUST_FILE_INFO))); Marshal.StructureToPtr(fileInfo, winTrustData.pFile, false); winTrustData.pPolicyCallbackData = IntPtr.Zero; // Use default code signing EKU winTrustData.pSIPClientData = IntPtr.Zero; // No data to pass to SIP winTrustData.UIChoice = Native.UIChoice.WTD_UI_NONE; // Disable all UI on execution winTrustData.UIContext = 0; winTrustData.UIContext = Native.UIContext.WTD_UICONTEXT_EXECUTE; // File is intended to be executed winTrustData.UnionChoice = Native.UnionChoice.WTD_CHOICE_FILE; // We're verifying a file winTrustData.RevocationChecks = Native.RevocationChecks.WTD_REVOKE_WHOLECHAIN; // Revocation checking on whole chain. winTrustData.dwProvFlags = Native.ProviderFlags.WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; // Don't reach across the network winTrustData.dwProvFlags |= Native.ProviderFlags.WTD_CACHE_ONLY_URL_RETRIEVAL; winTrustData.pwszURLReference = null; // Reserved for future use winTrustData.StateAction = Native.StateAction.WTD_STATEACTION_VERIFY; winTrustData.hWVTStateData = IntPtr.Zero; // This value set by API call if (kind != WinTrustDataKind.Normal) { Native.SignatureSettingsFlags flags = Native.SignatureSettingsFlags.WSS_GET_SECONDARY_SIG_COUNT; if (kind == WinTrustDataKind.EnforcePolicy) { flags = Native.SignatureSettingsFlags.WSS_VERIFY_SPECIFIC; } var signatureSettings = new Native.WINTRUST_SIGNATURE_SETTINGS(); signatureSettings.cbStruct = (uint)Marshal.SizeOf(typeof(Native.WINTRUST_SIGNATURE_SETTINGS)); signatureSettings.dwIndex = signatureIndex; signatureSettings.dwFlags = flags; signatureSettings.cSecondarySigs = 0; signatureSettings.dwVerifiedSigIndex = 0; var policy = new Native.CERT_STRONG_SIGN_PARA(); policy.cbStruct = (uint)Marshal.SizeOf(typeof(Native.CERT_STRONG_SIGN_PARA)); policy.dwInfoChoice = Native.InfoChoice.CERT_STRONG_SIGN_OID_INFO_CHOICE; policy.pszOID = Native.szOID_CERT_STRONG_SIGN_OS_1; signatureSettings.pCryptoPolicy = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Native.CERT_STRONG_SIGN_PARA))); Marshal.StructureToPtr(policy, signatureSettings.pCryptoPolicy, false); winTrustData.pSignatureSettings = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Native.WINTRUST_SIGNATURE_SETTINGS))); Marshal.StructureToPtr(signatureSettings, winTrustData.pSignatureSettings, false); } return winTrustData; }
private bool InvokeVerifyAction( BinaryAnalyzerContext context, string filePath, out Native.WINTRUST_DATA winTrustData, out string algorithmName) { Guid action; bool continueProcessing; CryptoError cryptoError; continueProcessing = false; var certInfo = new Native.CERT_INFO(); winTrustData = InitializeWinTrustDataStruct(filePath, enforcePolicy: true); algorithmName = null; // First, we will invoke the basic verification. Note that currently this code path // does not reach across the network to perform its function. We could optionally // enable this (which would require altering the code that initializes our // WINTRUST_DATA instance). action = Native.ActionGenericVerifyV2; cryptoError = (CryptoError)Native.WinVerifyTrust(Native.INVALID_HANDLE_VALUE, ref action, ref winTrustData); InvokeCloseAction(winTrustData); // We cannot retrieve algorithm id and cert info for images that fail // the stringent WinTrustVerify security check. We therefore start // a new call chain with looser validation criteria. winTrustData = InitializeWinTrustDataStruct(filePath, enforcePolicy: false); Native.WinVerifyTrust(Native.INVALID_HANDLE_VALUE, ref action, ref winTrustData); switch (cryptoError) { case CryptoError.ERROR_SUCCESS: { // Hash that represents the subject is trusted. // Trusted publisher with no verification errors. // No publisher or time stamp errors. // This verification excludes root chain info. algorithmName = RetrieveSignatureAlgorithmAndCertInfo(context, winTrustData, out certInfo); continueProcessing = true; break; } case CryptoError.NTE_BAD_ALGID: { algorithmName = RetrieveSignatureAlgorithmAndCertInfo(context, winTrustData, out certInfo); if (algorithmName != null) // If null, we have already logged an error { // '{0}' was signed using '{1}', an algorithm that WinTrustVerify has flagged as insecure. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2022_Error_BadSigningAlgorithm), algorithmName)); } break; } case CryptoError.TRUST_E_NOSIGNATURE: { Notes.LogNotApplicableToSpecifiedTarget(context, MetadataConditions.ImageIsNotSigned); break; } default: { string cryptoErrorDescription = cryptoError.GetErrorDescription(); // '{0}' signing was flagged as insecure by WinTrustVerify with error code: '{1}' ({2}) context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2022_Error_DidNotVerify), cryptoError.ToString(), cryptoErrorDescription)); break; } } return(continueProcessing); }
private bool InvokeVerifyAction( BinaryAnalyzerContext context, string filePath, out Native.WINTRUST_DATA winTrustData, out string algorithmNames) { Guid action; CryptoError cryptoError; HashSet <string> badAlgorithms = new HashSet <string>(); HashSet <string> goodAlgorithms = new HashSet <string>(); var certInfo = new Native.CERT_INFO(); algorithmNames = null; action = Native.ActionGenericVerifyV2; uint signatureCount = 1; // First, we retrieve the signature count winTrustData = InitializeWinTrustDataStruct(filePath, WinTrustDataKind.SignatureCount); Native.WinVerifyTrust(Native.INVALID_HANDLE_VALUE, ref action, ref winTrustData); if (winTrustData.pSignatureSettings != IntPtr.Zero) { var signatureSettings = Marshal.PtrToStructure <Native.WINTRUST_SIGNATURE_SETTINGS>(winTrustData.pSignatureSettings); signatureCount = signatureSettings.cSecondarySigs + 1; // Total count primary + cSecondary } InvokeCloseAction(winTrustData); // First, we will invoke the basic verification on all returned // signatures. Note that currently this code path does not reach across // the network to perform its function. We could optionally // enable this (which would require altering the code that initializes // our WINTRUST_DATA instance). for (uint i = 0; i < signatureCount; i++) { string algorithmName; winTrustData = InitializeWinTrustDataStruct(filePath, WinTrustDataKind.EnforcePolicy, i); cryptoError = (CryptoError)Native.WinVerifyTrust(Native.INVALID_HANDLE_VALUE, ref action, ref winTrustData); switch (cryptoError) { case CryptoError.ERROR_SUCCESS: { // Hash that represents the subject is trusted. // Trusted publisher with no verification errors. // No publisher or time stamp errors. // This verification excludes root chain info. algorithmName = RetrieveSignatureAlgorithmAndCertInfo(context, winTrustData, out certInfo); goodAlgorithms.Add(algorithmName); InvokeCloseAction(winTrustData); break; } case CryptoError.NTE_BAD_ALGID: { InvokeCloseAction(winTrustData); // We cannot retrieve algorithm id and cert info for images that fail // the stringent WinTrustVerify security check. We therefore start // a new call chain with looser validation criteria. winTrustData = InitializeWinTrustDataStruct(filePath, WinTrustDataKind.Normal); Native.WinVerifyTrust(Native.INVALID_HANDLE_VALUE, ref action, ref winTrustData); algorithmName = RetrieveSignatureAlgorithmAndCertInfo(context, winTrustData, out certInfo); badAlgorithms.Add(algorithmName); InvokeCloseAction(winTrustData); break; } case CryptoError.TRUST_E_NOSIGNATURE: { Notes.LogNotApplicableToSpecifiedTarget(context, MetadataConditions.ImageIsNotSigned); return(false); } default: { string cryptoErrorDescription = cryptoError.GetErrorDescription(); // '{0}' signing was flagged as insecure by WinTrustVerify with error code: '{1}' ({2}) context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2022_Error_DidNotVerify), context.TargetUri.GetFileName(), cryptoError.ToString(), cryptoErrorDescription)); InvokeCloseAction(winTrustData); return(false); } } } if (goodAlgorithms.Count == 0) { // '{0}' was signed using '{1}', algorithm(s) that WinTrustVerify has flagged as insecure. context.Logger.Log(this, RuleUtilities.BuildResult(ResultLevel.Error, context, null, nameof(RuleResources.BA2022_Error_BadSigningAlgorithm), context.TargetUri.GetFileName(), string.Join(",", badAlgorithms.ToArray()))); } else { algorithmNames = string.Join(",", goodAlgorithms.ToArray()); } return(goodAlgorithms.Count > 0); }