/// <summary> /// Method to create a common taint set from a list of functions /// </summary> /// <param name="functionList">The list of functions to analyze</param> /// <param name="argInfos">The argument ExpressionInfo to include in the analysis</param> /// <returns>A common worst-case TaintSets</returns> private ExpressionInfo CreateCommonTaintSets(IEnumerable <Function> functionList, IList <ExpressionInfo> argInfos) { //Create a common TaintSets that will be merged with the TaintSets from the found functions var exprInfo = new ExpressionInfo(); if (functionList.Any()) { AnalyzedFunctions.Add(functionList.First().Name); foreach (var func in functionList) { var summary = MatchWithFunctionSummary(func, argInfos); if (summary == null) { var taintSetsForFuncOrMethod = GetTaintSetsForFuncOrMethod(func, argInfos); exprInfo = exprInfo.Merge(taintSetsForFuncOrMethod); GenerateSummary(func, argInfos, taintSetsForFuncOrMethod); } else { exprInfo = exprInfo.Merge(summary.ReturnValue); } } } else { // If function cannot be resolve, use the arguments taint values. exprInfo = argInfos.Aggregate(exprInfo, (current, arg) => current.Merge(arg)); } return(exprInfo); }
/// <summary> /// Make sure that hardcoded callback functions are analyzed. /// </summary> private ExpressionInfo HandleHookCall(XmlNode node, ExpressionInfo exprInfo, IVariableStorage currentStorage, AnalysisStacks analysisStacks) { var functionCall = new FunctionCallExtractor().ExtractFunctionCall(node); var result = new ExpressionInfo(); foreach (var argument in functionCall.Arguments.Where(a => a.Value.LocalName == AstConstants.Nodes.Scalar_String)) { var stringValue = ScalarNode.GetStringValue(argument.Value); var functionAnalyzer = FunctionMethodAnalyzerFactory(currentStorage); var functions = functionAnalyzer.FunctionsHandler.LookupFunction(stringValue); if (functions.Any()) { //Console.WriteLine("FOUND " + functions.Count() + " functions with name: " + stringValue); var call = new FunctionCall(stringValue, null, AstNode.GetStartLine(node), AstNode.GetEndLine(node)); if (analysisStacks.CallStack.Any(c => c.Name == call.Name)) { // Avoid recursive registrations. continue; } analysisStacks.CallStack.Push(call); var funcCallResult = functionAnalyzer.AnalyzeFunctionCall(call, new ExpressionInfo[0]); analysisStacks.CallStack.Pop(); result = result.Merge(funcCallResult); } // https://codex.wordpress.org/Function_Reference/add_submenu_page // If a method is called, it is called with: array( $this, 'function_name' ) or array( __CLASS__, 'function_name' ) } return(result); }
/// <summary> /// Analyses a custom function in for security issues, with the currenctly known taint for actual parameters. /// </summary> /// <returns>A TainSets for the custom function that is being analyzed</returns> /// <param name="customFunction">Custom function object to perform the analysis on</param> /// <param name="varStorage">The currently known variable storage (this is to included because of superglobals, globals etc.)</param> /// <param name="paramActualVals">Parameter actual values</param> /// <param name="resolver">File inclusion resolver</param> /// <param name="includeStack">Currently known includes</param> /// <param name="functionCalls">Currently known function calls</param> internal ExpressionInfo AnalyseCustomFunction(Function customFunction, ImmutableVariableStorage varStorage, IVulnerabilityStorage vulnerabilityStorage, IList <ExpressionInfo> paramActualVals, IIncludeResolver resolver, AnalysisStacks stacks) { var stmts = customFunction.AstNode.GetSubNode(AstConstants.Subnode + ":" + AstConstants.Subnodes.Stmts).FirstChild; var traverser = new XmlTraverser(); var cfgcreator = new CFGCreator(); traverser.AddVisitor(cfgcreator); traverser.Traverse(stmts); var cfgPruner = new CFGPruner(); cfgPruner.Prune(cfgcreator.Graph); var initialTaint = varStorage.ToMutable(); initialTaint.SuperGlobals.Clear(); initialTaint.SuperGlobals.AddRange(varStorage.SuperGlobals); initialTaint.LocalVariables.Clear(); initialTaint.LocalAccessibleGlobals.Clear(); for (int i = 1; i <= paramActualVals.Count; i++) { var paramFormal = customFunction.Parameters.FirstOrDefault(x => x.Key.Item1 == i); if (paramFormal.Value == null) { continue; } var @var = new Variable(paramFormal.Value.Name, VariableScope.Function) { Info = paramActualVals[i - 1].ValueInfo }; initialTaint.LocalVariables.Add(paramFormal.Value.Name, @var); } var blockAnalyzer = new TaintBlockAnalyzer(vulnerabilityStorage, resolver, AnalysisScope.Function, fileAnalyzer, stacks, subroutineAnalyzerFactory, _funcHandler); blockAnalyzer.AnalysisExtensions.AddRange(AnalysisExtensions); var condAnalyser = new ConditionTaintAnalyser(AnalysisScope.Function, resolver, stacks.IncludeStack, _funcHandler); var cfgTaintAnalysis = new TaintAnalysis(blockAnalyzer, condAnalyser, ImmutableVariableStorage.CreateFromMutable(initialTaint)); //var taintAnalysis = new CFGTraverser(new ForwardTraversal(), cfgTaintAnalysis, new QueueWorklist()); var taintAnalysis = new CFGTraverser(new ForwardTraversal(), cfgTaintAnalysis, new ReversePostOrderWorkList(cfgcreator.Graph)); taintAnalysis.Analyze(cfgcreator.Graph); var exprInfoAll = new ExpressionInfo(); foreach (ExpressionInfo exprInfo in blockAnalyzer.ReturnInfos) { exprInfoAll = exprInfoAll.Merge(exprInfo); } return(exprInfoAll); }
public void ExpressionInfo_Merge() { var sqliTaint = new SQLITaintSet(SQLITaint.SQL_ALL); var xsstaint = new XSSTaintSet(XSSTaint.XSS_ALL); var ts1 = new TaintSets(sqliTaint, xsstaint); var exprInfo1 = new ExpressionInfo { ExpressionTaint = ts1 }; var exprInfo2 = new ExpressionInfo(); var exprInfo = exprInfo2.Merge(exprInfo1); Assert.AreEqual(sqliTaint, exprInfo.ExpressionTaint.SqliTaint.Single(), "SQL Taint was not the expected"); Assert.AreEqual(xsstaint, exprInfo.ExpressionTaint.XssTaint.Single(), "XSS Taint was not the expected"); }
/// <summary> /// Method to analyze a PHP method call with the class name included /// </summary> /// <param name="methodCall">The method call to analyze</param> /// <param name="argInfos">The argument infos to include in the analysis</param> /// <returns>The common TaintSets found</returns> public ExpressionInfo AnalyzeMethodCall(MethodCall methodCall, IList <ExpressionInfo> argInfos) { //In most cases there should be either 0 or 1 classes, but situations where functions are specified several places can happen. //Therefore, we support it and select the worst case. var exprInfo = new ExpressionInfo(); //Try to find all the possible method calls, and create the worst case scenario of taints. foreach (string className in methodCall.ClassNames) { IList <Function> funclist = _funcHandler.LookupFunction(methodCall.CreateFullMethodName(className)); exprInfo = exprInfo.Merge(CreateCommonTaintSets(funclist, argInfos)); } return(exprInfo); }
/// <summary> /// Method to get the TaintSets for a single function. /// </summary> /// <param name="func">The function to find the TaintSets for</param> /// <param name="argInfos">The argument ExpressionInfo to include in the analysis</param> /// <returns>The found TaintSets for the function</returns> private ExpressionInfo GetTaintSetsForFuncOrMethod(Function func, IList <ExpressionInfo> argInfos) { //Start working from the bast case, and find worser cases. var tmp = new ExpressionInfo(); //Try to cast the function to every possible function/method var xssSantizerFunc = CastToFunctionType <XSSSanitizer>(func); var sqlSantizerFunc = CastToFunctionType <SQLSanitizer>(func); //We have to find customFunctions explicitly as it can always be casted to a Function, as it is the base class. //However if we introduce a custom function class then it can be castet to this type, and work like the rest. Function customFunc = _funcHandler.FindCustomFunctionByName(func.Name); //If there exists a Source with the function name, then we select it. //But sources and be of several other types, like arrays etc. Source sourceFunc = _funcHandler.FindSourceByName(func.Name); //TODO: This should be changed to be dependent on parameters!!! if (xssSantizerFunc != null) { SQLITaintSet sqliTaintSet = new SQLITaintSet(); XSSTaintSet xssts = new XSSTaintSet(); var returnParameters = xssSantizerFunc.Parameters.Where(x => x.Value.IsReturn); try { ExpressionInfo returnParameter = new ExpressionInfo(); foreach (var item in returnParameters) { var actualParam = argInfos.ElementAt((int)item.Key.Item1 - 1); if (actualParam == null) { continue; } returnParameter = returnParameter.Merge(actualParam); } sqliTaintSet = returnParameter.ExpressionTaint.SqliTaint.Aggregate(sqliTaintSet, (current, taint) => current.Merge(taint)); xssts = returnParameter.ExpressionTaint.XssTaint.Aggregate(xssts, (current, taint) => current.Merge(taint)); } catch (NullReferenceException) { Debug.WriteLine("Could not map actual parameter with formal parameter, using default"); sqliTaintSet = new SQLITaintSet(); xssts = argInfos.Aggregate(xssts, (current1, info) => current1.Merge(info.ExpressionTaint.XssTaint.Aggregate(current1, (current, taint) => current.Merge(taint)))); } tmp = new ExpressionInfo() { ExpressionTaint = new TaintSets( sqliTaintSet, xssSantizerFunc.DefaultStatus < xssts.TaintTag ? new XSSTaintSet(xssSantizerFunc.DefaultStatus) : xssts.DeepClone()) }; } if (sqlSantizerFunc != null) { XSSTaintSet xssts = new XSSTaintSet(); SQLITaintSet sqlts = new SQLITaintSet(); var returnParameters = sqlSantizerFunc.Parameters.Where(x => x.Value.IsReturn); try { ExpressionInfo returnParameter = new ExpressionInfo(); foreach (var item in returnParameters) { var actualParam = argInfos.ElementAt((int)item.Key.Item1 - 1); if (actualParam == null) { continue; } returnParameter = returnParameter.Merge(actualParam); } xssts = returnParameter.ExpressionTaint.XssTaint.Aggregate(xssts, (current, taint) => current.Merge(taint)); sqlts = returnParameter.ExpressionTaint.SqliTaint.Aggregate(sqlts, (current, taint) => current.Merge(taint)); } catch (NullReferenceException) { Debug.WriteLine("Could not map actual parameter with formal parameter, using default"); xssts = new XSSTaintSet(); sqlts = argInfos.Aggregate(sqlts, (current1, info) => current1.Merge(info.ExpressionTaint.SqliTaint.Aggregate(current1, (current, taint) => current.Merge(taint)))); } tmp = new ExpressionInfo() { ExpressionTaint = new TaintSets( sqlSantizerFunc.DefaultStatus < sqlts.TaintTag ? new SQLITaintSet(sqlSantizerFunc.DefaultStatus) : sqlts.DeepClone(), xssts) }; } if (customFunc != null) { _funcHandler.ScannedFunctions.Add(customFunc); tmp = this._customFunctionHandler.AnalyseCustomFunction(customFunc, this._varStorage, _vulnerabilityStorage, argInfos, this._incResolver, this._stacks); } if (sourceFunc != null) { tmp = new ExpressionInfo() { ExpressionTaint = new TaintSets(sourceFunc.SqliTaint, sourceFunc.XssTaint) }; } //If no matches were found the function is unknown and therefore we return the worst case of them all! return(tmp); }
private ExpressionInfo Node_MethodCall(XmlNode node) { var functionCallExtractor = new FunctionCallExtractor(); var methodCall = functionCallExtractor.ExtractMethodCall(node, this._variableStorage, this._analysisScope); bool isAlreadyInStack = _analysisStacks.CallStack.Any(x => x.Name == methodCall.Name); _analysisStacks.CallStack.Push(methodCall); var argInfos = new List<ExpressionInfo>(); //Actually analyze the arguments for (uint index = 1; index <= methodCall.Arguments.Count; index++) { var item = methodCall.Arguments.FirstOrDefault(x => x.Key == index); var exprInfo = this.Analyze(item.Value); if (_varResolver.IsResolvableNode(item.Value)) { var @var = _varResolver.ResolveVariable(item.Value); exprInfo.ValueInfo = @var.Variable.Info; } argInfos.Add(exprInfo); } if (methodCall.Name == "") { var exprInfo = new ExpressionInfo(); _analysisStacks.CallStack.Pop(); return argInfos.Aggregate(exprInfo, (current, info) => current.Merge(info)); } var customFunctionHandler = new CustomFunctionHandler(this._analyzer, _subroutineAnalyzerFactory); customFunctionHandler.AnalysisExtensions.AddRange(this.AnalysisExtensions); var functionMethodAnalyzer = _subroutineAnalyzerFactory.Create(ImmutableVariableStorage.CreateFromMutable(_variableStorage), _inclusionResolver, _analysisStacks, customFunctionHandler, _vulnerabilityStorage); var methodCallTaintSet = new ExpressionInfo(); if(!isAlreadyInStack) { methodCallTaintSet = functionMethodAnalyzer.AnalyzeMethodCall(methodCall, argInfos); } FunctionsHandler fh = FunctionsHandler.Instance; var resultTaintSet = new ExpressionInfo(); foreach (var className in methodCall.ClassNames.Distinct()) { var tempResultTaintSet = methodCallTaintSet.AssignmentClone(); var sqlSaniFunc = fh.FindSQLSanitizerByName(methodCall.CreateFullMethodName(className)); var sqlSinkFunc = fh.FindSQLSinkByName(methodCall.CreateFullMethodName(className)); var xssSaniFunc = fh.FindXSSSanitizerByName(methodCall.CreateFullMethodName(className)); var xssSinkFunc = fh.FindXSSSinkByName(methodCall.CreateFullMethodName(className)); if (sqlSaniFunc != null && sqlSaniFunc.DefaultStatus == SQLITaint.None) { resultTaintSet.ExpressionTaint.SqliTaint.Clear(); } if (xssSaniFunc != null && xssSaniFunc.DefaultStatus == XSSTaint.None) { resultTaintSet.ExpressionTaint.XssTaint.Clear(); } if (sqlSinkFunc != null || xssSinkFunc != null) { if (sqlSinkFunc != null) { var vulnerableSqlParams = sqlSinkFunc.Parameters.Where(x => x.Value.IsSensitive) .ToDictionary(pair => pair.Key); var parameters = methodCall.Arguments.Where(x => vulnerableSqlParams.Keys.Any(z => z.Item1 == x.Key)); foreach (var parameter in parameters) { //var argInfo = Analyze(parameter.Value); var argInfo = argInfos.ElementAt((int)(parameter.Key - 1)); CheckForSQLVulnerabilities(argInfo, parameter.Value); } if (sqlSinkFunc.ReturnType == "object" || sqlSinkFunc.ReturnType == "mix") { resultTaintSet.ValueInfo.ClassNames.AddRange(sqlSinkFunc.Classnames); } } if (xssSinkFunc != null) { var vulnerableXssParams = xssSinkFunc.Parameters.Where(x => x.Value.IsSensitive).ToDictionary(pair => pair.Key); var param = methodCall.Arguments.Where(x => vulnerableXssParams.Keys.Any(z => z.Item1 == x.Key)); foreach (var parameter in param) { var argInfo = argInfos.ElementAt((int)(parameter.Key - 1)); CheckForXssVulnerabilities(argInfo, parameter.Value); } } // Assuming sinks does not return taint. resultTaintSet.ExpressionTaint.ClearTaint(); resultTaintSet.ExpressionStoredTaint.Taint.ClearTaint(); } tempResultTaintSet = StoredMethodHandler(tempResultTaintSet, node); resultTaintSet = resultTaintSet.Merge(tempResultTaintSet); var methodNameWithClass = methodCall.CreateFullMethodName(className); bool isStoredProvider = FunctionsHandler.Instance.FindStoredProviderMethods(methodNameWithClass).Any(); if (isStoredProvider) { resultTaintSet.ExpressionStoredTaint = resultTaintSet.ExpressionStoredTaint.Merge(methodCall.Var.Info.PossibleStoredTaint); //TODO: The following is not true in all cases. // What cases? var cloned = resultTaintSet.ExpressionStoredTaint.Taint.DeepClone(); resultTaintSet.ValueInfo.NestedVariablePossibleStoredDefaultTaintFactory = () => cloned; } } _analysisStacks.CallStack.Pop(); return resultTaintSet; }
/// <summary> /// Analyses a custom function in for security issues, with the currenctly known taint for actual parameters. /// </summary> /// <returns>A TainSets for the custom function that is being analyzed</returns> /// <param name="customFunction">Custom function object to perform the analysis on</param> /// <param name="varStorage">The currently known variable storage (this is to included because of superglobals, globals etc.)</param> /// <param name="paramActualVals">Parameter actual values</param> /// <param name="resolver">File inclusion resolver</param> /// <param name="includeStack">Currently known includes</param> /// <param name="functionCalls">Currently known function calls</param> internal ExpressionInfo AnalyseCustomFunction(Function customFunction, ImmutableVariableStorage varStorage, IVulnerabilityStorage vulnerabilityStorage, IList<ExpressionInfo> paramActualVals, IIncludeResolver resolver, AnalysisStacks stacks) { var stmts = customFunction.AstNode.GetSubNode(AstConstants.Subnode + ":" + AstConstants.Subnodes.Stmts).FirstChild; var traverser = new XmlTraverser(); var cfgcreator = new CFGCreator(); traverser.AddVisitor(cfgcreator); traverser.Traverse(stmts); var cfgPruner = new CFGPruner(); cfgPruner.Prune(cfgcreator.Graph); var initialTaint = varStorage.ToMutable(); initialTaint.SuperGlobals.Clear(); initialTaint.SuperGlobals.AddRange(varStorage.SuperGlobals); initialTaint.LocalVariables.Clear(); initialTaint.LocalAccessibleGlobals.Clear(); for(int i = 1; i <= paramActualVals.Count; i++) { var paramFormal = customFunction.Parameters.FirstOrDefault(x => x.Key.Item1 == i); if (paramFormal.Value == null) { continue; } var @var = new Variable(paramFormal.Value.Name, VariableScope.Function) {Info = paramActualVals[i - 1].ValueInfo}; initialTaint.LocalVariables.Add(paramFormal.Value.Name, @var); } var blockAnalyzer = new TaintBlockAnalyzer(vulnerabilityStorage, resolver, AnalysisScope.Function, fileAnalyzer, stacks, subroutineAnalyzerFactory); blockAnalyzer.AnalysisExtensions.AddRange(AnalysisExtensions); var condAnalyser = new ConditionTaintAnalyser(AnalysisScope.Function, resolver, stacks.IncludeStack); var cfgTaintAnalysis = new TaintAnalysis(blockAnalyzer, condAnalyser, ImmutableVariableStorage.CreateFromMutable(initialTaint)); //var taintAnalysis = new CFGTraverser(new ForwardTraversal(), cfgTaintAnalysis, new QueueWorklist()); var taintAnalysis = new CFGTraverser(new ForwardTraversal(), cfgTaintAnalysis, new ReversePostOrderWorkList(cfgcreator.Graph)); taintAnalysis.Analyze(cfgcreator.Graph); var exprInfoAll = new ExpressionInfo(); foreach (ExpressionInfo exprInfo in blockAnalyzer.ReturnInfos) { exprInfoAll = exprInfoAll.Merge(exprInfo); } return exprInfoAll; }
/// <summary> /// Make sure that hardcoded callback functions are analyzed. /// </summary> private ExpressionInfo HandleHookCall(XmlNode node, ExpressionInfo exprInfo, IVariableStorage currentStorage, AnalysisStacks analysisStacks) { var functionCall = new FunctionCallExtractor().ExtractFunctionCall(node); var result = new ExpressionInfo(); foreach (var argument in functionCall.Arguments.Where(a => a.Value.LocalName == AstConstants.Nodes.Scalar_String)) { var stringValue = ScalarNode.GetStringValue(argument.Value); var functions = FunctionsHandler.Instance.LookupFunction(stringValue); if (functions.Any()) { //Console.WriteLine("FOUND " + functions.Count() + " functions with name: " + stringValue); var functionAnalyzer = this.FunctionMethodAnalyzerFactory(currentStorage); var call = new FunctionCall(stringValue, null, AstNode.GetStartLine(node), AstNode.GetEndLine(node)); if (analysisStacks.CallStack.Any(c => c.Name == call.Name)) { // Avoid recursive registrations. continue; } analysisStacks.CallStack.Push(call); var funcCallResult = functionAnalyzer.AnalyzeFunctionCall(call, new ExpressionInfo[0]); analysisStacks.CallStack.Pop(); result = result.Merge(funcCallResult); } // https://codex.wordpress.org/Function_Reference/add_submenu_page // If a method is called, it is called with: array( $this, 'function_name' ) or array( __CLASS__, 'function_name' ) } return result; }