public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;
            Pdb      di       = context.Pdb;

            if (di == null)
            {
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildCouldNotLoadPdbMessage(context));
                return;
            }

            TruncatedCompilandRecordList warningTooLowModules      = new TruncatedCompilandRecordList();
            TruncatedCompilandRecordList disabledWarningModules    = new TruncatedCompilandRecordList();
            TruncatedCompilandRecordList unknownLanguageModules    = new TruncatedCompilandRecordList();
            TruncatedCompilandRecordList allWarningLevelLowModules = new TruncatedCompilandRecordList();

            string     exampleTooLowWarningCommandLine   = null;
            int        overallMinimumWarningLevel        = Int32.MaxValue;
            string     exampleDisabledWarningCommandLine = null;
            List <int> overallDisabledWarnings           = new List <int>();

            foreach (DisposableEnumerableView <Symbol> omView in di.CreateObjectModuleIterator())
            {
                Symbol om = omView.Value;
                ObjectModuleDetails omDetails = om.GetObjectModuleDetails();
                if (omDetails.Language == Language.Unknown)
                {
                    // See if this module contributed to an executable section. If not, we can ignore the module.
                    if (di.CompilandWithIdIsInExecutableSectionContrib(om.SymIndexId))
                    {
                        unknownLanguageModules.Add(om.CreateCompilandRecord());
                    }

                    continue;
                }

                if ((omDetails.Language != Language.C) && (omDetails.Language != Language.Cxx))
                {
                    continue;
                }

                if (omDetails.Compiler != "Microsoft (R) Optimizing Compiler")
                {
                    continue;
                }

                if (!om.CreateChildIterator(SymTagEnum.SymTagFunction).Any())
                {
                    // uninteresting...
                    continue;
                }

                int        warningLevel             = omDetails.WarningLevel;
                List <int> requiredDisabledWarnings = omDetails.ExplicitlyDisabledWarnings
                                                      .Where(context.Policy.GetProperty(RequiredCompilerWarnings).Contains).ToList();

                if (warningLevel >= 3 && requiredDisabledWarnings.Count == 0)
                {
                    // We duplicate this condition to bail out early and avoid writing the
                    // module description or newline into sbBadWarningModules if everything
                    // in the module is OK.
                    continue;
                }

                List <string> suffix = new List <string>(2);

                overallMinimumWarningLevel = Math.Min(overallMinimumWarningLevel, warningLevel);
                if (warningLevel < 3)
                {
                    exampleTooLowWarningCommandLine = exampleTooLowWarningCommandLine ?? omDetails.CommandLine;

                    string msg = "[warning level: " + warningLevel.ToString(CultureInfo.InvariantCulture) + "]";
                    warningTooLowModules.Add(om.CreateCompilandRecordWithSuffix(msg));
                    suffix.Add(msg);
                }

                if (requiredDisabledWarnings.Count != 0)
                {
                    MergeInto(overallDisabledWarnings, requiredDisabledWarnings);
                    exampleDisabledWarningCommandLine = exampleDisabledWarningCommandLine ?? omDetails.CommandLine;

                    string msg = "[Explicitly disabled warnings: " + CreateTextWarningList(requiredDisabledWarnings) + "]";
                    disabledWarningModules.Add(om.CreateCompilandRecordWithSuffix(msg));
                    suffix.Add(msg);
                }

                allWarningLevelLowModules.Add(om.CreateCompilandRecordWithSuffix(String.Join(" ", suffix)));
            }

            if (unknownLanguageModules.Empty &&
                exampleTooLowWarningCommandLine == null &&
                exampleDisabledWarningCommandLine == null)
            {
                // '{0}' was compiled at a secure warning level ({1}) and does not
                // include any modules that disable specific warnings which are
                // required by policy. As a result, there is a greater likelihood
                // that memory corruption, information disclosre, double-free and
                // other security-related vulnerabilities do not exist in code.
                context.Logger.Log(MessageKind.Pass, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.EnableCriticalCompilerWarnings_Pass, overallMinimumWarningLevel.ToString()));
                return;
            }

            if (!unknownLanguageModules.Empty)
            {
                // '{0}' contains code from an unknown language, preventing a
                // comprehensive analysis of the compiler warning settings.
                // The language could not be identified for the following modules: {1}
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.EnableCriticalCompilerWarnings_UnknownModuleLanguage_Fail,
                                                              unknownLanguageModules.ToString()));
            }

            if (exampleTooLowWarningCommandLine != null)
            {
                // '{0}' was compiled at too low a warning level. Warning level 3 enables
                // important static analysis in the compiler to flag bugs that can lead
                // to memory corruption, information disclosure, or double-free
                // vulnerabilities.To resolve this issue, compile at warning level 3 or
                // higher by supplying / W3, / W4, or / Wall to the compiler, and resolve
                // the warnings emitted.
                // An example compiler command line triggering this check: {1}
                // Modules triggering this check: {2}
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.EnableCriticalCompilerWarnings_InsufficientWarningLevel_Fail,
                                                              overallMinimumWarningLevel.ToString(),
                                                              exampleTooLowWarningCommandLine,
                                                              warningTooLowModules.CreateTruncatedObjectList()));
            }

            if (exampleDisabledWarningCommandLine != null)
            {
                // '{0}' disables compiler warning(s) which are required by policy. A
                // compiler warning is typically required if it has a high likelihood of
                // flagging memory corruption, information disclosure, or double-free
                // vulnerabilities. To resolve this issue, enable the indicated warning(s)
                // by removing /Wxxxx switches (where xxxx is a warning id indicated here)
                // from your command line, and resolve any warnings subsequently raised
                // during compilation.
                // An example compiler command line triggering this check was: {1}
                // Modules triggering this check were: {2}
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.EnableCriticalCompilerWarnings_WarningsDisabled_Fail,
                                                              exampleDisabledWarningCommandLine,
                                                              disabledWarningModules.CreateTruncatedObjectList()));
            }
        }
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;

            if (context.Pdb == null)
            {
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildCouldNotLoadPdbMessage(context));
                return;
            }

            Pdb di = context.Pdb;

            bool noCode = !di.CreateGlobalFunctionIterator().Any() && !di.ContainsExecutableSectionContribs();

            if (noCode)
            {
                // '{0}' is a C or C++ binary that is not required to initialize the stack protection, as it does not contain executable code.
                context.Logger.Log(MessageKind.Pass, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.InitializeStackProtection_NoCode_Pass));
                return;
            }

            bool bHasGSCheck = di.CreateGlobalFunctionIterator(
                StackProtectionUtilities.GSCheckFunctionName, NameSearchOptions.nsfCaseSensitive).Any();

            bool bHasGSInit = StackProtectionUtilities.GSInitializationFunctionNames.Any(
                functionName => di.CreateGlobalFunctionIterator(functionName,
                                                                NameSearchOptions.nsfCaseSensitive).Any());

            if (!bHasGSCheck && !bHasGSInit)
            {
                // '{0}' is a C or C++ binary that does not make use of the stack protection
                // buffer security feature. It is therefore not required to initialize the feature.
                context.Logger.Log(MessageKind.Pass, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.InitializeStackProtection_NoFeatureUse_Pass));
                return;
            }

            if (!bHasGSInit)
            {
                // '{0}' is a C or C++ binary that does not initialize the stack protector.
                // The stack protector(/ GS) is a security feature of the compiler which
                // makes it more difficult to exploit stack buffer overflow memory
                // corruption vulnerabilities. The stack protector requires access to
                // entropy in order to be effective, which means a binary must initialize
                // a random number generator at startup, by calling __security_init_cookie()
                // as close to the binary's entry point as possible. Failing to do so will
                // result in spurious buffer overflow detections on the part of the stack
                // protector. To resolve this issue, use the default entry point provided
                // by the C runtime, which will make this call for you, or call
                // __security_init_cookie() manually in your custom entry point.
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.InitializeStackProtection_Fail));
                return;
            }

            // '{0}' is a C or C++ binary built with the buffer security feature
            // that properly initializes the stack protecter. This has the
            //effect of increasing the effectiveness of the feature and reducing
            // spurious detections.
            context.Logger.Log(MessageKind.Pass, context,
                               RuleUtilities.BuildMessage(context,
                                                          RulesResources.InitializeStackProtection_Pass));
        }
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;
            Pdb      pdb      = context.Pdb;

            if (pdb == null)
            {
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildCouldNotLoadPdbMessage(context));
                return;
            }

            TruncatedCompilandRecordList noGsModules            = new TruncatedCompilandRecordList();
            TruncatedCompilandRecordList unknownLanguageModules = new TruncatedCompilandRecordList();

            foreach (DisposableEnumerableView <Symbol> omView in pdb.CreateObjectModuleIterator())
            {
                Symbol om = omView.Value;
                ObjectModuleDetails details = om.GetObjectModuleDetails();

                if (details.Language == Language.Unknown)
                {
                    // See if this module contributed to an executable section.
                    // If not, we can ignore the module.
                    if (pdb.CompilandWithIdIsInExecutableSectionContrib(om.SymIndexId))
                    {
                        unknownLanguageModules.Add(om.CreateCompilandRecord());
                    }
                    continue;
                }

                // Detection applies to C/C++ produced by MS compiler only
                if ((details.Language != Language.C) && (details.Language != Language.Cxx) ||
                    details.Compiler != "Microsoft (R) Optimizing Compiler")
                {
                    continue;
                }

                if (!details.HasSecurityChecks && om.CreateChildIterator(SymTagEnum.SymTagFunction).Any())
                {
                    noGsModules.Add(om.CreateCompilandRecord());
                }
            }

            if (unknownLanguageModules.Empty && noGsModules.Empty)
            {
                // '{0}' is a C or C++ binary built with the stack protector buffer security
                // feature enabled for all modules, making it more difficult for an attacker to
                // exploit stack buffer overflow memory corruption vulnerabilities.
                context.Logger.Log(MessageKind.Pass, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.EnableStackProtection_Pass));
                return;
            }

            if (!unknownLanguageModules.Empty)
            {
                // '{0}' contains code from unknown language, preventing a comprehensive analysis of the
                // stack protector buffer security features. The language could not be identified for
                // the following modules: {1}.
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.EnableStackProtection_UnknownModuleLanguage_Fail,
                                                              unknownLanguageModules.ToString()));
            }

            if (!noGsModules.Empty)
            {
                // '{0}' is a C or C++ binary built with the stack protector buffer security feature
                // disabled in one or more modules. The stack protector (/GS) is a security feature
                // of the compiler which makes it more difficult to exploit stack buffer overflow
                // memory corruption vulnerabilities. To resolve this issue, ensure that your code
                // is compiled with the stack protector enabled by supplying /GS on the Visual C++
                // compiler command line. The affected modules were: {1}
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.EnableStackProtection_Fail,
                                                              noGsModules.ToString()));
            }
        }
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;

            Pdb pdb = context.Pdb;

            if (pdb == null)
            {
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildCouldNotLoadPdbMessage(context));
                return;
            }

            Dictionary <string, TruncatedCompilandRecordList> vulnerabilityToModules = new Dictionary <string, TruncatedCompilandRecordList>();
            TruncatedCompilandRecordList moduleList;

            foreach (DisposableEnumerableView <Symbol> omView in pdb.CreateObjectModuleIterator())
            {
                Symbol om = omView.Value;
                ObjectModuleDetails details = om.GetObjectModuleDetails();
                if (details.Language != Language.C && details.Language != Language.Cxx)
                {
                    continue;
                }

                if (!details.HasDebugInfo)
                {
                    continue;
                }

                foreach (DisposableEnumerableView <SourceFile> sfView in pdb.CreateSourceFileIterator(om))
                {
                    SourceFile sf       = sfView.Value;
                    string     fileName = Path.GetFileName(sf.FileName);

                    if (!_files.Contains(fileName) || sf.HashType == HashType.None)
                    {
                        continue;
                    }

                    string hash = fileName + "#" + BitConverter.ToString(sf.Hash);
                    VulnerableDependencyDescriptor descriptor;

                    if (_filesToVulnerabilitiesMap.TryGetValue(hash, out descriptor))
                    {
                        if (!vulnerabilityToModules.TryGetValue(descriptor.Id, out moduleList))
                        {
                            moduleList = vulnerabilityToModules[descriptor.Id] = new TruncatedCompilandRecordList();
                        }
                        moduleList.Add(om.CreateCompilandRecordWithSuffix(hash));
                    }
                }
            }

            if (vulnerabilityToModules.Count != 0)
            {
                foreach (string id in vulnerabilityToModules.Keys)
                {
                    moduleList = vulnerabilityToModules[id];
                    VulnerableDependencyDescriptor descriptor = (VulnerableDependencyDescriptor)context.Policy.GetProperty(VulnerableDependencies)[id];

                    // '{0}' was built with a version of {1} which is subject to the following issues: {2}.
                    // To resolve this, {3}. The source files that triggered this were: {4}
                    context.Logger.Log(MessageKind.Fail, context,
                                       RuleUtilities.BuildMessage(context,
                                                                  RulesResources.DoNotIncorporateVulnerableBinaries_Fail,
                                                                  descriptor.Name,
                                                                  descriptor.VulnerabilityDescription,
                                                                  descriptor.Resolution,
                                                                  moduleList.ToString()));
                }
                return;
            }

            // '{0}' does not incorporate any known vulnerable dependencies, as configured by current policy.
            context.Logger.Log(MessageKind.Pass, context,
                               RuleUtilities.BuildMessage(context,
                                                          RulesResources.DoNotIncorporateVulnerableBinaries_Pass));
        }
Example #5
0
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;

            Pdb pdb = context.Pdb;

            if (pdb == null)
            {
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildCouldNotLoadPdbMessage(context));
                return;
            }

            Version minLinkVersion;
            Version minCompilerVersion;

            if (context.PE.IsXBox)
            {
                minCompilerVersion = context.Policy.GetProperty(MinimumToolVersions)[MIN_XBOX_COMPILER_VER];
                minLinkVersion     = context.Policy.GetProperty(MinimumToolVersions)[MIN_XBOX_LINKER_VER];
            }
            else
            {
                minCompilerVersion = context.Policy.GetProperty(MinimumToolVersions)[MIN_COMPILER_VER];
                minLinkVersion     = context.Policy.GetProperty(MinimumToolVersions)[MIN_LINKER_VER];
            }

            TruncatedCompilandRecordList badModuleList    = new TruncatedCompilandRecordList();
            StringToVersionMap           allowedLibraries = context.Policy.GetProperty(AllowedLibraries);

            foreach (DisposableEnumerableView <Symbol> omView in pdb.CreateObjectModuleIterator())
            {
                Symbol om = omView.Value;
                ObjectModuleDetails omDetails = om.GetObjectModuleDetails();

                // see if the item is in our skip list
                if (!string.IsNullOrEmpty(om.Lib))
                {
                    string  libFileName = string.Concat(System.IO.Path.GetFileName(om.Lib), ",", omDetails.Language.ToString()).ToLowerInvariant();
                    Version minAllowedVersion;

                    if (allowedLibraries.TryGetValue(libFileName, out minAllowedVersion) &&
                        omDetails.CompilerVersion >= minAllowedVersion)
                    {
                        continue;
                    }
                }

                Version  actualVersion;
                Version  minimumVersion;
                Language omLanguage = omDetails.Language;
                switch (omLanguage)
                {
                case Language.C:
                case Language.Cxx:
                    actualVersion  = Minimum(omDetails.CompilerVersion, omDetails.CompilerFrontEndVersion);
                    minimumVersion = minCompilerVersion;
                    break;

                case Language.MASM:
                    // TODO verify this
                    actualVersion  = omDetails.CompilerVersion;
                    minimumVersion = minLinkVersion;
                    break;

                case Language.LINK:
                    continue;

                default:
                    continue;
                }

                if (actualVersion < minimumVersion)
                {
                    // built with {0} compiler version {1} (Front end version: {2})
                    badModuleList.Add(
                        om.CreateCompilandRecordWithSuffix(
                            String.Format(CultureInfo.InvariantCulture,
                                          RulesResources.BuildWithSecureTools_BadModule,
                                          omLanguage, omDetails.CompilerVersion, omDetails.CompilerFrontEndVersion)));
                }
            }

            if (!badModuleList.Empty)
            {
                // '{0}' was compiled with one or more modules which were not built using minimum
                // required tool versions (compiler version {1}, linker version {2}). More recent
                // tool chains contain mitigations that make it more difficult for an attacker to
                // exploit vulnerabilities in programs they produce. To resolve this issue,
                // compile and/or link your binary with more recent tools. If you are servicing a
                // product where the tool chain cannot be modified (e.g. producing a hotfix for
                // an already shipped version) ignore this warning.
                // Modules built outside of policy: {3}
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.BuildWithSecureTools_Fail,
                                                              minCompilerVersion.ToString(),
                                                              minLinkVersion.ToString(),
                                                              badModuleList.CreateSortedObjectList()));
                return;
            }

            // '{0}' was built with a tool chain that satisfies configured policy
            // (compiler minimum version {1}, linker minimum version {2}).
            context.Logger.Log(MessageKind.Pass, context,
                               RuleUtilities.BuildMessage(context,
                                                          RulesResources.BuildWithSecureTools_Pass,
                                                          minCompilerVersion.ToString(), minLinkVersion.ToString()));
        }
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;
            Pdb      pdb      = context.Pdb;

            if (pdb == null)
            {
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildCouldNotLoadPdbMessage(context));
                return;
            }

            List <string> names = new List <string>();

            foreach (DisposableEnumerableView <Symbol> functionView in pdb.CreateGlobalFunctionIterator())
            {
                Symbol function = functionView.Value;
                if (function.IsManaged)
                {
                    continue;
                }
                if (!function.IsSafeBuffers)
                {
                    continue;
                }

                string functionName = function.GetUndecoratedName();

                if (functionName == "__security_init_cookie" ||
                    context.Policy.GetProperty(ApprovedFunctionsThatDisableStackProtection).Contains(functionName))
                {
                    continue;
                }
                names.Add(functionName);
            }

            if (names.Count != 0)
            {
                string functionNames = string.Join(";", names);

                // '{0}' is a C or C++ binary built with function(s) ({1}) that disable the stack
                // protector. The stack protector (/GS) is a security feature of the compiler
                // which makes it more difficult to exploit stack buffer overflow memory
                // corruption vulnerabilities. Disabling the stack protector, even on a
                // function -by-function basis, is disallowed by SDL policy. To resolve this
                // issue, remove occurrences of __declspec(safebuffers) from your code. If the
                // additional code inserted by the stack protector has been shown in profiling
                // to cause a significant performance problem for your application, attempt to
                // move stack buffer modifications out of the hot path of execution to allow the
                // compiler to avoid inserting stack protector checks in these locations rather
                // than disabling the stack protector altogether.
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.DoNotDisableStackProtectionForFunctions_Pass, functionNames));
                return;
            }

            // '{0}' is a C or C++ binary built with the stack protector buffer
            // security feature enabled which does not disable protection for
            // any individual functions (via __declspec(safebuffers), making it
            // more difficult for an attacker to exploit stack buffer overflow
            // memory corruption vulnerabilities.
            context.Logger.Log(MessageKind.Pass, context,
                               RuleUtilities.BuildMessage(context,
                                                          RulesResources.DoNotDisableStackProtectionForFunctions_Pass));
        }