static void ScanDirectory(DirectoryInfo AppDirectory, string RelativeAppPath) { var ignoreExtensions = new[] { "png", "jpg", "jpeg", "gif", "ico", "js", "css", }; var urlDictionary = CreateUrlDictionary(RelativeAppPath, AppDirectory); var urlCollections = new[] { urlDictionary.Select(x => x.Key).ToList(), new List <string>() }; var traceTable = new TraceTable(); var report = new StringBuilder(); var coverageReport = new StringBuilder(); var map = new ApplicationMap(); var alerts = new ScanAlertCollection(); int requestCount = 0; MessageDumper messageDumper = null; if (Config.DumpMessages) { messageDumper = new MessageDumper(_reportWriter.ReportPath.FullName); } for (int urlCollectionIndex = 0; urlCollectionIndex < 2; urlCollectionIndex++) { foreach (ScanPluginBase plugin in Config.ScanPlugins) { if (Config.CodeCoverageReport > 0) { ScanMetrics.Default.Annotator.Reset(); ScanMetrics.Default.Annotator.AnnotationTable = ScanMetrics.Default.PluginAnnotations[plugin.ToString()]; } if (!urlCollections[urlCollectionIndex].Any()) { continue; } ScannerCli.DisplayScanPlugin(plugin); foreach (var remotePath in urlCollections[urlCollectionIndex]) { ScannerCli.DisplayResourcePath(remotePath); IEnumerable <IEnumerable <TracedFunctionCall> > calls = null; if (urlCollectionIndex == 0) { var key = urlDictionary[remotePath]; if (Program.PageFieldTable.ContainsKey(key)) { calls = Program.PageFieldTable[key] .Select(x => x.Value.Select(y => new TracedFunctionCall() { Name = x.Key, ParameterValues = new List <string>() { y } })); } } for (int i = 0; i < plugin.ModeCount; i++) { foreach (var useStaticAnalysisInputs in new[] { false, true }) { var trace = new FileTrace(); if (useStaticAnalysisInputs && calls != null) { if (!calls.Any(x => x.Any())) { continue; } foreach (var c in calls) { trace.Calls.AddRange(c); } } bool discoveredVars = true; while (discoveredVars) { var traceFile = new FileInfo(TraceFileName); IOHelper.TryAction(traceFile.Delete); var client = new TcpClient() { ReceiveTimeout = Config.Timeout, SendTimeout = Config.Timeout }; while (!client.Connected) { try { client.Connect(Config.Server, Config.Port); } catch (SocketException ex) { ScannerCli.DisplayError(ex.Message); Thread.Sleep(5000); } } client.LingerState = new LingerOption(true, 0); HttpResponse resp = null; string req, respString = ""; using (var stream = client.GetStream()) { req = plugin.BuildRequest(i, remotePath, trace); if (Config.DumpMessages) { messageDumper.Dump(req, requestCount, MessageType.Request); } var stopwatch = new Stopwatch(); stopwatch.Start(); stream.WriteString(req); try { var sgs = trace.Calls .Superglobals() .Select(x => new { x.Name, Value = x.ParameterValues.Count > 0 ? x.ParameterValues[0] : null }) .Distinct(); var discoveredVarCount = sgs.Count(); var reader = new HttpResponseReader(stream); resp = reader.Read(); stopwatch.Stop(); respString = resp.CompleteResponse; ScannerCli.DisplayResponse( resp, i, discoveredVarCount, stopwatch.ElapsedMilliseconds, resp.CompleteResponse.Length); } catch (SocketException) { ScannerCli.DisplayResponseError(respString); } catch (IOException) { ScannerCli.DisplayResponseError(respString); } } if (urlCollectionIndex == 0 && resp != null) { var abs = "http://" + Config.Server + remotePath; var urls = new UriScraper() { Regex = new Regex(@"[/.]" + Config.Server + @"($|/)", RegexOptions.IgnoreCase | RegexOptions.Compiled), } .Parse(resp.Body, abs) .Select(x => new Uri(x).LocalPath); foreach (var url in urls) { if (!ignoreExtensions.Any(x => url.ToLower().EndsWith("." + x)) && !urlCollections[0].Contains(url) && !urlCollections[1].Contains(url)) { urlCollections[1].Add(url); ScannerCli.DisplayDiscoveredUrl(url); } } } client.Close(); if (Config.DumpMessages) { messageDumper.Dump(respString, requestCount, MessageType.Response); } requestCount++; traceFile = new FileInfo(TraceFileName); FileTrace newTrace = null; IOHelper.TryAction(() => { if (traceFile.Exists) { using (var reader = traceFile.OpenText()) newTrace = FileTrace.Parse(reader); } }); if (newTrace == null) { newTrace = new FileTrace(); } newTrace.Request = req; newTrace.Response = respString; newTrace.File = remotePath; var alert = plugin.ScanTrace(newTrace); if (alert != null) { if (Config.BeepOnAlert) { Console.Beep(); } ScannerCli.DisplayAlert(alert); alerts.Add(alert); report.Append(alert.ToString()); } discoveredVars = false; foreach (TracedFunctionCall c in newTrace.Calls .Superglobals() .Where(x => x.ParameterValues.Any())) { var oldCalls = trace.Calls.Where(x => x.Name == c.Name && x.ParameterValues.SequenceEqual(c.ParameterValues)); if (!oldCalls.Any()) { discoveredVars = true; break; } } var orphanedInputs = trace.Calls .Superglobals() .Where(x => !newTrace.Calls .Any(y => x.Name == y.Name && x.ParameterValues .SequenceEqual(y.ParameterValues))) .ToArray(); newTrace.Calls.AddRange(orphanedInputs); trace = newTrace; } var superglobals = trace.Calls.Superglobals(); map.AddTrace(trace); if (Config.DiscoveryReport) { if (!traceTable.ContainsKey(plugin)) { traceTable.Add(plugin, new Dictionary <int, Dictionary <string, FileTrace> >()); } if (!traceTable[plugin].ContainsKey(i)) { traceTable[plugin].Add(i, new Dictionary <string, FileTrace>()); } if (!traceTable[plugin][i].ContainsKey(trace.File)) { traceTable[plugin][i].Add(trace.File, trace); } else { traceTable[plugin][i][trace.File] = trace; } } } } } if (Config.CodeCoverageReport > 0 && urlCollectionIndex == 0) { Cli.WriteLine("Calculating code coverage..."); CodeCoverageTable coverage = null; IOHelper.TryAction(() => { var calculator = new CodeCoverageCalculator( ScanMetrics.Default.Annotator.AnnotationFile, ScanMetrics.Default.PluginAnnotations[plugin.ToString()]); coverage = calculator.CalculateCoverage(); }); coverage.Plugin = plugin.ToString(); coverageReport.AppendLine(coverage.ToString() + "\r\n"); } Cli.WriteLine(); } } _reportWriter.Write("Vulnerability Report", report.ToString()); _reportWriter.Write("Input Map Report", map.ToXml(), "xml"); if (alerts.Any()) { _reportWriter.Write("Vulnerability Report", alerts.ToXml(), "pxml"); } if (Config.DiscoveryReport) { _reportWriter.Write("Scan Overview Report", DiscoveryReport.Create(traceTable)); } if (Config.CodeCoverageReport > 0) { _reportWriter.Write("Code Coverage Report", coverageReport.ToString()); var annotationXml = ScanMetrics.Default.PluginAnnotations.ToXml(); var annotationFile = _reportWriter.Write( "Annotation", annotationXml, "axml"); Cli.WriteLine(); var commenter = new CoverageCommenter(ScanMetrics.Default.PluginAnnotations); commenter.LoadTable(annotationFile); commenter.WriteCommentedFiles(_reportWriter.ReportPath.FullName); _reportWriter.ReportFiles.Add( new ReportFile( "Coverage Comments", Path.Combine("Code Coverage", "index.html"))); } }
static void ScanDirectory(DirectoryInfo AppDirectory, string RelativeAppPath) { var ignoreExtensions = new[] { "png", "jpg", "jpeg", "gif", "ico", "js", "css", }; var urlDictionary = CreateUrlDictionary(RelativeAppPath, AppDirectory); var urlCollections = new[] { urlDictionary.Select(x => x.Key).ToList(), new List<string>() }; var traceTable = new TraceTable(); var report = new StringBuilder(); var coverageReport = new StringBuilder(); var map = new ApplicationMap(); var alerts = new ScanAlertCollection(); int requestCount = 0; MessageDumper messageDumper = null; if (Config.DumpMessages) { messageDumper = new MessageDumper(_reportWriter.ReportPath.FullName); } for (int urlCollectionIndex = 0; urlCollectionIndex < 2; urlCollectionIndex++) foreach (ScanPluginBase plugin in Config.ScanPlugins) { if (Config.CodeCoverageReport > 0) { ScanMetrics.Default.Annotator.Reset(); ScanMetrics.Default.Annotator.AnnotationTable = ScanMetrics.Default.PluginAnnotations[plugin.ToString()]; } if (!urlCollections[urlCollectionIndex].Any()) continue; ScannerCli.DisplayScanPlugin(plugin); foreach (var remotePath in urlCollections[urlCollectionIndex]) { ScannerCli.DisplayResourcePath(remotePath); IEnumerable<IEnumerable<TracedFunctionCall>> calls = null; if (urlCollectionIndex == 0) { var key = urlDictionary[remotePath]; if (Program.PageFieldTable.ContainsKey(key)) calls = Program.PageFieldTable[key] .Select(x => x.Value.Select(y => new TracedFunctionCall() { Name = x.Key, ParameterValues = new List<string>() { y } })); } for (int i = 0; i < plugin.ModeCount; i++) { foreach (var useStaticAnalysisInputs in new[] { false, true }) { var trace = new FileTrace(); if (useStaticAnalysisInputs && calls != null) { if (!calls.Any(x => x.Any())) continue; foreach (var c in calls) trace.Calls.AddRange(c); } bool discoveredVars = true; while (discoveredVars) { var traceFile = new FileInfo(TraceFileName); IOHelper.TryAction(traceFile.Delete); var client = new TcpClient() { ReceiveTimeout = Config.Timeout, SendTimeout = Config.Timeout }; while (!client.Connected) try { client.Connect(Config.Server, Config.Port); } catch (SocketException ex) { ScannerCli.DisplayError(ex.Message); Thread.Sleep(5000); } client.LingerState = new LingerOption(true, 0); HttpResponse resp = null; string req, respString = ""; using (var stream = client.GetStream()) { req = plugin.BuildRequest(i, remotePath, trace); if (Config.DumpMessages) { messageDumper.Dump(req, requestCount, MessageType.Request); } var stopwatch = new Stopwatch(); stopwatch.Start(); stream.WriteString(req); try { var sgs = trace.Calls .Superglobals() .Select(x => new { x.Name, Value = x.ParameterValues.Count > 0 ? x.ParameterValues[0] : null }) .Distinct(); var discoveredVarCount = sgs.Count(); var reader = new HttpResponseReader(stream); resp = reader.Read(); stopwatch.Stop(); respString = resp.CompleteResponse; ScannerCli.DisplayResponse( resp, i, discoveredVarCount, stopwatch.ElapsedMilliseconds, resp.CompleteResponse.Length); } catch (SocketException) { ScannerCli.DisplayResponseError(respString); } catch (IOException) { ScannerCli.DisplayResponseError(respString); } } if (urlCollectionIndex == 0 && resp != null) { var abs = "http://" + Config.Server + remotePath; var urls = new UriScraper() { Regex = new Regex(@"[/.]" + Config.Server + @"($|/)", RegexOptions.IgnoreCase | RegexOptions.Compiled), } .Parse(resp.Body, abs) .Select(x => new Uri(x).LocalPath); foreach (var url in urls) { if (!ignoreExtensions.Any(x => url.ToLower().EndsWith("." + x)) && !urlCollections[0].Contains(url) && !urlCollections[1].Contains(url)) { urlCollections[1].Add(url); ScannerCli.DisplayDiscoveredUrl(url); } } } client.Close(); if (Config.DumpMessages) { messageDumper.Dump(respString, requestCount, MessageType.Response); } requestCount++; traceFile = new FileInfo(TraceFileName); FileTrace newTrace = null; IOHelper.TryAction(() => { if (traceFile.Exists) using (var reader = traceFile.OpenText()) newTrace = FileTrace.Parse(reader); }); if (newTrace == null) { newTrace = new FileTrace(); } newTrace.Request = req; newTrace.Response = respString; newTrace.File = remotePath; var alert = plugin.ScanTrace(newTrace); if (alert != null) { if (Config.BeepOnAlert) Console.Beep(); ScannerCli.DisplayAlert(alert); alerts.Add(alert); report.Append(alert.ToString()); } discoveredVars = false; foreach (TracedFunctionCall c in newTrace.Calls .Superglobals() .Where(x => x.ParameterValues.Any())) { var oldCalls = trace.Calls.Where(x => x.Name == c.Name && x.ParameterValues.SequenceEqual(c.ParameterValues)); if (!oldCalls.Any()) { discoveredVars = true; break; } } var orphanedInputs = trace.Calls .Superglobals() .Where(x => !newTrace.Calls .Any(y => x.Name == y.Name && x.ParameterValues .SequenceEqual(y.ParameterValues))) .ToArray(); newTrace.Calls.AddRange(orphanedInputs); trace = newTrace; } var superglobals = trace.Calls.Superglobals(); map.AddTrace(trace); if (Config.DiscoveryReport) { if (!traceTable.ContainsKey(plugin)) { traceTable.Add(plugin, new Dictionary<int, Dictionary<string, FileTrace>>()); } if (!traceTable[plugin].ContainsKey(i)) { traceTable[plugin].Add(i, new Dictionary<string, FileTrace>()); } if (!traceTable[plugin][i].ContainsKey(trace.File)) { traceTable[plugin][i].Add(trace.File, trace); } else { traceTable[plugin][i][trace.File] = trace; } } } } } if (Config.CodeCoverageReport > 0 && urlCollectionIndex == 0) { Cli.WriteLine("Calculating code coverage..."); CodeCoverageTable coverage = null; IOHelper.TryAction(() => { var calculator = new CodeCoverageCalculator( ScanMetrics.Default.Annotator.AnnotationFile, ScanMetrics.Default.PluginAnnotations[plugin.ToString()]); coverage = calculator.CalculateCoverage(); }); coverage.Plugin = plugin.ToString(); coverageReport.AppendLine(coverage.ToString() + "\r\n"); } Cli.WriteLine(); } _reportWriter.Write("Vulnerability Report", report.ToString()); _reportWriter.Write("Input Map Report", map.ToXml(), "xml"); if (alerts.Any()) { _reportWriter.Write("Vulnerability Report", alerts.ToXml(), "pxml"); } if (Config.DiscoveryReport) { _reportWriter.Write("Scan Overview Report", DiscoveryReport.Create(traceTable)); } if (Config.CodeCoverageReport > 0) { _reportWriter.Write("Code Coverage Report", coverageReport.ToString()); var annotationXml = ScanMetrics.Default.PluginAnnotations.ToXml(); var annotationFile = _reportWriter.Write( "Annotation", annotationXml, "axml"); Cli.WriteLine(); var commenter = new CoverageCommenter(ScanMetrics.Default.PluginAnnotations); commenter.LoadTable(annotationFile); commenter.WriteCommentedFiles(_reportWriter.ReportPath.FullName); _reportWriter.ReportFiles.Add( new ReportFile( "Coverage Comments", Path.Combine("Code Coverage", "index.html"))); } }