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())); }
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())); }
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())); }
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())); } }
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); }
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())); }
private Dictionary <string, string> InitializeRichMessageStrings() { return(RuleUtilities.BuildDictionary(ResourceManager, RichMessageResourceNames, ruleId: Id, prefix: "Rich")); }
private Dictionary <string, string> InitializeMessageStrings() { return(RuleUtilities.BuildDictionary(ResourceManager, MessageResourceNames, ruleId: Id)); }
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); }
private Dictionary <string, MultiformatMessageString> InitializeMultiformatMessageStrings() { return((ResourceManager == null) ? null : RuleUtilities.BuildDictionary(ResourceManager, MessageResourceNames, ruleId: Id)); }
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)); }
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())); }
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())); }
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); }