public void Analyze(BinaryAnalyzerContext context) { PEHeader peHeader = context.PE.PEHeaders.PEHeader; Pdb di = context.Pdb; if (di == null) { context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildCouldNotLoadPdbMessage(context)); return; } TruncatedCompilandRecordList warningTooLowModules = new TruncatedCompilandRecordList(); TruncatedCompilandRecordList disabledWarningModules = new TruncatedCompilandRecordList(); TruncatedCompilandRecordList unknownLanguageModules = new TruncatedCompilandRecordList(); TruncatedCompilandRecordList allWarningLevelLowModules = new TruncatedCompilandRecordList(); string exampleTooLowWarningCommandLine = null; int overallMinimumWarningLevel = Int32.MaxValue; string exampleDisabledWarningCommandLine = null; List <int> overallDisabledWarnings = new List <int>(); foreach (DisposableEnumerableView <Symbol> omView in di.CreateObjectModuleIterator()) { Symbol om = omView.Value; ObjectModuleDetails omDetails = om.GetObjectModuleDetails(); if (omDetails.Language == Language.Unknown) { // See if this module contributed to an executable section. If not, we can ignore the module. if (di.CompilandWithIdIsInExecutableSectionContrib(om.SymIndexId)) { unknownLanguageModules.Add(om.CreateCompilandRecord()); } continue; } if ((omDetails.Language != Language.C) && (omDetails.Language != Language.Cxx)) { continue; } if (omDetails.Compiler != "Microsoft (R) Optimizing Compiler") { continue; } if (!om.CreateChildIterator(SymTagEnum.SymTagFunction).Any()) { // uninteresting... continue; } int warningLevel = omDetails.WarningLevel; List <int> requiredDisabledWarnings = omDetails.ExplicitlyDisabledWarnings .Where(context.Policy.GetProperty(RequiredCompilerWarnings).Contains).ToList(); if (warningLevel >= 3 && requiredDisabledWarnings.Count == 0) { // We duplicate this condition to bail out early and avoid writing the // module description or newline into sbBadWarningModules if everything // in the module is OK. continue; } List <string> suffix = new List <string>(2); overallMinimumWarningLevel = Math.Min(overallMinimumWarningLevel, warningLevel); if (warningLevel < 3) { exampleTooLowWarningCommandLine = exampleTooLowWarningCommandLine ?? omDetails.CommandLine; string msg = "[warning level: " + warningLevel.ToString(CultureInfo.InvariantCulture) + "]"; warningTooLowModules.Add(om.CreateCompilandRecordWithSuffix(msg)); suffix.Add(msg); } if (requiredDisabledWarnings.Count != 0) { MergeInto(overallDisabledWarnings, requiredDisabledWarnings); exampleDisabledWarningCommandLine = exampleDisabledWarningCommandLine ?? omDetails.CommandLine; string msg = "[Explicitly disabled warnings: " + CreateTextWarningList(requiredDisabledWarnings) + "]"; disabledWarningModules.Add(om.CreateCompilandRecordWithSuffix(msg)); suffix.Add(msg); } allWarningLevelLowModules.Add(om.CreateCompilandRecordWithSuffix(String.Join(" ", suffix))); } if (unknownLanguageModules.Empty && exampleTooLowWarningCommandLine == null && exampleDisabledWarningCommandLine == null) { // '{0}' was compiled at a secure warning level ({1}) and does not // include any modules that disable specific warnings which are // required by policy. As a result, there is a greater likelihood // that memory corruption, information disclosre, double-free and // other security-related vulnerabilities do not exist in code. context.Logger.Log(MessageKind.Pass, context, RuleUtilities.BuildMessage(context, RulesResources.EnableCriticalCompilerWarnings_Pass, overallMinimumWarningLevel.ToString())); return; } if (!unknownLanguageModules.Empty) { // '{0}' contains code from an unknown language, preventing a // comprehensive analysis of the compiler warning settings. // The language could not be identified for the following modules: {1} context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildMessage(context, RulesResources.EnableCriticalCompilerWarnings_UnknownModuleLanguage_Fail, unknownLanguageModules.ToString())); } if (exampleTooLowWarningCommandLine != null) { // '{0}' was compiled at too low a warning level. Warning level 3 enables // important static analysis in the compiler to flag bugs that can lead // to memory corruption, information disclosure, or double-free // vulnerabilities.To resolve this issue, compile at warning level 3 or // higher by supplying / W3, / W4, or / Wall to the compiler, and resolve // the warnings emitted. // An example compiler command line triggering this check: {1} // Modules triggering this check: {2} context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildMessage(context, RulesResources.EnableCriticalCompilerWarnings_InsufficientWarningLevel_Fail, overallMinimumWarningLevel.ToString(), exampleTooLowWarningCommandLine, warningTooLowModules.CreateTruncatedObjectList())); } if (exampleDisabledWarningCommandLine != null) { // '{0}' disables compiler warning(s) which are required by policy. A // compiler warning is typically required if it has a high likelihood of // flagging memory corruption, information disclosure, or double-free // vulnerabilities. To resolve this issue, enable the indicated warning(s) // by removing /Wxxxx switches (where xxxx is a warning id indicated here) // from your command line, and resolve any warnings subsequently raised // during compilation. // An example compiler command line triggering this check was: {1} // Modules triggering this check were: {2} context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildMessage(context, RulesResources.EnableCriticalCompilerWarnings_WarningsDisabled_Fail, exampleDisabledWarningCommandLine, disabledWarningModules.CreateTruncatedObjectList())); } }
public void Analyze(BinaryAnalyzerContext context) { PEHeader peHeader = context.PE.PEHeaders.PEHeader; if (context.Pdb == null) { context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildCouldNotLoadPdbMessage(context)); return; } Pdb di = context.Pdb; bool noCode = !di.CreateGlobalFunctionIterator().Any() && !di.ContainsExecutableSectionContribs(); if (noCode) { // '{0}' is a C or C++ binary that is not required to initialize the stack protection, as it does not contain executable code. context.Logger.Log(MessageKind.Pass, context, RuleUtilities.BuildMessage(context, RulesResources.InitializeStackProtection_NoCode_Pass)); return; } bool bHasGSCheck = di.CreateGlobalFunctionIterator( StackProtectionUtilities.GSCheckFunctionName, NameSearchOptions.nsfCaseSensitive).Any(); bool bHasGSInit = StackProtectionUtilities.GSInitializationFunctionNames.Any( functionName => di.CreateGlobalFunctionIterator(functionName, NameSearchOptions.nsfCaseSensitive).Any()); if (!bHasGSCheck && !bHasGSInit) { // '{0}' is a C or C++ binary that does not make use of the stack protection // buffer security feature. It is therefore not required to initialize the feature. context.Logger.Log(MessageKind.Pass, context, RuleUtilities.BuildMessage(context, RulesResources.InitializeStackProtection_NoFeatureUse_Pass)); return; } if (!bHasGSInit) { // '{0}' is a C or C++ binary that does not initialize the stack protector. // The stack protector(/ GS) is a security feature of the compiler which // makes it more difficult to exploit stack buffer overflow memory // corruption vulnerabilities. The stack protector requires access to // entropy in order to be effective, which means a binary must initialize // a random number generator at startup, by calling __security_init_cookie() // as close to the binary's entry point as possible. Failing to do so will // result in spurious buffer overflow detections on the part of the stack // protector. To resolve this issue, use the default entry point provided // by the C runtime, which will make this call for you, or call // __security_init_cookie() manually in your custom entry point. context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildMessage(context, RulesResources.InitializeStackProtection_Fail)); return; } // '{0}' is a C or C++ binary built with the buffer security feature // that properly initializes the stack protecter. This has the //effect of increasing the effectiveness of the feature and reducing // spurious detections. context.Logger.Log(MessageKind.Pass, context, RuleUtilities.BuildMessage(context, RulesResources.InitializeStackProtection_Pass)); }
public void Analyze(BinaryAnalyzerContext context) { PEHeader peHeader = context.PE.PEHeaders.PEHeader; Pdb pdb = context.Pdb; if (pdb == null) { context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildCouldNotLoadPdbMessage(context)); return; } TruncatedCompilandRecordList noGsModules = new TruncatedCompilandRecordList(); TruncatedCompilandRecordList unknownLanguageModules = new TruncatedCompilandRecordList(); foreach (DisposableEnumerableView <Symbol> omView in pdb.CreateObjectModuleIterator()) { Symbol om = omView.Value; ObjectModuleDetails details = om.GetObjectModuleDetails(); if (details.Language == Language.Unknown) { // See if this module contributed to an executable section. // If not, we can ignore the module. if (pdb.CompilandWithIdIsInExecutableSectionContrib(om.SymIndexId)) { unknownLanguageModules.Add(om.CreateCompilandRecord()); } continue; } // Detection applies to C/C++ produced by MS compiler only if ((details.Language != Language.C) && (details.Language != Language.Cxx) || details.Compiler != "Microsoft (R) Optimizing Compiler") { continue; } if (!details.HasSecurityChecks && om.CreateChildIterator(SymTagEnum.SymTagFunction).Any()) { noGsModules.Add(om.CreateCompilandRecord()); } } if (unknownLanguageModules.Empty && noGsModules.Empty) { // '{0}' is a C or C++ binary built with the stack protector buffer security // feature enabled for all modules, making it more difficult for an attacker to // exploit stack buffer overflow memory corruption vulnerabilities. context.Logger.Log(MessageKind.Pass, context, RuleUtilities.BuildMessage(context, RulesResources.EnableStackProtection_Pass)); return; } if (!unknownLanguageModules.Empty) { // '{0}' contains code from unknown language, preventing a comprehensive analysis of the // stack protector buffer security features. The language could not be identified for // the following modules: {1}. context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildMessage(context, RulesResources.EnableStackProtection_UnknownModuleLanguage_Fail, unknownLanguageModules.ToString())); } if (!noGsModules.Empty) { // '{0}' is a C or C++ binary built with the stack protector buffer security feature // disabled in one or more modules. The stack protector (/GS) is a security feature // of the compiler which makes it more difficult to exploit stack buffer overflow // memory corruption vulnerabilities. To resolve this issue, ensure that your code // is compiled with the stack protector enabled by supplying /GS on the Visual C++ // compiler command line. The affected modules were: {1} context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildMessage(context, RulesResources.EnableStackProtection_Fail, noGsModules.ToString())); } }
public void Analyze(BinaryAnalyzerContext context) { PEHeader peHeader = context.PE.PEHeaders.PEHeader; Pdb pdb = context.Pdb; if (pdb == null) { context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildCouldNotLoadPdbMessage(context)); return; } Dictionary <string, TruncatedCompilandRecordList> vulnerabilityToModules = new Dictionary <string, TruncatedCompilandRecordList>(); TruncatedCompilandRecordList moduleList; foreach (DisposableEnumerableView <Symbol> omView in pdb.CreateObjectModuleIterator()) { Symbol om = omView.Value; ObjectModuleDetails details = om.GetObjectModuleDetails(); if (details.Language != Language.C && details.Language != Language.Cxx) { continue; } if (!details.HasDebugInfo) { continue; } foreach (DisposableEnumerableView <SourceFile> sfView in pdb.CreateSourceFileIterator(om)) { SourceFile sf = sfView.Value; string fileName = Path.GetFileName(sf.FileName); if (!_files.Contains(fileName) || sf.HashType == HashType.None) { continue; } string hash = fileName + "#" + BitConverter.ToString(sf.Hash); VulnerableDependencyDescriptor descriptor; if (_filesToVulnerabilitiesMap.TryGetValue(hash, out descriptor)) { if (!vulnerabilityToModules.TryGetValue(descriptor.Id, out moduleList)) { moduleList = vulnerabilityToModules[descriptor.Id] = new TruncatedCompilandRecordList(); } moduleList.Add(om.CreateCompilandRecordWithSuffix(hash)); } } } if (vulnerabilityToModules.Count != 0) { foreach (string id in vulnerabilityToModules.Keys) { moduleList = vulnerabilityToModules[id]; VulnerableDependencyDescriptor descriptor = (VulnerableDependencyDescriptor)context.Policy.GetProperty(VulnerableDependencies)[id]; // '{0}' was built with a version of {1} which is subject to the following issues: {2}. // To resolve this, {3}. The source files that triggered this were: {4} context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildMessage(context, RulesResources.DoNotIncorporateVulnerableBinaries_Fail, descriptor.Name, descriptor.VulnerabilityDescription, descriptor.Resolution, moduleList.ToString())); } return; } // '{0}' does not incorporate any known vulnerable dependencies, as configured by current policy. context.Logger.Log(MessageKind.Pass, context, RuleUtilities.BuildMessage(context, RulesResources.DoNotIncorporateVulnerableBinaries_Pass)); }
public void Analyze(BinaryAnalyzerContext context) { PEHeader peHeader = context.PE.PEHeaders.PEHeader; Pdb pdb = context.Pdb; if (pdb == null) { context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildCouldNotLoadPdbMessage(context)); return; } Version minLinkVersion; Version minCompilerVersion; if (context.PE.IsXBox) { minCompilerVersion = context.Policy.GetProperty(MinimumToolVersions)[MIN_XBOX_COMPILER_VER]; minLinkVersion = context.Policy.GetProperty(MinimumToolVersions)[MIN_XBOX_LINKER_VER]; } else { minCompilerVersion = context.Policy.GetProperty(MinimumToolVersions)[MIN_COMPILER_VER]; minLinkVersion = context.Policy.GetProperty(MinimumToolVersions)[MIN_LINKER_VER]; } TruncatedCompilandRecordList badModuleList = new TruncatedCompilandRecordList(); StringToVersionMap allowedLibraries = context.Policy.GetProperty(AllowedLibraries); foreach (DisposableEnumerableView <Symbol> omView in pdb.CreateObjectModuleIterator()) { Symbol om = omView.Value; ObjectModuleDetails omDetails = om.GetObjectModuleDetails(); // see if the item is in our skip list if (!string.IsNullOrEmpty(om.Lib)) { string libFileName = string.Concat(System.IO.Path.GetFileName(om.Lib), ",", omDetails.Language.ToString()).ToLowerInvariant(); Version minAllowedVersion; if (allowedLibraries.TryGetValue(libFileName, out minAllowedVersion) && omDetails.CompilerVersion >= minAllowedVersion) { continue; } } Version actualVersion; Version minimumVersion; Language omLanguage = omDetails.Language; switch (omLanguage) { case Language.C: case Language.Cxx: actualVersion = Minimum(omDetails.CompilerVersion, omDetails.CompilerFrontEndVersion); minimumVersion = minCompilerVersion; break; case Language.MASM: // TODO verify this actualVersion = omDetails.CompilerVersion; minimumVersion = minLinkVersion; break; case Language.LINK: continue; default: continue; } if (actualVersion < minimumVersion) { // built with {0} compiler version {1} (Front end version: {2}) badModuleList.Add( om.CreateCompilandRecordWithSuffix( String.Format(CultureInfo.InvariantCulture, RulesResources.BuildWithSecureTools_BadModule, omLanguage, omDetails.CompilerVersion, omDetails.CompilerFrontEndVersion))); } } if (!badModuleList.Empty) { // '{0}' was compiled with one or more modules which were not built using minimum // required tool versions (compiler version {1}, linker version {2}). More recent // tool chains contain mitigations that make it more difficult for an attacker to // exploit vulnerabilities in programs they produce. To resolve this issue, // compile and/or link your binary with more recent tools. If you are servicing a // product where the tool chain cannot be modified (e.g. producing a hotfix for // an already shipped version) ignore this warning. // Modules built outside of policy: {3} context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildMessage(context, RulesResources.BuildWithSecureTools_Fail, minCompilerVersion.ToString(), minLinkVersion.ToString(), badModuleList.CreateSortedObjectList())); return; } // '{0}' was built with a tool chain that satisfies configured policy // (compiler minimum version {1}, linker minimum version {2}). context.Logger.Log(MessageKind.Pass, context, RuleUtilities.BuildMessage(context, RulesResources.BuildWithSecureTools_Pass, minCompilerVersion.ToString(), minLinkVersion.ToString())); }
public void Analyze(BinaryAnalyzerContext context) { PEHeader peHeader = context.PE.PEHeaders.PEHeader; Pdb pdb = context.Pdb; if (pdb == null) { context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildCouldNotLoadPdbMessage(context)); return; } List <string> names = new List <string>(); foreach (DisposableEnumerableView <Symbol> functionView in pdb.CreateGlobalFunctionIterator()) { Symbol function = functionView.Value; if (function.IsManaged) { continue; } if (!function.IsSafeBuffers) { continue; } string functionName = function.GetUndecoratedName(); if (functionName == "__security_init_cookie" || context.Policy.GetProperty(ApprovedFunctionsThatDisableStackProtection).Contains(functionName)) { continue; } names.Add(functionName); } if (names.Count != 0) { string functionNames = string.Join(";", names); // '{0}' is a C or C++ binary built with function(s) ({1}) that disable the stack // protector. The stack protector (/GS) is a security feature of the compiler // which makes it more difficult to exploit stack buffer overflow memory // corruption vulnerabilities. Disabling the stack protector, even on a // function -by-function basis, is disallowed by SDL policy. To resolve this // issue, remove occurrences of __declspec(safebuffers) from your code. If the // additional code inserted by the stack protector has been shown in profiling // to cause a significant performance problem for your application, attempt to // move stack buffer modifications out of the hot path of execution to allow the // compiler to avoid inserting stack protector checks in these locations rather // than disabling the stack protector altogether. context.Logger.Log(MessageKind.Fail, context, RuleUtilities.BuildMessage(context, RulesResources.DoNotDisableStackProtectionForFunctions_Pass, functionNames)); return; } // '{0}' is a C or C++ binary built with the stack protector buffer // security feature enabled which does not disable protection for // any individual functions (via __declspec(safebuffers), making it // more difficult for an attacker to exploit stack buffer overflow // memory corruption vulnerabilities. context.Logger.Log(MessageKind.Pass, context, RuleUtilities.BuildMessage(context, RulesResources.DoNotDisableStackProtectionForFunctions_Pass)); }