예제 #1
0
        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),
                    context.TargetUri.GetFileName(),
                    failedApiName,
                    cryptoError.ToString()));
                return null;
            }

            return GetAlgorithmName(certInfo.SignatureAlgorithm.pszObjId);
        }
예제 #2
0
        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;
        }
예제 #3
0
        private CryptoError GetCertInfo(IntPtr hWVTStateData, out Native.CERT_INFO certInfo, out string failedApiName)
        {
            certInfo = new Native.CERT_INFO();

            failedApiName = "WTHelperProvDataFromStateData";
            IntPtr providerData = Native.WTHelperProvDataFromStateData(hWVTStateData);
            if (providerData == IntPtr.Zero)
            {
                return (CryptoError)Marshal.GetLastWin32Error();
            }

            failedApiName = "WTHelperGetProvSignerFromChain";
            IntPtr providerSigner = Native.WTHelperGetProvSignerFromChain(providerData, 0, false, 0);

            if (providerSigner == IntPtr.Zero)
            {
                return (CryptoError)Marshal.GetLastWin32Error();
            }

            var cryptProviderSigner = (Native.CRYPT_PROVIDER_SGNR)Marshal.PtrToStructure(providerSigner, typeof(Native.CRYPT_PROVIDER_SGNR));

            var cryptoError = (CryptoError)cryptProviderSigner.dwError;

            if (cryptProviderSigner.pChainContext != IntPtr.Zero)
            {
                var chainContext = (Native.CERT_CHAIN_CONTEXT)Marshal.PtrToStructure(cryptProviderSigner.pChainContext, typeof(Native.CERT_CHAIN_CONTEXT));

                IntPtr[] simpleChains = new IntPtr[chainContext.cChain];
                Marshal.Copy(chainContext.rgpChain, simpleChains, 0, simpleChains.Length);
                var certChain = (Native.CERT_SIMPLE_CHAIN)Marshal.PtrToStructure(simpleChains[0], typeof(Native.CERT_SIMPLE_CHAIN));

                IntPtr[] chainElements = new IntPtr[certChain.cElement];
                Marshal.Copy(certChain.rgpElement, chainElements, 0, chainElements.Length);
                var certElement = (Native.CERT_CHAIN_ELEMENT)Marshal.PtrToStructure(chainElements[0], typeof(Native.CERT_CHAIN_ELEMENT));

                var certContext = (Native.CERT_CONTEXT)Marshal.PtrToStructure(certElement.pCertContext, typeof(Native.CERT_CONTEXT));
                certInfo = (Native.CERT_INFO)Marshal.PtrToStructure(certContext.pCertInfo, typeof(Native.CERT_INFO));
            }

            if (cryptoError == CryptoError.ERROR_SUCCESS)
            {
                failedApiName = null;
            }

            return cryptoError;
        }
예제 #4
0
        private void InvokeCloseAction(Native.WINTRUST_DATA winTrustData)
        {
            Guid action = Native.ActionGenericVerifyV2;
            winTrustData.StateAction = Native.StateAction.WTD_STATEACTION_CLOSE;
            Native.WinVerifyTrust(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);
            }
        }
예제 #5
0
        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);
        }