public override void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader optionalHeader = context.PE.PEHeaders.PEHeader;

            if ((optionalHeader.DllCharacteristics & DllCharacteristics.DynamicBase) != DllCharacteristics.DynamicBase)
            {
                // '{0}' is not marked as DYNAMICBASE. This means that the binary is not eligible for relocation
                // by Address Space Layout Randomization (ASLR). ASLR is an important mitigation that makes it
                // more difficult for an attacker to exploit memory corruption vulnerabilities. To resolve this
                // issue, configure your tool chain to build with this feature enabled. For C and C++ binaries,
                // add /DYNAMICBASE to your linker command line. For .NET applications, use a compiler shipping
                // with Visual Studio 2008 or later.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultLevel.Error, context, null,
                                                             nameof(RuleResources.BA2009_Error_NotDynamicBase),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            CoffHeader coffHeader = context.PE.PEHeaders.CoffHeader;

            if ((coffHeader.Characteristics & Characteristics.RelocsStripped) == Characteristics.RelocsStripped)
            {
                // '{0}' is marked as DYNAMICBASE but relocation data has been stripped
                // from the image, preventing address space layout randomization.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultLevel.Error, context, null,
                                                             nameof(RuleResources.BA2009_Error_RelocsStripped),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            if (context.PE.Subsystem == Subsystem.WindowsCEGui)
            {
                Debug.Assert(context.PE.OSVersion >= OSVersions.WindowsCE7);

                bool relocSectionFound = false;


                // For WinCE 7+ ASLR is a machine-wide setting and binaries must
                // have relocation info present in order to be dynamically rebased.
                foreach (SectionHeader sectionHeader in context.PE.PEHeaders.SectionHeaders)
                {
                    if (sectionHeader.Name.Equals(".reloc", StringComparison.OrdinalIgnoreCase) &&
                        sectionHeader.SizeOfRawData > 0)
                    {
                        relocSectionFound = true;
                        break;
                    }
                }

                if (!relocSectionFound)
                {
                    // EnableAddressSpaceLayoutRandomization_WinCENoRelocationSection_Error	'{0}'
                    // is a Windows CE image but does not contain any relocation data, preventing
                    // address space layout randomization.
                    context.Logger.Log(this,
                                       RuleUtilities.BuildResult(ResultLevel.Error, context, null,
                                                                 nameof(RuleResources.BA2009_Error_WinCENoRelocationSection),
                                                                 context.TargetUri.GetFileName()));
                    return;
                }
            }

            //'{0}' is properly compiled to enable address space layout randomization.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultLevel.Pass, context, null,
                                                         nameof(RuleResources.BA2009_Pass),
                                                         context.TargetUri.GetFileName()));
        }
Пример #2
0
        public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context)
        {
            PEBinary target = context.PEBinary();
            Pdb      di     = target.Pdb;

            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();

                // Detection applies to C/C++ produced by MS compiler only
                if (omDetails.WellKnownCompiler != WellKnownCompilers.MicrosoftNativeCompiler)
                {
                    continue;
                }

                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 (!om.CreateChildIterator(SymTagEnum.SymTagFunction).Any())
                {
                    // uninteresting...
                    continue;
                }

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

                overallMinimumWarningLevel = Math.Min(overallMinimumWarningLevel, warningLevel);

                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);

                if (warningLevel < 3)
                {
                    exampleTooLowWarningCommandLine = exampleTooLowWarningCommandLine ?? omDetails.RawCommandLine;

                    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.RawCommandLine;

                    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 disclosure, double-free and
                // other security-related vulnerabilities do not exist in code.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                             nameof(RuleResources.BA2007_Pass),
                                                             context.TargetUri.GetFileName(),
                                                             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(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2007_Error_UnknownModuleLanguage),
                                                             context.TargetUri.GetFileName(),
                                                             unknownLanguageModules.CreateSortedObjectList()));
            }

            if (!String.IsNullOrEmpty(exampleTooLowWarningCommandLine))
            {
                // '{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(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2007_Error_InsufficientWarningLevel),
                                                             context.TargetUri.GetFileName(),
                                                             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(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2007_Error_WarningsDisabled),
                                                             context.TargetUri.GetFileName(),
                                                             exampleDisabledWarningCommandLine,
                                                             disabledWarningModules.CreateTruncatedObjectList()));
            }
        }
        public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context)
        {
            PEBinary target = context.PEBinary();
            Pdb      pdb    = target.Pdb;

            var 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 (!this.files.Contains(fileName) || sf.HashType == HashType.None)
                    {
                        continue;
                    }

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

                    if (this.filesToVulnerabilitiesMap.TryGetValue(hash, out VulnerableDependencyDescriptor 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];
                    var 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(this,
                                       RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                                 nameof(RuleResources.BA2002_Error),
                                                                 context.TargetUri.GetFileName(),
                                                                 descriptor.Name,
                                                                 descriptor.VulnerabilityDescription,
                                                                 descriptor.Resolution,
                                                                 moduleList.CreateSortedObjectList()));
                }
                return;
            }

            // '{0}' does not incorporate any known vulnerable dependencies, as configured by current policy.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                         nameof(RuleResources.BA2002_Pass),
                                                         context.TargetUri.GetFileName()));
        }
Пример #4
0
        public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context)
        {
            PEBinary target = context.PEBinary();

            Machine reflectionMachineType = target.PE.Machine;

            // The current Machine enum does not have support for Arm64, so translate to our Machine enum
            ExtendedMachine machineType = (ExtendedMachine)reflectionMachineType;

            if (!machineType.CanBeMitigated())
            {
                // QUESTION:
                // Machine HW is unsupported for mitigations...
                // should this be in the CanAnalyze() method or here and issue a warning?
                return;
            }

            Pdb pdb = target.Pdb;

            TruncatedCompilandRecordList masmModules = new TruncatedCompilandRecordList();
            TruncatedCompilandRecordList mitigationNotEnabledModules         = new TruncatedCompilandRecordList();
            TruncatedCompilandRecordList mitigationDisabledInDebugBuild      = new TruncatedCompilandRecordList();
            TruncatedCompilandRecordList mitigationExplicitlyDisabledModules = 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;
                Language omLanguage = omDetails.Language;

                // We already opted-out of IL Only binaries, so only check for native languages
                // or those that can appear in mixed binaries.
                switch (omLanguage)
                {
                case Language.C:
                case Language.Cxx:
                {
                    if (omDetails.WellKnownCompiler != WellKnownCompilers.MicrosoftNativeCompiler)
                    {
                        // TODO: https://github.com/Microsoft/binskim/issues/114
                        continue;
                    }
                    else
                    {
                        actualVersion = omDetails.CompilerVersion;
                    }
                    break;
                }

                case Language.MASM:
                {
                    masmModules.Add(om.CreateCompilandRecord());
                    continue;
                }

                case Language.LINK:
                {
                    // Linker is not involved in the mitigations, so no need to check version or switches at this time.
                    continue;
                }

                case Language.CVTRES:
                {
                    // Resource compiler is not involved in the mitigations, so no need to check version or switches at this time.
                    continue;
                }

                case Language.HLSL:
                {
                    // HLSL compiler is not involved in the mitigations, so no need to check version or switches at this time.
                    continue;
                }

                // Mixed binaries (/clr) can contain non C++ compilands if they are linked in via netmodules
                // .NET IL should be mitigated by the runtime if any mitigations are necessary
                // At this point simply accept them as safe until this is disproven.
                case Language.CSharp:
                case Language.MSIL:
                case Language.ILASM:
                {
                    continue;
                }

                case Language.Unknown:
                {
                    // The linker may emit debug information for modules from static libraries that do not contribute to actual code.
                    // do not contribute to actual code. Currently these come back as Language.Unknown :(
                    // TODO: https://github.com/Microsoft/binskim/issues/116
                    continue;
                }

                default:
                {
                    // TODO: https://github.com/Microsoft/binskim/issues/117
                    // Review unknown languages for this and all checks
                }
                    continue;
                }

                // Get the appropriate compiler version against which to check this compiland.
                // check that we are greater than or equal to the first fully supported release: 15.6 first
                Version omVersion = omDetails.CompilerVersion;

                CompilerMitigations availableMitigations = GetAvailableMitigations(context, machineType, omVersion);

                if (availableMitigations == CompilerMitigations.None)
                {
                    // Built with a compiler version {0} that does not support any Spectre
                    // mitigations. We do not report here. BA2006 will fire instead.
                    continue;
                }
                string[] mitigationSwitches = new string[] { "/Qspectre", "/guardspecload" };

                SwitchState effectiveState;

                // Go process the command line to check for switches
                effectiveState = omDetails.GetSwitchState(mitigationSwitches, null, SwitchState.SwitchDisabled, OrderOfPrecedence.LastWins);

                if (effectiveState == SwitchState.SwitchDisabled)
                {
                    SwitchState QSpectreState        = SwitchState.SwitchNotFound;
                    SwitchState d2guardspecloadState = SwitchState.SwitchNotFound;

                    if (availableMitigations.HasFlag(CompilerMitigations.QSpectreAvailable))
                    {
                        QSpectreState = omDetails.GetSwitchState(mitigationSwitches[0] /*"/Qspectre"*/, OrderOfPrecedence.LastWins);
                    }

                    if (availableMitigations.HasFlag(CompilerMitigations.D2GuardSpecLoadAvailable))
                    {
                        // /d2xxxx options show up in the PDB without the d2 string
                        // So search for just /guardspecload
                        d2guardspecloadState = omDetails.GetSwitchState(mitigationSwitches[1] /*"/guardspecload"*/, OrderOfPrecedence.LastWins);
                    }

                    // TODO: https://github.com/Microsoft/binskim/issues/119
                    // We should flag cases where /d2guardspecload is enabled but the
                    // toolset supports /qSpectre (which should be preferred).

                    if (QSpectreState == SwitchState.SwitchNotFound && d2guardspecloadState == SwitchState.SwitchNotFound)
                    {
                        // Built with tools that support the Spectre mitigations but these have not been enabled.
                        mitigationNotEnabledModules.Add(om.CreateCompilandRecord());
                    }
                    else
                    {
                        // Built with the Spectre mitigations explicitly disabled.
                        mitigationExplicitlyDisabledModules.Add(om.CreateCompilandRecord());
                    }

                    continue;
                }

                if (!availableMitigations.HasFlag(CompilerMitigations.NonoptimizedCodeMitigated))
                {
                    string[] OdSwitches = { "/Od" };
                    // These switches override /Od - there is no one place to find this information on msdn at this time.
                    string[] OptimizeSwitches = { "/O1", "/O2", "/Ox", "/Og" };

                    bool debugEnabled = false;

                    if (omDetails.GetSwitchState(OdSwitches, OptimizeSwitches, SwitchState.SwitchEnabled, OrderOfPrecedence.LastWins) == SwitchState.SwitchEnabled)
                    {
                        debugEnabled = true;
                    }

                    if (debugEnabled)
                    {
                        // Built with /Od which disables Spectre mitigations.
                        mitigationDisabledInDebugBuild.Add(om.CreateCompilandRecord());
                        continue;
                    }
                }
            }

            string line;
            var    sb = new StringBuilder();

            if (!mitigationExplicitlyDisabledModules.Empty)
            {
                // The following modules were compiled with Spectre
                // mitigations explicitly disabled: {0}
                line = string.Format(
                    RuleResources.BA2024_Error_SpectreMitigationExplicitlyDisabled,
                    mitigationExplicitlyDisabledModules.CreateSortedObjectList());
                sb.AppendLine(line);
            }

            if (!mitigationNotEnabledModules.Empty)
            {
                // The following modules were compiled with a toolset that supports
                // /Qspectre but the switch was not enabled on the command-line: {0}
                line = string.Format(
                    RuleResources.BA2024_Error_SpectreMitigationNotEnabled,
                    mitigationNotEnabledModules.CreateSortedObjectList());
                sb.AppendLine(line);
            }

            if (!mitigationDisabledInDebugBuild.Empty)
            {
                // The following modules were compiled with optimizations disabled(/ Od),
                // a condition that disables Spectre mitigations: {0}
                line = string.Format(
                    RuleResources.BA2024_Error_OptimizationsDisabled,
                    mitigationDisabledInDebugBuild.CreateSortedObjectList());
                sb.AppendLine(line);
            }

            if ((context.Policy.GetProperty(Reporting) & ReportingOptions.WarnIfMasmModulesPresent) == ReportingOptions.WarnIfMasmModulesPresent &&
                !masmModules.Empty)
            {
                line = string.Format(
                    RuleResources.BA2024_Error_MasmModulesDetected,
                    masmModules.CreateSortedObjectList());
                sb.AppendLine(line);
            }

            if (sb.Length > 0)
            {
                // '{0}' was compiled with one or more modules that do not properly enable code
                // generation mitigations for speculative execution side-channel attack (Spectre)
                // vulnerabilities. Spectre attacks can compromise hardware-based isolation,
                // allowing non-privileged users to retrieve potentially sensitive data from the
                // CPU cache. To resolve the issue, provide the /Qspectre switch on the compiler
                // command-line (or /d2guardspecload in cases where your compiler supports this
                // switch and it is not possible to update to a toolset that supports /Qspectre).
                // The following modules are out of policy: {1}
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultLevel.Error, context, null,
                                                             nameof(RuleResources.BA2024_Error),
                                                             context.TargetUri.GetFileName(),
                                                             sb.ToString()));
                return;
            }

            // All linked modules ‘{0}’ were compiled with mitigations enabled that help prevent Spectre (speculative execution side-channel attack) vulnerabilities.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultLevel.Pass, context, null,
                                                         nameof(RuleResources.BA2024_Pass),
                                                         context.TargetUri.GetFileName()));
        }
        public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context)
        {
            PEBinary target = context.PEBinary();
            Pdb      di     = target.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(this,
                                   RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                             nameof(RuleResources.BA2013_Pass_NoCode),
                                                             context.TargetUri.GetFileName()));
                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 enable the stack protection buffer
                // security feature. It is therefore not required to initialize the stack protector.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultKind.NotApplicable, context, null,
                                                             nameof(RuleResources.BA2013_NotApplicable_FeatureNotEnabled),
                                                             context.TargetUri.GetFileName()));
                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(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2013_Error),
                                                             context.TargetUri.GetFileName()));
                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(this,
                               RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                         nameof(RuleResources.BA2013_Pass),
                                                         context.TargetUri.GetFileName()));
        }
Пример #6
0
        public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context)
        {
            PEBinary target = context.PEBinary();
            Pdb      pdb    = target.Pdb;

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

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

                // Detection applies to C/C++ produced by MS compiler only
                if (omDetails.WellKnownCompiler != WellKnownCompilers.MicrosoftC &&
                    omDetails.WellKnownCompiler != WellKnownCompilers.MicrosoftCxx)
                {
                    continue;
                }

                if (omDetails.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;
                }

                if (!omDetails.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(this,
                                   RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                             nameof(RuleResources.BA2011_Pass),
                                                             context.TargetUri.GetFileName()));
                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(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2011_Error_UnknownModuleLanguage),
                                                             context.TargetUri.GetFileName(),
                                                             unknownLanguageModules.CreateSortedObjectList()));
            }

            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(this,
                                   RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                             nameof(RuleResources.BA2011_Error),
                                                             context.TargetUri.GetFileName(),
                                                             noGsModules.ToString()));
            }
        }
Пример #7
0
        public void BuildResult_BuildsExpectedResult()
        {
            // Arrange
            const string RuleMessageId = "Default";
            const string RuleId        = "TST0001";

            string[] Arguments = new string[] { "42", "54" };

            var context = new TestAnalysisContext
            {
                TargetUri = new System.Uri("file:///c:/src/file.c"),
                Rule      = new ReportingDescriptor
                {
                    Id             = RuleId,
                    MessageStrings = new Dictionary <string, MultiformatMessageString>
                    {
                        [RuleMessageId] = new MultiformatMessageString {
                            Text = "Expected {0} but got {1}."
                        }
                    }
                }
            };

            var region = new Region
            {
                StartLine = 42
            };

            (context.RuntimeErrors & RuntimeConditions.OneOrMoreWarningsFired).Should().Be(RuntimeConditions.None);
            (context.RuntimeErrors & RuntimeConditions.OneOrMoreErrorsFired).Should().Be(RuntimeConditions.None);

            // Act.
            Result result = RuleUtilities.BuildResult(
                FailureLevel.Error,
                context,
                region,
                RuleMessageId,
                Arguments);

            // Assert.
            result.RuleId.Should().Be(RuleId);

            result.Message.Id.Should().Be(RuleMessageId);

            result.Message.Arguments.Count.Should().Be(Arguments.Length);
            result.Message.Arguments[0].Should().Be(Arguments[0]);
            result.Message.Arguments[1].Should().Be(Arguments[1]);

            result.Locations.Count.Should().Be(1);
            result.Locations[0].PhysicalLocation.Region.ValueEquals(region).Should().BeTrue();

            (context.RuntimeErrors & RuntimeConditions.OneOrMoreWarningsFired).Should().Be(RuntimeConditions.None);
            (context.RuntimeErrors & RuntimeConditions.OneOrMoreErrorsFired).Should().Be(RuntimeConditions.OneOrMoreErrorsFired);

            result = RuleUtilities.BuildResult(
                FailureLevel.Warning,
                context,
                region,
                RuleMessageId,
                Arguments);

            (context.RuntimeErrors & RuntimeConditions.OneOrMoreWarningsFired).Should().Be(RuntimeConditions.OneOrMoreWarningsFired);
        }
Пример #8
0
        public override void Analyze(BinaryAnalyzerContext context)
        {
            PEBinary           target             = context.PEBinary();
            PEHeader           peHeader           = target.PE.PEHeaders.PEHeader;
            DllCharacteristics dllCharacteristics = peHeader.DllCharacteristics;

            CoffHeader      coffHeader      = target.PE.PEHeaders.CoffHeader;
            Characteristics characteristics = coffHeader.Characteristics;

            bool highEntropyVA = ((int)dllCharacteristics & 0x0020 /*IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA*/) == 0x0020;

            //  /LARGEADDRESSAWARE is necessary for HIGH_ENTROPY_VA to have effect
            bool largeAddressAware = (characteristics & Characteristics.LargeAddressAware /*IMAGE_FILE_LARGE_ADDRESS_AWARE*/) == Characteristics.LargeAddressAware;

            if (!highEntropyVA && !largeAddressAware)
            {
                // '{0}' does not declare itself as high entropy ASLR compatible. High entropy allows
                // Address Space Layout Randomization to be more effective in mitigating memory
                // corruption vulnerabilities. To resolve this issue, configure your tool chain to
                // mark the program high entropy compatible; e.g. by supplying /HIGHENTROPYVA as well
                // as /LARGEADDRESSAWARE to the C or C++ linker command line.
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultLevel.Error, context, null,
                                                             nameof(RuleResources.BA2015_Error_NeitherHighEntropyVANorLargeAddressAware),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            if (!highEntropyVA)
            {
                // '{0}' does not declare itself as high entropy ASLR compatible. High entropy allows
                // Address Space Layout Randomization to be more effective in mitigating memory
                // corruption vulnerabilities. To resolve this issue, configure your tool chain to
                // mark the program high entropy compatible; e.g. by supplying /HIGHENTROPYVA to the
                // C or C++ linker command line. (This image was determined to have been properly
                // compiled as /LARGEADDRESSAWARE.)
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultLevel.Error, context, null,
                                                             nameof(RuleResources.BA2015_Error_NoHighEntropyVA),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            if (!largeAddressAware)
            {
                // '{0}' does not declare itself as high entropy ASLR compatible. High entropy allows
                // Address Space Layout Randomization to be more effective in mitigating memory
                // corruption vulnerabilities. To resolve this issue, configure your tool chain to
                // mark the program high entropy compatible by supplying /LARGEADDRESSAWARE to the C
                // or C++ linker command line. (This image was determined to have been properly
                // compiled as /HIGHENTROPYVA.)
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultLevel.Error, context, null,
                                                             nameof(RuleResources.BA2015_Error_NoLargeAddressAware),
                                                             context.TargetUri.GetFileName()));
                return;
            }

            //'{0}' is high entropy ASLR compatible.
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultLevel.Pass, context, null,
                                                         nameof(RuleResources.BA2015_Pass),
                                                         context.TargetUri.GetFileName()));
        }
        private bool Validate64BitImage(BinaryAnalyzerContext context)
        {
            PEHeader    peHeader     = context.PE.PEHeaders.PEHeader;
            SafePointer sp           = new SafePointer(context.PE.ImageBytes, peHeader.LoadConfigTableDirectory.RelativeVirtualAddress);
            SafePointer loadConfigVA = context.PE.RVA2VA(sp);
            ImageLoadConfigDirectory64 loadConfig = new ImageLoadConfigDirectory64(peHeader, loadConfigVA);

            UInt64 cookieVA    = (UInt64)loadConfig.GetField(ImageLoadConfigDirectory64.Fields.SecurityCookie);
            UInt64 baseAddress = peHeader.ImageBase;

            // we need to find the offset in the file based on the cookie's VA
            UInt64        sectionSize, sectionVA = 0;
            SectionHeader ish = new SectionHeader();
            bool          foundCookieSection = false;

            foreach (SectionHeader t in context.PE.PEHeaders.SectionHeaders)
            {
                sectionVA   = (UInt64)(UInt32)t.VirtualAddress + baseAddress;
                sectionSize = (UInt32)t.VirtualSize;
                if ((cookieVA >= sectionVA) &&
                    (cookieVA < sectionVA + sectionSize))
                {
                    ish = t;
                    foundCookieSection = true;
                    break;
                }
            }

            if (!foundCookieSection)
            {
                // '{0}' is a C or C++binary that enables the stack protection feature but the security cookie could not be located. The binary may be corrupted.
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.DoNotModifyStackProtectionCookie_CouldNotLocateCookie_Fail));
                return(false);
            }

            UInt64      fileCookieOffset = (cookieVA - baseAddress) - (sectionVA - baseAddress) + (UInt32)ish.PointerToRawData;
            SafePointer fileCookiePtr    = loadConfigVA;

            fileCookiePtr.Address = (int)fileCookieOffset;
            UInt64 cookie = BitConverter.ToUInt64(fileCookiePtr.GetBytes(8), 0);

            if (cookie != StackProtectionUtilities.DefaultCookieX64)
            {
                // '{0}' is a C or C++ binary that interferes with 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 relies on a random number, called
                // the "security cookie", to detect these buffer overflows. This 'cookie'
                // is statically linked with your binary from a Visual C++ library in the
                // form of the symbol __security_cookie. On recent Windows versions, the
                // loader looks for the magic statically linked value of this cookie, and
                // initializes the cookie with a far better source of entropy -- the system's
                // secure random number generator -- rather than the limited random number
                // generator available early in the C runtime startup code. When this symbol
                // is not the default value, the additional entropy is not injected by the
                // operating system, reducing the effectiveness of the stack protector. To
                // resolve this issue, ensure that your code does not reference or create a
                // symbol named __security_cookie or __security_cookie_complement. NOTE:
                // the modified cookie value detected was: {1}
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.DoNotModifyStackProtectionCookie_Fail, cookie.ToString("x")));
                return(false);
            }
            return(true);
        }
        public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context)
        {
            PEBinary target = context.PEBinary();
            Pdb      pdb    = target.Pdb;

            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(this,
                                   RuleUtilities.BuildResult(ResultLevel.Error, context, null,
                                                             nameof(RuleResources.BA2014_Error),
                                                             context.TargetUri.GetFileName(),
                                                             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(this,
                               RuleUtilities.BuildResult(ResultLevel.Pass, context, null,
                                                         nameof(RuleResources.BA2014_Pass),
                                                         context.TargetUri.GetFileName()));
        }
        public override void AnalyzePortableExecutableAndPdb(BinaryAnalyzerContext context)
        {
            PEBinary target = context.PEBinary();
            Pdb      pdb    = target.Pdb;

            Version minCompilerVersion;

            minCompilerVersion = (target.PE.IsXBox)
                ?  context.Policy.GetProperty(MinimumToolVersions)[MIN_XBOX_COMPILER_VER]
                : context.Policy.GetProperty(MinimumToolVersions)[MIN_COMPILER_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();

                if (omDetails.WellKnownCompiler != WellKnownCompilers.MicrosoftNativeCompiler)
                {
                    continue;
                }

                // 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;

                default:
                    continue;
                }

                bool foundIssue = actualVersion < minimumVersion;

                AdvancedMitigations advancedMitigations = context.Policy.GetProperty(AdvancedMitigationsEnforced);
                if (!foundIssue &&
                    (advancedMitigations & AdvancedMitigations.Spectre) == AdvancedMitigations.Spectre)
                {
                    ExtendedMachine machineType = (ExtendedMachine)target.PE.Machine;

                    // Current toolchain is within the version range to validate.
                    // Now we'll retrieve relevant compiler mitigation details to
                    // ensure this object module's build and revision meet
                    // expectations.
                    CompilerMitigations newMitigationData =
                        EnableSpectreMitigations.GetAvailableMitigations(context, machineType, actualVersion);

                    // Current compiler version does not support Spectre mitigations.
                    foundIssue = !newMitigationData.HasFlag(CompilerMitigations.D2GuardSpecLoadAvailable) &&
                                 !newMitigationData.HasFlag(CompilerMitigations.QSpectreAvailable);

                    if (foundIssue)
                    {
                        // Get the closest compiler version that has mitigations--i.e. if the user is using a 19.0 (VS2015) compiler, we should be recommending an upgrade to the
                        // 19.0 version that has the mitigations, not an upgrade to a 19.10+ (VS2017) compiler.
                        // Limitation--if there are multiple 'upgrade to' versions to recommend, this just going to give users the last one we see in the error.
                        minCompilerVersion = EnableSpectreMitigations.GetClosestCompilerVersionWithSpectreMitigations(context, machineType, actualVersion);

                        // Indicates Spectre mitigations are not supported on this platform.  We won't flag this case.
                        if (minCompilerVersion == null)
                        {
                            foundIssue = false;
                        }
                    }
                }

                if (foundIssue)
                {
                    // built with {0} compiler version {1} (Front end version: {2})
                    badModuleList.Add(
                        om.CreateCompilandRecordWithSuffix(
                            String.Format(CultureInfo.InvariantCulture,
                                          RuleResources.BA2006_Error_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}). More recent toolchains
                // 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: {2}
                context.Logger.Log(this,
                                   RuleUtilities.BuildResult(ResultLevel.Error, context, null,
                                                             nameof(RuleResources.BA2006_Error),
                                                             context.TargetUri.GetFileName(),
                                                             minCompilerVersion.ToString(),
                                                             badModuleList.CreateSortedObjectList()));
                return;
            }

            // All linked modules of '{0}' generated by the Microsoft front-end
            // satisfy configured policy (compiler minimum version {1}).
            context.Logger.Log(this,
                               RuleUtilities.BuildResult(ResultLevel.Pass, context, null,
                                                         nameof(RuleResources.BA2006_Pass),
                                                         context.TargetUri.GetFileName(),
                                                         minCompilerVersion.ToString()));
        }
Пример #12
0
 private Dictionary <string, string> InitializeRichMessageStrings()
 {
     return(RuleUtilities.BuildDictionary(ResourceManager, RichMessageResourceNames, ruleId: Id, prefix: "Rich"));
 }
Пример #13
0
 private Dictionary <string, string> InitializeMessageStrings()
 {
     return(RuleUtilities.BuildDictionary(ResourceManager, MessageResourceNames, ruleId: Id));
 }
Пример #14
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.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 hashAlgorithm, hashEncryptionAlgorithm;
                winTrustData = InitializeWinTrustDataStruct(filePath, WinTrustDataKind.EnforcePolicy, i);

                cryptoError = (CryptoError)Native.WinVerifyTrust(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.WinVerifyTrust(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(ResultLevel.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(ResultLevel.Error, context, null,
                                                                   nameof(RuleResources.BA2022_Error_BadSigningAlgorithm),
                                                                   context.TargetUri.GetFileName(),
                                                                   algorithmsText));
            }

            return(goodAlgorithms.Count > 0);
        }
Пример #15
0
 private Dictionary <string, MultiformatMessageString> InitializeMultiformatMessageStrings()
 {
     return((ResourceManager == null) ? null
         : RuleUtilities.BuildDictionary(ResourceManager, MessageResourceNames, ruleId: Id));
 }
Пример #16
0
        public void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;

            /* IMAGE_DLLCHARACTERISTICS_NO_SEH */
            if ((peHeader.DllCharacteristics & DllCharacteristics.NoSeh) == DllCharacteristics.NoSeh)
            {
                // '{0}' is an x86 binary that does not use SEH, making it an invalid
                // target for exploits that attempt to replace SEH jump targets with
                // attacker-controlled shellcode.
                context.Logger.Log(MessageKind.Pass, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.EnableSafeSEH_NoSEH_Pass));
                return;
            }

            // This will not raise false positives for non-C and C++ code, because the above
            // check for IMAGE_DLLCHARACTERISTICS_NO_SEH excludes things that don't actually
            // handle SEH exceptions like .NET ngen'd code.
            if (peHeader.LoadConfigTableDirectory.RelativeVirtualAddress == 0)
            {
                // '{0}' is an x86 binary which does not contain a load configuration table,
                // indicating that it does not enable the SafeSEH mitigation. SafeSEH makes
                // it more difficult to exploit memory corruption vulnerabilities that can
                // overwrite SEH control blocks on the stack, by verifying that the location
                // to which a thrown SEH exception would jump is indeed defined as an
                // exception handler in the source program (and not shellcode). To resolve
                // this issue, supply the /SafeSEH flag on the linker command line. Note
                // that you will need to configure your build system to supply this flag for
                // x86 builds only, as the /SafeSEH flag is invalid when linking for ARM and x64.
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.EnableSafeSEH_NoLoadConfigurationTable_Fail));
                return;
            }

            SafePointer sp           = new SafePointer(context.PE.ImageBytes, peHeader.LoadConfigTableDirectory.RelativeVirtualAddress);
            SafePointer loadConfigVA = context.PE.RVA2VA(sp);
            ImageLoadConfigDirectory32 loadConfig = new ImageLoadConfigDirectory32(peHeader, loadConfigVA);

            Int32 seHandlerSize = (Int32)loadConfig.GetField(ImageLoadConfigDirectory32.Fields.Size);

            if (seHandlerSize < 72)
            {
                // contains an unexpectedly small load configuration table {size 0}
                string seHandlerSizeText = String.Format(RulesResources.EnableSafeSEH_LoadConfigurationIsTooSmall_Fail, seHandlerSize.ToString());

                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.EnableSafeSEH_Formatted_Fail,
                                                              RulesResources.EnableSafeSEH_LoadConfigurationIsTooSmall_Fail,
                                                              seHandlerSizeText));
                return;
            }

            UInt32 seHandlerTable = (UInt32)loadConfig.GetField(ImageLoadConfigDirectory32.Fields.SEHandlerTable);
            UInt32 seHandlerCount = (UInt32)loadConfig.GetField(ImageLoadConfigDirectory32.Fields.SEHandlerCount);

            if (seHandlerTable == 0 || seHandlerCount == 0)
            {
                string failureKind = null;
                if (seHandlerTable == 0)
                {
                    // has an empty SE handler table in the load configuration table
                    failureKind = RulesResources.EnableSafeSEH_EmptySEHandlerTable_Fail;
                }
                else if (seHandlerCount == 0)
                {
                    // has zero SE handlers in the load configuration table
                    failureKind = RulesResources.EnableSafeSEH_ZeroCountSEHandlers_Fail;
                }

                // '{0}' is an x86 binary which {1}, indicating that it does not enable the SafeSEH
                // mitigation. SafeSEH makes it more difficult to exploit memory corruption
                // vulnerabilities that can overwrite SEH control blocks on the stack, by verifying
                // that the location to which a thrown SEH exception would jump is indeed defined
                // as an exception handler in the source program (and not shellcode). To resolve
                // this issue, supply the /SafeSEH flag on the linker command line. Note that you
                // will need to configure your build system to supply this flag for x86 builds only,
                // as the /SafeSEH flag is invalid when linking for ARM and x64.
                context.Logger.Log(MessageKind.Fail, context,
                                   RuleUtilities.BuildMessage(context,
                                                              RulesResources.EnableSafeSEH_Formatted_Fail, failureKind));
                return;
            }

            // ''{0}' is an x86 binary that enables SafeSEH, a mitigation that verifies SEH exception
            // jump targets are defined as exception handlers in the program (and not shellcode).
            context.Logger.Log(MessageKind.Pass, context,
                               RuleUtilities.BuildMessage(context,
                                                          RulesResources.EnableSafeSEH_SafeSEHEnabled_Pass));
        }
Пример #17
0
 public override void Analyze(AnalyzeTestContext context)
 {
     context.Logger.Log(context.Rule, RuleUtilities.BuildResult(FailureLevel.Error, context, null,
                                                                nameof(MultitoolResources.AT1001_Error_FiredAnError),
                                                                context.TargetUri.GetFileName()));
 }
Пример #18
0
        public override void Analyze(BinaryAnalyzerContext context)
        {
            PEHeader peHeader = context.PE.PEHeaders.PEHeader;

            Pdb pdb = context.Pdb;

            if (pdb == null)
            {
                Errors.LogExceptionLoadingPdb(context, context.PdbParseException.Message);
                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,
                                          RuleResources.BA2006_Fail_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(this,
                                   RuleUtilities.BuildResult(ResultKind.Error, context, null,
                                                             nameof(RuleResources.BA2006_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(this,
                               RuleUtilities.BuildResult(ResultKind.Pass, context, null,
                                                         nameof(RuleResources.BA2006_Pass),
                                                         minCompilerVersion.ToString(), minLinkVersion.ToString()));
        }
Пример #19
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);
        }