diff --git a/README.md b/README.md index 66c7e22..5e432b6 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # SharpBurp -C# application, which parses Nmap XML output files and allows sending +C# application, which parses Nmap or Nessus XML output files and allows sending selected HTTP services to the BurpSuite Scanner via BurpSuite's REST API. Use this application to start large-scale web application security scans -based on Nmap scan results. +based on Nmap or Nessus scan results. ## Configuration @@ -18,9 +18,10 @@ store all configuration: ## Usage - 1. Load one or more Nmap XML scan result files into SharpBurp by using - the button 'Load Nmap XML'. Per default, SharpBurp only imports open ports - (see checkbox 'Import Open'). + 1. Load one or more Nmap/Nessus XML scan result files into SharpBurp by using + the button 'Load Nmap XML' or 'Load Nessus'. In case of Nmap, SharpBurp, per default, + only imports open ports (see checkbox 'Import Open') (Nessus reports only open ports, + therefore the status filters are irrelevant). 2. Tell SharpBurp, which URLs shall be sent to the BurpSuite Scanner by checking or unchecking the respective checkboxes in column 'Scan'. In addition, the table's context menu can be used to check or uncheck multiple @@ -32,7 +33,8 @@ store all configuration: BurpSuite scans is the total number of selected rows (see status bar) divided by the 'Scan Size'. 5. Click button 'Send to Burp API' to send the selected URLs to the BurpSuite - Scanner. + Scanner. Note that SharpBurp keeps track which URLs have already been sent to + BurpSuite and therefore, only sends each URL once. ## Author diff --git a/Resources/sharpburp_config.png b/Resources/sharpburp_config.png index 8bb19f8..e288c1f 100644 Binary files a/Resources/sharpburp_config.png and b/Resources/sharpburp_config.png differ diff --git a/SharpBurp/NmapLib/Core.cs b/SharpBurp/NmapLib/Core.cs new file mode 100644 index 0000000..7999777 --- /dev/null +++ b/SharpBurp/NmapLib/Core.cs @@ -0,0 +1,422 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.ComponentModel; + + +namespace ScanLib +{ + public enum ServiceProtocol + { + tcp, + udp + }; + + public enum ServiceState + { + open, + closed, + filtered, + openFiltered, + closedFiltered + }; + + public enum ScanSource + { + nmap, + nessus + }; + + /// + /// This class manages all information for a single service + /// + public class ScanEntry + { + private bool scan; + public string Host { get; set; } + private ServiceProtocol protocol; + private ServiceState state; + public int Port { get; set; } + public bool Tls { get; set; } + private string nmapNameNew; + public string NmapNameOriginal { get; } + public string Version { get; } + public int Confidence { get; } + public string OsType { get; } + public ScanSource Source { get; } + + public ScanEntry() + { + } + + public ScanEntry(ScanEntry entry) + { + this.Host = entry.Host; + this.Protocol = entry.Protocol; + this.State = entry.State; + this.Port = entry.Port; + this.Tls = entry.Tls; + this.NmapNameNew = entry.NmapNameNew; + this.NmapNameOriginal = entry.NmapNameOriginal; + this.Version = entry.Version; + this.Confidence = entry.Confidence; + this.OsType = entry.OsType; + this.Scan = entry.Scan; + this.Source = entry.Source; + } + + public ScanEntry(string host, ScanEntry entry) : this(entry) + { + this.Host = host; + } + + public ScanEntry(string protocol + , int port + , string state + , string nmapNameNew + , string nmapNameOriginal + , string version + , bool tls + , int confidence + , string osType + , ScanSource source) + { + if (protocol == "tcp") + this.Protocol = ServiceProtocol.tcp; + else if (protocol == "udp") + this.Protocol = ServiceProtocol.udp; + else + throw new NotImplementedException(String.Format("ServiceProtocol '{0}' not implemented.", protocol)); + if (state == "open") + this.State = ServiceState.open; + else if (state == "closed") + this.State = ServiceState.closed; + else if (state == "filtered") + this.State = ServiceState.filtered; + else if (state == "open|filtered") + this.State = ServiceState.openFiltered; + else if (state == "closed|filtered") + this.State = ServiceState.closedFiltered; + else + throw new NotImplementedException(String.Format("Service state '{0}' not implemented.", state)); + this.Port = port; + this.NmapNameNew = nmapNameNew; + this.NmapNameOriginal = confidence != 10 && !string.IsNullOrEmpty(nmapNameOriginal) ? String.Format("{0}?", nmapNameOriginal) : nmapNameOriginal; + this.Version = version; + this.Confidence = confidence; + this.OsType = osType; + this.Tls = tls; + this.Source = source; + } + + public bool Scan + { + get { return this.scan; } + set + { + if (value && !this.IsScanable()) + throw new Exception("Service cannot be scanned."); + this.scan = value; + } + } + + public string NmapNameNew + { + get { return this.nmapNameNew; } + set + { + this.nmapNameNew = value; + this.scan = this.IsScanable(); + } + } + + public ServiceState State + { + get { return this.state; } + set + { + this.state = value; + this.scan = this.IsScanable(); + } + } + + public ServiceProtocol Protocol + { + get { return this.protocol; } + set + { + this.protocol = value; + this.scan = this.IsScanable(); + } + } + + public Uri Url + { + get + { + Uri result = null; + if (this.IsScanable()) + { + if ((this.Tls && this.Port == 443) || (!this.Tls && this.Port == 80)) + { + result = new Uri(String.Format("{0}://{1}" + , (this.Tls ? "https" : "http") + , this.Host)); + } + else + { + result = new Uri(String.Format("{0}://{1}:{2}" + , (this.Tls ? "https" : "http") + , this.Host + , this.Port)); + } + } + return result; + } + } + + public bool IsScanable() + { + return (this.NmapNameNew == "http" || this.NmapNameNew == "https") && this.State == ServiceState.open && this.Protocol == ServiceProtocol.tcp; + } + + public bool HasState(List states) + { + return states.Contains(this.State); + } + } + + /// + /// This class implements all BackgroundWorker functionalities for SharpBurp + /// + public class ScanLoaderBackgroundWorker : BackgroundWorker + { + public XmlScanLoaderBase ScanLoader { get; } + + protected List exceptions { get; set; } + + public ScanLoaderBackgroundWorker() + { + this.WorkerSupportsCancellation = true; + this.WorkerReportsProgress = true; + this.ScanLoader = null; + this.exceptions = new List(); + } + + public List Exceptions + { + get { return this.exceptions; } + } + + public ScanLoaderBackgroundWorker(XmlScanLoaderBase nmapLoader) : this() + { + try + { + this.ScanLoader = nmapLoader; + this.DoWork += new DoWorkEventHandler(this.ScanLoader.LoadXml); + } + catch (Exception ex) + { + this.exceptions.Add(ex); + } + } + } + + /// + /// This class implements all base functionality for loading scan results from an XML file. + /// + public abstract class XmlScanLoaderBase : List + { + /// + /// List of files containing scan results + /// + protected string[] Files { get; } + /// + /// The XML path to the host entries + /// + protected string XmlBaseBath { get; } + /// + /// The scanning source that created the scan results + /// + protected ScanSource Source { get; } + /// + /// List of Nmap states that shall be imported + /// + protected List States { get; } + /// + /// Pointer to the background worker for notification purposes + /// + protected ScanLoaderBackgroundWorker BackgroundWorker { get; set; } + /// + /// Pointer to the background worker-s evant arguments + /// + protected DoWorkEventArgs EventArguments { get; set; } + protected List exceptions { get; set; } + + public XmlScanLoaderBase(string[] files + , List states + , ScanSource scanSource) : base() + { + this.BackgroundWorker = null; + this.EventArguments = null; + this.Files = files; + this.States = states; + this.Source = scanSource; + this.exceptions = new List(); + if (scanSource == ScanSource.nmap) + this.XmlBaseBath = "/nmaprun/host"; + else if (scanSource == ScanSource.nessus) + this.XmlBaseBath = "/NessusClientData_v2/Report/ReportHost"; + else + throw new NotImplementedException(String.Format("Scan source '{0}' not implemented.", scanSource.ToString())); + } + + public List Exceptions + { + get { return this.exceptions; } + } + + #region Helper Methods + /// + /// Returns the total number of hosts in the XML files + /// + public int XmlHostCount + { + get + { + int result = 0; + foreach (var file in this.Files) + { + var doc = new XmlDocument(); + try + { + doc.Load(file); + result += doc.DocumentElement.SelectNodes(this.XmlBaseBath).Count; + } + catch (Exception ex) + { + this.Exceptions.Add(new XmlException("Failed parsing file: " + file, ex)); + } + } + return result; + } + } + + /// + /// This method shall be used to obtain the value of a specific XML tag attribute as string. + /// + /// The XML tag from which a specific attribute value shall be returned. + /// The name of the attribute whose value shall be returned. + /// The value of the attribute name or null if the attribute does not exist. + protected string GetAttributeString(XmlNode node, string name) + { + string result = null; + if (node != null && node.Attributes[name] != null) + { + result = node.Attributes[name].Value; + } + return result; + } + + /// + /// This method shall be used to obtain the value of a specific XML tag attribute as int. + /// + /// The XML tag from which a specific attribute value shall be returned. + /// The name of the attribute whose value shall be returned, + /// The value of the attribute name or null if the attribute does not exist. + protected int GetAttributeInt(XmlNode node, string name) + { + string result = this.GetAttributeString(node, name); + int result_int = 0; + if (!string.IsNullOrEmpty(result)) + result_int = Convert.ToInt32(result); + return result_int; + } + + /// + /// Checks if the BackgroundWorker has been canceled. + /// + /// True if the BackgroundWorker has been canceled. + public bool IsBackgroundWorkerCanceled() + { + bool result = false; + if (this.EventArguments != null && this.EventArguments.Cancel) + result = true; + else if (this.BackgroundWorker != null && this.BackgroundWorker.CancellationPending) + { + this.EventArguments.Cancel = true; + result = true; + } + return result; + } + #endregion + + /// + /// Main method used by the backgroundworker to start import + /// + /// + /// + public void LoadXml(object sender, DoWorkEventArgs e) + { + this.BackgroundWorker = sender as ScanLoaderBackgroundWorker; + this.EventArguments = e; + this.LoadXml(); + } + + /// + /// The method that performs the import + /// + protected void LoadXml() + { + int processedHostCount = 0; + int totalHostCount = this.XmlHostCount; + foreach (var file in this.Files) + { + if (this.IsBackgroundWorkerCanceled()) + break; + this.ParseXml(file, ref processedHostCount, totalHostCount); + } + if (this.IsBackgroundWorkerCanceled()) + this.Clear(); + } + + /// + /// Method that parses the given XML file + /// + /// XML file that shall be parsed. + /// The number of currently processed hosts. This number is + /// used for progress reporting. + /// Total number of hosts to be processed. This number is used + /// for progress reporting. + protected void ParseXml(string file, ref int processedHostCount, int totalHostCount) + { + var doc = new XmlDocument(); + try + { + doc.Load(file); + foreach (XmlNode hostNode in doc.DocumentElement.SelectNodes(this.XmlBaseBath)) + { + if (this.IsBackgroundWorkerCanceled()) + break; + this.ParseXml(hostNode); + processedHostCount += 1; + if (this.BackgroundWorker != null && totalHostCount > 0) + this.BackgroundWorker.ReportProgress(Convert.ToInt32((processedHostCount / (float)totalHostCount) * 100)); + } + } + catch (Exception ex) + { + this.Exceptions.Add(new XmlException("Failed parsing file: " + file, ex)); + } + } + + /// + /// This method parses the XML content of a single host XML node. + /// + /// The XML host node that shall be parsed. + protected abstract void ParseXml(XmlNode hostNode); + } +} diff --git a/SharpBurp/NmapLib/NessusLoader.cs b/SharpBurp/NmapLib/NessusLoader.cs new file mode 100644 index 0000000..6f31132 --- /dev/null +++ b/SharpBurp/NmapLib/NessusLoader.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Xml; + +namespace ScanLib +{ + public class NessusLoader : XmlScanLoaderBase + { + public NessusLoader(string[] files, List states) : base(files, states, ScanSource.nessus) + { + } + + /// + /// This method parses the service XML nodes of the given host XML node. + /// + /// The host XML node whose services shall be parsed. + /// + private List ParseServices(XmlNode nodeHost, string os) + { + var result = new List(); + var dedup = new Dictionary(); + foreach (XmlNode nodePort in nodeHost.SelectNodes("ReportItem")) + { + int port = this.GetAttributeInt(nodePort, "port"); + if (port > 0) + { + string protocol = this.GetAttributeString(nodePort, "protocol"); + int confidence = 10; + string serviceName = this.GetAttributeString(nodePort, "svc_name"); + if (serviceName.EndsWith("?")) + { + serviceName = serviceName.Substring(0, serviceName.Length - 1); + confidence = 3; + } + string serviceNameNew = serviceName == "www" || serviceName == "https" ? "http" : serviceName; + string key = String.Format("{0}/{1}", protocol, serviceName); + if (!dedup.ContainsKey(key)) + { + dedup.Add(key, null); + XmlNode tlsNode = nodeHost.SelectSingleNode(String.Format("ReportItem[@protocol='{0}' and @port='{1}' and " + + "@pluginID='56984' and @pluginName='SSL / TLS Versions Supported']", protocol, port)); + ScanEntry entry = new ScanEntry(protocol + , port + , "open" + , serviceNameNew + , serviceName + , null + , tlsNode != null + , confidence + , os + , this.Source); + result.Add(entry); + } + } + } + return result; + } + + /// + /// This method parses the XML content of a single host XML node. + /// + /// The XML host node that shall be parsed. + protected override void ParseXml(XmlNode nodeHost) + { + var hosts = new List(); + string hostName = this.GetAttributeString(nodeHost, "name"); + string os = nodeHost.SelectSingleNode("HostProperties/tag[@name='os']").InnerText; + string hostIp = nodeHost.SelectSingleNode("HostProperties/tag[@name='host-ip']").InnerText; + hosts.Add(hostName); + if (hostName != hostIp) + hosts.Add(hostIp); + // Obtain all services + List services = this.ParseServices(nodeHost, os); + if (services.Count > 0) + { + foreach (var host in hosts) + { + foreach (var service in services) + { + this.Add(new ScanEntry(host, service)); + } + } + } + } + } +} diff --git a/SharpBurp/NmapLib/NmapLoader.cs b/SharpBurp/NmapLib/NmapLoader.cs index 9a920f1..afa677d 100644 --- a/SharpBurp/NmapLib/NmapLoader.cs +++ b/SharpBurp/NmapLib/NmapLoader.cs @@ -2,203 +2,26 @@ using System.Collections.Generic; using System.Xml; using System.Text.RegularExpressions; -using System.ComponentModel; -namespace NmapLib +namespace ScanLib { - public enum ServiceProtocol - { - tcp, - udp - }; - - public enum ServiceState - { - open, - closed, - filtered, - openFiltered, - closedFiltered - }; - - public class NmapEntry - { - private bool scan; - public string Host { get; set; } - private ServiceProtocol protocol; - private ServiceState state; - public int Port { get; set; } - public bool Tls { get; set; } - private string nmapNameNew { get; set; } - public string NmapNameOriginal { get; } - public string Version { get; } - public int Confidence { get; } - public string OsType { get; } - - public NmapEntry() - { - } - - public NmapEntry(NmapEntry entry) - { - this.Host = entry.Host; - this.Protocol = entry.Protocol; - this.State = entry.State; - this.Port = entry.Port; - this.Tls = entry.Tls; - this.NmapNameNew = entry.NmapNameNew; - this.NmapNameOriginal = entry.NmapNameOriginal; - this.Version = entry.Version; - this.Confidence = entry.Confidence; - this.OsType = entry.OsType; - this.Scan = entry.Scan; - } - - public NmapEntry(string host, NmapEntry entry) : this(entry) - { - this.Host = host; - } - - public NmapEntry(string protocol, int port, string state, string nmapNameNew, string nmapNameOriginal, string version, bool tls, int confidence, string osType) - { - if (protocol == "tcp") - this.Protocol = ServiceProtocol.tcp; - else if (protocol == "udp") - this.Protocol = ServiceProtocol.udp; - else - throw new NotImplementedException(String.Format("ServiceProtocol '{0}' not implemented.", protocol)); - if (state == "open") - this.State = ServiceState.open; - else if (state == "closed") - this.State = ServiceState.closed; - else if (state == "filtered") - this.State = ServiceState.filtered; - else if (state == "open|filtered") - this.State = ServiceState.openFiltered; - else if (state == "closed|filtered") - this.State = ServiceState.closedFiltered; - else - throw new NotImplementedException(String.Format("Service state '{0}' not implemented.", state)); - this.Port = port; - this.NmapNameNew = confidence != 10 && !string.IsNullOrEmpty(nmapNameNew) ? nmapNameNew + "?" : nmapNameNew; - this.NmapNameOriginal = confidence != 10 && !string.IsNullOrEmpty(nmapNameOriginal) ? nmapNameOriginal + "?" : nmapNameOriginal; - this.Version = version; - this.Confidence = confidence; - this.OsType = osType; - this.Tls = tls; - } - - public bool Scan - { - get { return this.scan; } - set { - if (value && !this.IsScanable()) - throw new Exception("Service cannot be scanned."); - this.scan = value; - } - } - - public string NmapNameNew - { - get { return this.nmapNameNew; } - set - { - this.nmapNameNew = value; - this.scan = this.IsScanable(); - } - } - - public ServiceState State - { - get { return this.state; } - set { - this.state = value; - this.scan = this.IsScanable(); - } - } - - public ServiceProtocol Protocol - { - get { return this.protocol; } - set - { - this.protocol = value; - this.scan = this.IsScanable(); - } - } - - public Uri Url - { - get - { - Uri result = null; - if (this.IsScanable()) - { - if ((this.Tls && this.Port == 443) || (!this.Tls && this.Port == 80)) - { - result = new Uri(String.Format("{0}://{1}" - , (this.Tls ? "https" : "http") - , this.Host)); - } - else - { - result = new Uri(String.Format("{0}://{1}:{2}" - , (this.Tls ? "https" : "http") - , this.Host - , this.Port)); - } - } - return result; - } - } - - public bool IsScanable() - { - return this.NmapNameNew == "http" && this.State == ServiceState.open && this.Protocol == ServiceProtocol.tcp; - } - - public bool HasState(List states) - { - return states.Contains(this.State); - } - } - - public class NmapLoader : SortableBindingList + public class NmapLoader : XmlScanLoaderBase { - protected string[] Files { get; } - protected List States { get; } protected Regex HttpResponseRegex { get; } - public NmapLoader(string[] files, List states) + public NmapLoader(string[] files, List states) : base(files, states, ScanSource.nmap) { - this.RaiseListChangedEvents = true; - this.Files = files; - this.States = states; this.HttpResponseRegex = new Regex(@"HTTP/\d+\.\d+ \d{3} [a-zA-Z]+", RegexOptions.Compiled | RegexOptions.IgnoreCase); } - private string GetAttributeString(XmlNode node, string name) - { - string result = null; - if (node != null && node.Attributes[name] != null) - { - result = node.Attributes[name].Value; - } - return result; - } - - private int GetAttributeInt(XmlNode node, string name) - { - string result = this.GetAttributeString(node, name); - int result_int = 0; - if (!string.IsNullOrEmpty(result)) - result_int = Convert.ToInt32(result); - return result_int; - } - - private List ParseServices(XmlNode nodeHost) + /// + /// This method parses the service XML nodes of the given host XML node. + /// + /// The host XML node whose services shall be parsed. + /// + private List ParseServices(XmlNode nodeHost) { - var result = new List(); + var result = new List(); foreach (XmlNode nodePort in nodeHost.SelectNodes("ports/port")) { int port = this.GetAttributeInt(nodePort, "portid"); @@ -208,13 +31,13 @@ private List ParseServices(XmlNode nodeHost) XmlNode nodeScript = nodePort.SelectSingleNode("script[@id='fingerprint-strings']"); string state = this.GetAttributeString(nodeState, "state"); string serviceName = this.GetAttributeString(nodeService, "name"); - string serviceNameNew = serviceName; + string serviceNameNew = this.GetAttributeString(nodeService, "name"); string product = this.GetAttributeString(nodeService, "product"); string tunnel = this.GetAttributeString(nodeService, "tunnel"); string osType = this.GetAttributeString(nodeService, "ostype"); string version = this.GetAttributeString(nodeService, "version"); bool tls = !string.IsNullOrEmpty(tunnel) && tunnel == "ssl"; - int convidence = this.GetAttributeInt(nodeService, "conf"); + int confidence = this.GetAttributeInt(nodeService, "conf"); string productVersion = null; string scriptOutput = this.GetAttributeString(nodeScript, "output"); @@ -227,7 +50,7 @@ private List ParseServices(XmlNode nodeHost) { serviceNameNew = "http"; } - if (!string.IsNullOrEmpty(scriptOutput)) + if (serviceNameNew != "http" && !string.IsNullOrEmpty(scriptOutput)) { if (this.HttpResponseRegex.IsMatch(scriptOutput)) serviceNameNew = "http"; @@ -244,189 +67,63 @@ private List ParseServices(XmlNode nodeHost) { productVersion = version; } - NmapEntry nmapEntry = new NmapEntry(protocol, port, state, serviceNameNew, serviceName, productVersion, tls, convidence, osType); + ScanEntry nmapEntry = new ScanEntry(protocol + , port + , state + , serviceNameNew + , serviceName + , productVersion + , tls + , confidence + , osType + , this.Source); if (nmapEntry.HasState(this.States)) result.Add(nmapEntry); } return result; } - private void ParseXml(string file) + /// + /// This method parses the XML content of a single host XML node. + /// + /// The XML host node that shall be parsed. + protected override void ParseXml(XmlNode hostNode) { - var doc = new XmlDocument(); - doc.Load(file); - foreach (XmlNode hostNode in doc.DocumentElement.SelectNodes("/nmaprun/host")) + var hosts = new List(); + XmlNode nodeStatus = hostNode.SelectSingleNode("status"); + string hostState = this.GetAttributeString(nodeStatus, "state"); + if (hostState == "up") { - var hosts = new List(); - XmlNode nodeStatus = hostNode.SelectSingleNode("status"); - string hostState = this.GetAttributeString(nodeStatus, "state"); - if (hostState == "up") + // Process all IP addresses + foreach (XmlNode nodeAddress in hostNode.SelectNodes("address")) { - // Process all IP addresses - foreach (XmlNode nodeAddress in hostNode.SelectNodes("address")) - { - string ipAddress = this.GetAttributeString(nodeAddress, "addr"); - string ipType = this.GetAttributeString(nodeAddress, "addrtype"); - if (ipType == "ipv6") - ipAddress = String.Format("[{0}]", ipAddress); - hosts.Add(ipAddress); - } - // Process all host names - foreach (XmlNode nodeHostname in hostNode.SelectNodes("hostnames/hostname")) - { - string hostname = this.GetAttributeString(nodeHostname, "name"); - string type = this.GetAttributeString(nodeHostname, "type"); - if (type == "user") - hosts.Add(hostname); - } - // Obtain all services - List services = this.ParseServices(hostNode); - if (services.Count > 0) + string ipAddress = this.GetAttributeString(nodeAddress, "addr"); + string ipType = this.GetAttributeString(nodeAddress, "addrtype"); + if (ipType == "ipv6") + ipAddress = String.Format("[{0}]", ipAddress); + hosts.Add(ipAddress); + } + // Process all host names + foreach (XmlNode nodeHostname in hostNode.SelectNodes("hostnames/hostname")) + { + string hostname = this.GetAttributeString(nodeHostname, "name"); + string type = this.GetAttributeString(nodeHostname, "type"); + if (type == "user") + hosts.Add(hostname); + } + // Obtain all services + List services = this.ParseServices(hostNode); + if (services.Count > 0) + { + foreach (var host in hosts) { - foreach (var host in hosts) + foreach (var service in services) { - foreach (var service in services) - { - this.Add(new NmapEntry(host, service)); - } + this.Add(new ScanEntry(host, service)); } } } } } - - public void LoadXml() - { - foreach (var file in this.Files) - { - this.ParseXml(file); - } - } - } - - - /// - /// Provides a generic collection that supports data binding and additionally supports sorting. - /// See http://msdn.microsoft.com/en-us/library/ms993236.aspx - /// If the elements are IComparable it uses that; otherwise compares the ToString() - /// - /// The type of elements in the list. - public class SortableBindingList : BindingList where T : class - { - private bool _isSorted; - private ListSortDirection _sortDirection = ListSortDirection.Ascending; - private PropertyDescriptor _sortProperty; - - /// - /// Initializes a new instance of the class. - /// - public SortableBindingList() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// An of items to be contained in the . - public SortableBindingList(IList list) - : base(list) - { - } - - /// - /// Gets a value indicating whether the list supports sorting. - /// - protected override bool SupportsSortingCore - { - get { return true; } - } - - /// - /// Gets a value indicating whether the list is sorted. - /// - protected override bool IsSortedCore - { - get { return _isSorted; } - } - - /// - /// Gets the direction the list is sorted. - /// - protected override ListSortDirection SortDirectionCore - { - get { return _sortDirection; } - } - - /// - /// Gets the property descriptor that is used for sorting the list if sorting is implemented in a derived class; otherwise, returns null - /// - protected override PropertyDescriptor SortPropertyCore - { - get { return _sortProperty; } - } - - /// - /// Removes any sort applied with ApplySortCore if sorting is implemented - /// - protected override void RemoveSortCore() - { - _sortDirection = ListSortDirection.Ascending; - _sortProperty = null; - _isSorted = false; //thanks Luca - } - - /// - /// Sorts the items if overridden in a derived class - /// - /// - /// - protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction) - { - _sortProperty = prop; - _sortDirection = direction; - - List list = Items as List; - if (list == null) return; - - list.Sort(Compare); - - _isSorted = true; - //fire an event that the list has been changed. - OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); - } - - - private int Compare(T lhs, T rhs) - { - var result = OnComparison(lhs, rhs); - //invert if descending - if (_sortDirection == ListSortDirection.Descending) - result = -result; - return result; - } - - private int OnComparison(T lhs, T rhs) - { - object lhsValue = lhs == null ? null : _sortProperty.GetValue(lhs); - object rhsValue = rhs == null ? null : _sortProperty.GetValue(rhs); - if (lhsValue == null) - { - return (rhsValue == null) ? 0 : -1; //nulls are equal - } - if (rhsValue == null) - { - return 1; //first has value, second doesn't - } - if (lhsValue is IComparable) - { - return ((IComparable)lhsValue).CompareTo(rhsValue); - } - if (lhsValue.Equals(rhsValue)) - { - return 0; //both are the same - } - //not comparable, compare ToString - return lhsValue.ToString().CompareTo(rhsValue.ToString()); - } } } diff --git a/SharpBurp/NmapLib/Properties/AssemblyInfo.cs b/SharpBurp/NmapLib/Properties/AssemblyInfo.cs index 8a2163b..27c6f4b 100644 --- a/SharpBurp/NmapLib/Properties/AssemblyInfo.cs +++ b/SharpBurp/NmapLib/Properties/AssemblyInfo.cs @@ -5,11 +5,11 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("NmapLib")] +[assembly: AssemblyTitle("ScanLib")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("NmapLib")] +[assembly: AssemblyProduct("ScanLib")] [assembly: AssemblyCopyright("Copyright © 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/SharpBurp/NmapLib/Properties/Settings.Designer.cs b/SharpBurp/NmapLib/Properties/Settings.Designer.cs index ff3ac2d..2bf3b0a 100644 --- a/SharpBurp/NmapLib/Properties/Settings.Designer.cs +++ b/SharpBurp/NmapLib/Properties/Settings.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace NmapLib.Properties { +namespace ScanLib.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] diff --git a/SharpBurp/NmapLib/NmapLib.csproj b/SharpBurp/NmapLib/ScanLib.csproj similarity index 97% rename from SharpBurp/NmapLib/NmapLib.csproj rename to SharpBurp/NmapLib/ScanLib.csproj index 3012dbf..19704dc 100644 --- a/SharpBurp/NmapLib/NmapLib.csproj +++ b/SharpBurp/NmapLib/ScanLib.csproj @@ -40,6 +40,8 @@ + + diff --git a/SharpBurp/SharpBurp.sln b/SharpBurp/SharpBurp.sln index 227380d..0afe1e6 100644 --- a/SharpBurp/SharpBurp.sln +++ b/SharpBurp/SharpBurp.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 15.0.27703.2035 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpBurp", "SharpBurp\SharpBurp.csproj", "{D8113C9E-1E8F-4E44-A1B4-479D0EA6202E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NmapLib", "NmapLib\NmapLib.csproj", "{DAD48919-39E8-4E97-A529-5F4889237B66}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScanLib", "NmapLib\ScanLib.csproj", "{DAD48919-39E8-4E97-A529-5F4889237B66}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BurpSuiteLib", "BurpSuiteLib\BurpSuiteLib.csproj", "{14B7F046-C579-4FFE-A46C-2C58A1FFD595}" EndProject diff --git a/SharpBurp/SharpBurp/SharpBurp.Designer.cs b/SharpBurp/SharpBurp/SharpBurp.Designer.cs index 84e10d9..e08294a 100644 --- a/SharpBurp/SharpBurp/SharpBurp.Designer.cs +++ b/SharpBurp/SharpBurp/SharpBurp.Designer.cs @@ -29,7 +29,9 @@ protected override void Dispose(bool disposing) private void InitializeComponent() { this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SharpBurp)); this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.loadNessus = new System.Windows.Forms.Button(); this.chunkSize = new System.Windows.Forms.NumericUpDown(); this.label6 = new System.Windows.Forms.Label(); this.ImportFiltered = new System.Windows.Forms.CheckBox(); @@ -65,6 +67,7 @@ private void InitializeComponent() this.confidenceDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.osTypeDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.urlDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewLinkColumn(); + this.Source = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.nmapResults = new System.Windows.Forms.BindingSource(this.components); this.logMessages = new System.Windows.Forms.TextBox(); this.contextMenuTable = new System.Windows.Forms.ContextMenuStrip(this.components); @@ -76,6 +79,7 @@ private void InitializeComponent() this.statusMessage = new System.Windows.Forms.ToolStripStatusLabel(); this.statusRowCount = new System.Windows.Forms.ToolStripStatusLabel(); this.progressBar = new System.Windows.Forms.ToolStripProgressBar(); + this.cancelWorker = new System.Windows.Forms.ToolStripDropDownButton(); this.groupBox1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.chunkSize)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); @@ -92,6 +96,7 @@ private void InitializeComponent() // this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.groupBox1.Controls.Add(this.loadNessus); this.groupBox1.Controls.Add(this.chunkSize); this.groupBox1.Controls.Add(this.label6); this.groupBox1.Controls.Add(this.ImportFiltered); @@ -120,6 +125,16 @@ private void InitializeComponent() this.groupBox1.TabStop = false; this.groupBox1.Text = "Configuration"; // + // loadNessus + // + this.loadNessus.Location = new System.Drawing.Point(449, 285); + this.loadNessus.Name = "loadNessus"; + this.loadNessus.Size = new System.Drawing.Size(224, 50); + this.loadNessus.TabIndex = 13; + this.loadNessus.Text = "Load &Nessus"; + this.loadNessus.UseVisualStyleBackColor = true; + this.loadNessus.Click += new System.EventHandler(this.loadNessus_Click); + // // chunkSize // this.chunkSize.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); @@ -241,30 +256,30 @@ private void InitializeComponent() // // exportCsv // - this.exportCsv.Location = new System.Drawing.Point(1046, 285); + this.exportCsv.Location = new System.Drawing.Point(1177, 285); this.exportCsv.Name = "exportCsv"; - this.exportCsv.Size = new System.Drawing.Size(260, 50); - this.exportCsv.TabIndex = 15; + this.exportCsv.Size = new System.Drawing.Size(224, 50); + this.exportCsv.TabIndex = 16; this.exportCsv.Text = "&Export to Excel"; this.exportCsv.UseVisualStyleBackColor = true; this.exportCsv.Click += new System.EventHandler(this.exportCsv_Click); // // clearTable // - this.clearTable.Location = new System.Drawing.Point(487, 285); + this.clearTable.Location = new System.Drawing.Point(693, 285); this.clearTable.Name = "clearTable"; - this.clearTable.Size = new System.Drawing.Size(260, 50); - this.clearTable.TabIndex = 13; + this.clearTable.Size = new System.Drawing.Size(224, 50); + this.clearTable.TabIndex = 14; this.clearTable.Text = "&Clear Table"; this.clearTable.UseVisualStyleBackColor = true; this.clearTable.Click += new System.EventHandler(this.clearTable_Click); // // sendBurp // - this.sendBurp.Location = new System.Drawing.Point(768, 285); + this.sendBurp.Location = new System.Drawing.Point(935, 285); this.sendBurp.Name = "sendBurp"; - this.sendBurp.Size = new System.Drawing.Size(260, 50); - this.sendBurp.TabIndex = 14; + this.sendBurp.Size = new System.Drawing.Size(224, 50); + this.sendBurp.TabIndex = 15; this.sendBurp.Text = "&Send To Burp API"; this.sendBurp.UseVisualStyleBackColor = true; this.sendBurp.Click += new System.EventHandler(this.sendBurp_Click); @@ -273,7 +288,7 @@ private void InitializeComponent() // this.loadNmap.Location = new System.Drawing.Point(207, 285); this.loadNmap.Name = "loadNmap"; - this.loadNmap.Size = new System.Drawing.Size(260, 50); + this.loadNmap.Size = new System.Drawing.Size(224, 50); this.loadNmap.TabIndex = 12; this.loadNmap.Text = "&Load Nmap XML"; this.loadNmap.UseVisualStyleBackColor = true; @@ -376,7 +391,8 @@ private void InitializeComponent() this.versionDataGridViewTextBoxColumn, this.confidenceDataGridViewTextBoxColumn, this.osTypeDataGridViewTextBoxColumn, - this.urlDataGridViewTextBoxColumn}); + this.urlDataGridViewTextBoxColumn, + this.Source}); this.services.DataSource = this.nmapResults; this.services.Location = new System.Drawing.Point(3, -7); this.services.Name = "services"; @@ -385,7 +401,7 @@ private void InitializeComponent() this.services.TabIndex = 0; this.services.CellContentDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.services_CellContentDoubleClick); this.services.CellValidating += new System.Windows.Forms.DataGridViewCellValidatingEventHandler(this.services_CellValidating); - this.services.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.services_CellValueChanged); + this.services.RowValidating += new System.Windows.Forms.DataGridViewCellCancelEventHandler(this.services_RowValidating); this.services.UserAddedRow += new System.Windows.Forms.DataGridViewRowEventHandler(this.services_UserAddedRow); this.services.UserDeletedRow += new System.Windows.Forms.DataGridViewRowEventHandler(this.services_UserDeletedRow); this.services.MouseClick += new System.Windows.Forms.MouseEventHandler(this.services_MouseClick); @@ -441,17 +457,17 @@ private void InitializeComponent() // nmapNameNewDataGridViewTextBoxColumn // this.nmapNameNewDataGridViewTextBoxColumn.DataPropertyName = "NmapNameNew"; - this.nmapNameNewDataGridViewTextBoxColumn.HeaderText = "Nmap Name New"; + this.nmapNameNewDataGridViewTextBoxColumn.HeaderText = "Service Name New"; this.nmapNameNewDataGridViewTextBoxColumn.Name = "nmapNameNewDataGridViewTextBoxColumn"; - this.nmapNameNewDataGridViewTextBoxColumn.Width = 167; + this.nmapNameNewDataGridViewTextBoxColumn.Width = 181; // // nmapNameOriginalDataGridViewTextBoxColumn // this.nmapNameOriginalDataGridViewTextBoxColumn.DataPropertyName = "NmapNameOriginal"; - this.nmapNameOriginalDataGridViewTextBoxColumn.HeaderText = "Nmap Name Original"; + this.nmapNameOriginalDataGridViewTextBoxColumn.HeaderText = "Service Name Original"; this.nmapNameOriginalDataGridViewTextBoxColumn.Name = "nmapNameOriginalDataGridViewTextBoxColumn"; this.nmapNameOriginalDataGridViewTextBoxColumn.ReadOnly = true; - this.nmapNameOriginalDataGridViewTextBoxColumn.Width = 233; + this.nmapNameOriginalDataGridViewTextBoxColumn.Width = 248; // // versionDataGridViewTextBoxColumn // @@ -488,10 +504,18 @@ private void InitializeComponent() this.urlDataGridViewTextBoxColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; this.urlDataGridViewTextBoxColumn.Width = 99; // + // Source + // + this.Source.DataPropertyName = "Source"; + this.Source.HeaderText = "Source"; + this.Source.Name = "Source"; + this.Source.ReadOnly = true; + this.Source.Width = 125; + // // nmapResults // this.nmapResults.AllowNew = true; - this.nmapResults.DataSource = typeof(NmapLib.NmapEntry); + this.nmapResults.DataSource = typeof(ScanLib.ScanEntry); // // logMessages // @@ -502,6 +526,7 @@ private void InitializeComponent() this.logMessages.Multiline = true; this.logMessages.Name = "logMessages"; this.logMessages.ReadOnly = true; + this.logMessages.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.logMessages.Size = new System.Drawing.Size(1570, 129); this.logMessages.TabIndex = 0; // @@ -550,7 +575,8 @@ private void InitializeComponent() this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.statusMessage, this.statusRowCount, - this.progressBar}); + this.progressBar, + this.cancelWorker}); this.statusStrip1.Location = new System.Drawing.Point(0, 1144); this.statusStrip1.Name = "statusStrip1"; this.statusStrip1.Size = new System.Drawing.Size(1594, 38); @@ -574,6 +600,16 @@ private void InitializeComponent() this.progressBar.Name = "progressBar"; this.progressBar.Size = new System.Drawing.Size(100, 32); // + // cancelWorker + // + this.cancelWorker.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.cancelWorker.Image = ((System.Drawing.Image)(resources.GetObject("cancelWorker.Image"))); + this.cancelWorker.ImageTransparentColor = System.Drawing.Color.Magenta; + this.cancelWorker.Name = "cancelWorker"; + this.cancelWorker.Size = new System.Drawing.Size(54, 36); + this.cancelWorker.Text = "toolStripDropDownButton1"; + this.cancelWorker.Click += new System.EventHandler(this.cancelWorker_Click); + // // SharpBurp // this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 25F); @@ -643,6 +679,8 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripStatusLabel statusRowCount; private System.Windows.Forms.ToolStripProgressBar progressBar; private System.Windows.Forms.BindingSource nmapResults; + private System.Windows.Forms.ToolStripDropDownButton cancelWorker; + private System.Windows.Forms.Button loadNessus; private System.Windows.Forms.DataGridViewCheckBoxColumn scanDataGridViewCheckBoxColumn; private System.Windows.Forms.DataGridViewTextBoxColumn hostDataGridViewTextBoxColumn; private System.Windows.Forms.DataGridViewComboBoxColumn protocolDataGridViewTextBoxColumn; @@ -655,6 +693,7 @@ private void InitializeComponent() private System.Windows.Forms.DataGridViewTextBoxColumn confidenceDataGridViewTextBoxColumn; private System.Windows.Forms.DataGridViewTextBoxColumn osTypeDataGridViewTextBoxColumn; private System.Windows.Forms.DataGridViewLinkColumn urlDataGridViewTextBoxColumn; + private System.Windows.Forms.DataGridViewTextBoxColumn Source; } } diff --git a/SharpBurp/SharpBurp/SharpBurp.cs b/SharpBurp/SharpBurp/SharpBurp.cs index 69e1169..649ce19 100644 --- a/SharpBurp/SharpBurp/SharpBurp.cs +++ b/SharpBurp/SharpBurp/SharpBurp.cs @@ -3,7 +3,7 @@ using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; -using NmapLib; +using ScanLib; using SharpBurp.Properties; using System.Security.Cryptography; using BurpSuiteLib; @@ -16,12 +16,21 @@ namespace SharpBurp public partial class SharpBurp : Form { readonly Encoding _encoding = Encoding.Unicode; + ScanLoaderBackgroundWorker NmapLoaderBackgroundWorker = new ScanLoaderBackgroundWorker(); + BackgroundWorker ExcelExportBackgroundWorker = new BackgroundWorker(); public SharpBurp() { InitializeComponent(); this.protocolDataGridViewTextBoxColumn.DataSource = Enum.GetValues(typeof(ServiceProtocol)); this.stateDataGridViewTextBoxColumn.DataSource = Enum.GetValues(typeof(ServiceState)); + this.nmapResults.DataSource = new SortableBindingList(); + this.cancelWorker.Visible = false; + this.ExcelExportBackgroundWorker.WorkerSupportsCancellation = true; + this.ExcelExportBackgroundWorker.WorkerReportsProgress = true; + this.ExcelExportBackgroundWorker.DoWork += new DoWorkEventHandler(this.DoExcelExport); + this.ExcelExportBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.ExcelExportCompleted); + this.ExcelExportBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(ExcelExportProgressChanged); } #region Helper Methods @@ -41,9 +50,10 @@ public void LogMessage(Exception ex) /// The message that shall be reported public void LogMessage(string message) { - string logMessage = String.Format("{0}: {1}\n" + string logMessage = String.Format("{0}: {1}{2}" , DateTime.Now.ToString("MM/dd/yyyy hh:mm") - , message); + , message + , Environment.NewLine); this.logMessages.AppendText(logMessage); } @@ -53,7 +63,7 @@ public void LogMessage(string message) /// public void UpdateServiceCount() { - BindingList list = this.nmapResults.DataSource as BindingList; + BindingList list = this.nmapResults.DataSource as BindingList; if (list != null) { int count = (from item in list where item.Scan select item).Count(); @@ -61,6 +71,10 @@ public void UpdateServiceCount() } } + /// + /// This method verifies the states of the Nmap service state checkboxes. + /// + /// Returns list of ServiceStates that are checked in the GUI. private List GetStates() { var result = new List(); @@ -151,34 +165,73 @@ private bool InputsComplete() #endregion #region Button Events - private void loadNmap_Click(object sender, EventArgs e) + private void loadNessus_Click(object sender, EventArgs e) { var states = this.GetStates(); using (var openFileDialog = new OpenFileDialog()) { try { - openFileDialog.Filter = "Nmap XML Result File (*.xml)|*.*"; - openFileDialog.Title = "Open Nmap XML Scan Results"; - openFileDialog.Multiselect = true; - openFileDialog.FilterIndex = 2; - openFileDialog.RestoreDirectory = true; + if (!this.NmapLoaderBackgroundWorker.IsBusy) + { + openFileDialog.Filter = "Nessus Result File (*.nessus)|*.*"; + openFileDialog.Title = "Open Nessus Scan Results"; + openFileDialog.Multiselect = true; + openFileDialog.FilterIndex = 2; + openFileDialog.RestoreDirectory = true; + + if (openFileDialog.ShowDialog() == DialogResult.OK) + { + this.services.Enabled = false; + this.cancelWorker.Visible = true; + this.progressBar.Value = 0; + this.progressBar.Maximum = 100; + this.statusMessage.Text = "Import started"; + var loader = new NessusLoader(openFileDialog.FileNames, states); + this.NmapLoaderBackgroundWorker = new ScanLoaderBackgroundWorker(loader); + this.NmapLoaderBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(ScanLoaderCompleted); + this.NmapLoaderBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(ScanLoaderProgressChanged); + this.NmapLoaderBackgroundWorker.RunWorkerAsync(); + } + } + } + catch (Exception ex) + { + this.LogMessage(ex); + } + } + } - if (openFileDialog.ShowDialog() == DialogResult.OK) + private void loadNmap_Click(object sender, EventArgs e) + { + var states = this.GetStates(); + using (var openFileDialog = new OpenFileDialog()) + { + try + { + if (!this.NmapLoaderBackgroundWorker.IsBusy) { - this.statusMessage.Text = "Loading started"; - var loader = new NmapLoader(openFileDialog.FileNames, states); - loader.LoadXml(); - this.nmapResults.DataSource = loader; - MessageBox.Show(this - , "Nmap XML scan results import successfully completed." - , "Complete ..." - , MessageBoxButtons.OK - , MessageBoxIcon.Information); - this.statusMessage.Text = "Loading completed"; - this.UpdateServiceCount(); + openFileDialog.Filter = "Nmap XML Result File (*.xml)|*.*"; + openFileDialog.Title = "Open Nmap XML Scan Results"; + openFileDialog.Multiselect = true; + openFileDialog.FilterIndex = 2; + openFileDialog.RestoreDirectory = true; + + if (openFileDialog.ShowDialog() == DialogResult.OK) + { + this.services.Enabled = false; + this.cancelWorker.Visible = true; + this.progressBar.Value = 0; + this.progressBar.Maximum = 100; + this.statusMessage.Text = "Import started"; + var loader = new NmapLoader(openFileDialog.FileNames, states); + this.NmapLoaderBackgroundWorker = new ScanLoaderBackgroundWorker(loader); + this.NmapLoaderBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(ScanLoaderCompleted); + this.NmapLoaderBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(ScanLoaderProgressChanged); + this.NmapLoaderBackgroundWorker.RunWorkerAsync(); + } } - } + } catch (Exception ex) { this.LogMessage(ex); @@ -199,62 +252,15 @@ private void exportCsv_Click(object sender, EventArgs e) saveFileDialog.Filter = "Microsoft Excel File (*.xlsx)|*.*"; saveFileDialog.Title = "Save Microsoft Excel File"; - if (saveFileDialog.ShowDialog() == DialogResult.OK && !string.IsNullOrEmpty(saveFileDialog.FileName)) + if (!this.ExcelExportBackgroundWorker.IsBusy && + saveFileDialog.ShowDialog() == DialogResult.OK && !string.IsNullOrEmpty(saveFileDialog.FileName)) { - try - { - this.statusMessage.Text = "Export started"; - var excelApp = new Microsoft.Office.Interop.Excel.Application(); - if (excelApp != null) - { - var excelWorkbook = excelApp.Workbooks.Add(1); - var excelWorksheet = (Microsoft.Office.Interop.Excel.Worksheet)excelApp.Worksheets[1]; - uint column_index = 1; - // Add header information - foreach (DataGridViewColumn item in this.services.Columns) - { - excelWorksheet.Cells[1, column_index] = item.HeaderText; - column_index += 1; - } - // Add rows - int row_index = 2; - this.progressBar.Maximum = this.services.Rows.Count; - this.progressBar.Value = 0; - foreach (DataGridViewRow row in this.services.Rows) - { - if (row.IsNewRow) continue; - column_index = 1; - foreach (DataGridViewCell item in row.Cells) - { - excelWorksheet.Cells[row_index, column_index] = item.Value != null ? item.Value.ToString() : ""; - column_index += 1; - } - row_index += 1; - this.progressBar.Value += 1; - } - excelWorkbook.SaveAs(saveFileDialog.FileName); - Marshal.ReleaseComObject(excelWorkbook); - Marshal.ReleaseComObject(excelApp); - MessageBox.Show(this - , "Microsoft Excel export successfully completed." - , "Complete ..." - , MessageBoxButtons.OK - , MessageBoxIcon.Information); - this.statusMessage.Text = "Export completed"; - } - } - catch (Exception ex) - { - this.LogMessage(ex); - } - } - else - { - MessageBox.Show(this - , "Microsoft Excel is not properly installed." - , "Microsoft Excel not working ..." - , MessageBoxButtons.OK - , MessageBoxIcon.Error); + if (System.IO.File.Exists(saveFileDialog.FileName)) + System.IO.File.Delete(saveFileDialog.FileName); + this.progressBar.Maximum = 100; + this.cancelWorker.Visible = true; + this.progressBar.Value = 0; + this.ExcelExportBackgroundWorker.RunWorkerAsync(saveFileDialog.FileName.ToString()); } } } @@ -271,10 +277,10 @@ private void sendBurp_Click(object sender, EventArgs e) , this.scanConfiguration.Text , this.resourcePool.Text , (int)this.chunkSize.Value); - BindingList results = this.nmapResults.DataSource as BindingList; + BindingList results = this.nmapResults.DataSource as BindingList; this.statusMessage.Text = "Task started"; List urls = new List(); - foreach (NmapEntry entry in results) + foreach (ScanLib.ScanEntry entry in results) { if (entry.Scan) urls.Add(entry.Url); @@ -293,6 +299,18 @@ private void sendBurp_Click(object sender, EventArgs e) } } } + + private void cancelWorker_Click(object sender, EventArgs e) + { + if (this.NmapLoaderBackgroundWorker.WorkerSupportsCancellation) + { + this.NmapLoaderBackgroundWorker.CancelAsync(); + } + if (this.ExcelExportBackgroundWorker.WorkerSupportsCancellation) + { + this.ExcelExportBackgroundWorker.CancelAsync(); + } + } #endregion #region Form Loading and Closing Events @@ -358,8 +376,8 @@ private void UpdateRows(bool scan) this.statusMessage.Text = "Update rows"; this.progressBar.Maximum = this.nmapResults.Count; this.progressBar.Value = 0; - BindingList results = this.nmapResults.DataSource as BindingList; - foreach (NmapEntry row in results) + BindingList results = this.nmapResults.DataSource as BindingList; + foreach (ScanLib.ScanEntry row in results) { if (row.Scan != scan && row.IsScanable()) row.Scan = scan; @@ -379,14 +397,15 @@ private void UpdateRows(DataGridViewSelectedRowCollection rows, bool scan) { try { - BindingList results = this.nmapResults.DataSource as BindingList; + BindingList results = this.nmapResults.DataSource as BindingList; this.statusMessage.Text = "Update rows"; this.progressBar.Maximum = rows.Count; this.progressBar.Value = 0; foreach (DataGridViewRow row in rows) { if (row.IsNewRow) continue; - NmapEntry entry = row.DataBoundItem as NmapEntry; + ScanLib.ScanEntry entry = row.DataBoundItem as ScanLib.ScanEntry; + if (entry.Scan != scan && entry.IsScanable()) entry.Scan = scan; this.progressBar.Value += 1; @@ -439,7 +458,7 @@ private void services_UserAddedRow(object sender, DataGridViewRowEventArgs e) this.UpdateServiceCount(); } - private void services_CellValueChanged(object sender, DataGridViewCellEventArgs e) + private void services_RowValidating(object sender, DataGridViewCellCancelEventArgs e) { this.UpdateServiceCount(); } @@ -452,16 +471,16 @@ private void services_CellValueChanged(object sender, DataGridViewCellEventArgs private void services_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) { bool outScan; - BindingList results = this.nmapResults.DataSource as BindingList; + BindingList results = this.nmapResults.DataSource as BindingList; if (results != null - && e.ColumnIndex == 0 - && (!bool.TryParse(e.FormattedValue.ToString(), out outScan) + && e.ColumnIndex == 0 + && (!bool.TryParse(e.FormattedValue.ToString(), out outScan) || outScan) && (!results[e.RowIndex].IsScanable())) { e.Cancel = true; MessageBox.Show(this, "Row is not an active web application and therefore cannot be scanned. " + - "Make sure that column 'Nmap Name New' contains 'http', 'ServiceProtocol' contains 'tcp' and 'State'" + + "Make sure that column 'Service Name New' contains 'http', 'Protocol' contains 'tcp' and 'State'" + "contains 'open'." , "Row is not a web application ..." , MessageBoxButtons.OK @@ -476,7 +495,7 @@ private void services_CellValidating(object sender, DataGridViewCellValidatingEv /// private void services_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e) { - BindingList results = this.nmapResults.DataSource as BindingList; + BindingList results = this.nmapResults.DataSource as BindingList; if (results != null && e.ColumnIndex == (this.services.Columns.Count - 1)) { @@ -484,5 +503,153 @@ private void services_CellContentDoubleClick(object sender, DataGridViewCellEven } } #endregion + + #region ScanLoader BackgroundWorker + private void ScanLoaderCompleted(object sender, RunWorkerCompletedEventArgs e) + { + ScanLoaderBackgroundWorker backgroundWorker = sender as ScanLoaderBackgroundWorker; + SortableBindingList results = this.nmapResults.DataSource as SortableBindingList; + + if (backgroundWorker != null && results != null) + { + if (e.Cancelled) + { + MessageBox.Show(this + , "Scan import canceled." + , "Import canceled ..." + , MessageBoxButtons.OK + , MessageBoxIcon.Information); + this.statusMessage.Text = "Import completed"; + } + else + { + try + { + this.cancelWorker.Visible = false; + // Add parsed items to DataGridView + this.progressBar.Maximum = backgroundWorker.ScanLoader.Count; + this.progressBar.Step = 1; + this.progressBar.Value = 0; + // Report caught exceptions + foreach (var exception in backgroundWorker.ScanLoader.Exceptions) + { + this.LogMessage(exception); + } + // Update table + foreach (ScanLib.ScanEntry item in backgroundWorker.ScanLoader) + { + results.Add(item); + this.progressBar.PerformStep(); + } + this.statusMessage.Text = "Import completed"; + this.UpdateServiceCount(); + MessageBox.Show(this + , "Scan results import successfully completed." + , "Import complete ..." + , MessageBoxButtons.OK + , MessageBoxIcon.Information); + } + catch (Exception ex) + { + this.LogMessage(ex); + MessageBox.Show(this + , "Loading scan results failed" + , "Import failed ..." + , MessageBoxButtons.OK + , MessageBoxIcon.Error); + } + } + } + this.progressBar.Value = 0; + this.services.Enabled = true; + } + + private void ScanLoaderProgressChanged(object sender, ProgressChangedEventArgs e) + { + int percent = e.ProgressPercentage; + this.progressBar.Value = percent <= 100 ? percent : 100; + } + #endregion + + #region Excel Export BackgroundWorker + public void DoExcelExport(object sender, DoWorkEventArgs e) + { + var excelApp = new Microsoft.Office.Interop.Excel.Application(); + string fileName = e.Argument.ToString(); + if (excelApp != null) + { + var excelWorkbook = excelApp.Workbooks.Add(1); + var excelWorksheet = (Microsoft.Office.Interop.Excel.Worksheet)excelApp.Worksheets[1]; + uint column_index = 1; + // Add header information + foreach (DataGridViewColumn item in this.services.Columns) + { + excelWorksheet.Cells[1, column_index] = item.HeaderText; + column_index += 1; + } + // Add rows + int row_index = 2; + foreach (DataGridViewRow row in this.services.Rows) + { + if (this.ExcelExportBackgroundWorker.CancellationPending) + { + e.Cancel = true; + break; + } + if (row.IsNewRow) continue; + column_index = 1; + foreach (DataGridViewCell item in row.Cells) + { + excelWorksheet.Cells[row_index, column_index] = item.Value != null ? item.Value.ToString() : ""; + column_index += 1; + } + row_index += 1; + this.ExcelExportBackgroundWorker.ReportProgress(Convert.ToInt32((row_index/(float)this.services.Rows.Count) * 100)); + } + if (!e.Cancel) + excelWorkbook.SaveAs(fileName); + excelApp.DisplayAlerts = false; + excelApp.Workbooks.Close(); + excelApp.Quit(); + Marshal.ReleaseComObject(excelWorkbook); + Marshal.ReleaseComObject(excelApp); + } + } + + private void ExcelExportCompleted(object sender, RunWorkerCompletedEventArgs e) + { + if (e.Cancelled) + { + MessageBox.Show(this + , "Microsoft Excel export canceled." + , "Canceled ..." + , MessageBoxButtons.OK + , MessageBoxIcon.Information); + this.statusMessage.Text = "Excel export canceled"; + } + else if (e.Error != null) + { + this.LogMessage(e.Error); + this.statusMessage.Text = "Excel export failed"; + } + else + { + MessageBox.Show(this + , "Microsoft Excel export successfully completed." + , "Complete ..." + , MessageBoxButtons.OK + , MessageBoxIcon.Information); + this.statusMessage.Text = "Excel export completed"; + } + this.cancelWorker.Visible = false; + this.progressBar.Value = 0; + } + + private void ExcelExportProgressChanged(object sender, ProgressChangedEventArgs e) + { + int percent = e.ProgressPercentage; + this.progressBar.Value = percent <= 100 ? percent : 100; + } + #endregion } } diff --git a/SharpBurp/SharpBurp/SharpBurp.csproj b/SharpBurp/SharpBurp/SharpBurp.csproj index 0d09e6c..e7850ab 100644 --- a/SharpBurp/SharpBurp/SharpBurp.csproj +++ b/SharpBurp/SharpBurp/SharpBurp.csproj @@ -55,6 +55,7 @@ + SharpBurp.cs @@ -86,9 +87,9 @@ {14b7f046-c579-4ffe-a46c-2c58a1ffd595} BurpSuiteLib - + {dad48919-39e8-4e97-a529-5f4889237b66} - NmapLib + ScanLib diff --git a/SharpBurp/SharpBurp/SharpBurp.resx b/SharpBurp/SharpBurp/SharpBurp.resx index 1d889ca..429a69e 100644 --- a/SharpBurp/SharpBurp/SharpBurp.resx +++ b/SharpBurp/SharpBurp/SharpBurp.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + True + 491, 17 @@ -126,4 +129,30 @@ 291, 17 + + + + iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAAHYcAAB2HAY/l8WUAAAR4SURBVFhHvZdPiFVVHMcHB2qjbqZFhJshwaGmzAYsBkdk + MCUdTDNNozBqYhhhRPEPGOXCRbRoE8xGBiJBcFGLgihaRESCGgjhpiIEcXgz79mfcdSMkXkcf5/fu9/7 + zr3v3nkzLTrwnXvfPfee7+f3O+f87twOtfB/tZnGIbFtNC78+9N3oXZqOFTe3hIqbw2GyoGNYXL/c2Fy + b1+q67ufccW/b+x6MtX1odUturZ5levXgS7Xld194dpXnzUhOJn75UqojGwLN0+Phr/G3w9/fnTMzwGq + nTwQasdfC9Wj+8L0oZebGnspTB3ckWibP195pwkvEQSgv+/sdZCLA4+Gb59d2YTgDwZ/fDAWZs+Pp5r5 + 5MMUBtGfQknASYI8sicCNCgyCohlDIiftz4evul/zMV0OAD0mGF858uzKQTXiiCkDIyADGT6xP4UwjNj + WVEmBDDRsxzrJgAGMUBZBjLGuQxkpkoZSKZl8vV+B7g69EQBgN2ACaaxOcJY5hwBZM3U5+74ALT6/Xt+ + jWcByK8HzJUBAH4c7M4B2E0YYBibA6WIMY5Nyxr3ACLTeCexBgTw6douv98BuJk0Yijj2JwtqjZ/s+Iw + 9GnLsvAAp0/t7vdfeMR5FQMYJQCkeCFzjNl2nuK4XhBtEiUgaoLwGmHH8gwYAHOn+dacY6hGWrXvfX4T + gDTVZq4oyYgaQCpU5QD2MA9hKgHB4qIBwspOo08WmFoMIDNlYv6fGe9vD2AGvp2SLabomVcVl3z61fLm + KsNaE8pCKQAPM3i6p01sK5qiV+ktzIABIAFUdjYAWEs0xuJ3KYA/ZAOzDrygGIDoGcQzsBCApbiy7+kM + AKoOP+/9TIOgqIStGbAOBiVSIJCa0p+ZghIAV2KOpnasSu6wWtMWwAYkSjc0qaUAi8gAqr66JjWPATiP + ATKVkA62E7Xbzcx0/nbNb/ApiDLg94xatkY2eT+t+mZfC0BtT3eojb3o/UyBgAoBeE97FBYZJkRbtgjL + AFDtjV4HcHPT7Jl3vZ9ihrn+MSkGsFVMWkkxRhQemm/DFIC+9gCC0EK+9fHhUN3+iEsA51Z3el8K4KvX + yqrv8WRH6OXjWVgCAOaNQlT39PNb5oUAXATAp4EssMgMIq7rZMSNJQNAbLU8ABFj7s/ZNGA+9cIKVymA + L0QKii1GQRBt/mXUYp4AYI6a0HV/GSn1mN/Y8FD4bf3DxQBMAxXRs8BUKBOm/OuYbFCuBfD3e6+4cWPO + ibzVPAa48FRnmOhe5uM5ABe1DgThmRCICYPF/EPCnJN2Vn0+enR1XWc5ANNQCJG888kGIGREK5zGOdcw + 9qlIFl0++gUB0mmIIVgTgEjJS8e3bFJ4iqpfmTk+hQBcBEAQVCveXIsRwBLPYswxb67o0dc9y7IAfC5B + dXn9Cv9yQUAsRkDrGIuxiFhRK3LEDvAPEwFMX/ohfN673MVnE9LXy0LSfXqmnYgcc6Jv+T4EgsHopE6n + xyWIwWWA4nONh0fGXM2v/KfW2PcNtWt2T+bzvKPjAaxoHnXzLO/gAAAAAElFTkSuQmCC + + \ No newline at end of file diff --git a/SharpBurp/SharpBurp/SortableBindingList.cs b/SharpBurp/SharpBurp/SortableBindingList.cs new file mode 100644 index 0000000..e80c1b2 --- /dev/null +++ b/SharpBurp/SharpBurp/SortableBindingList.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; + +namespace SharpBurp +{ + /// + /// Provides a generic collection that supports data binding and additionally supports sorting. + /// See http://msdn.microsoft.com/en-us/library/ms993236.aspx + /// If the elements are IComparable it uses that; otherwise compares the ToString() + /// + /// The type of elements in the list. + public class SortableBindingList : BindingList where T : class + { + private bool _isSorted; + private ListSortDirection _sortDirection = ListSortDirection.Ascending; + private PropertyDescriptor _sortProperty; + + /// + /// Initializes a new instance of the class. + /// + public SortableBindingList() + { + this.RaiseListChangedEvents = true; + } + + /// + /// Initializes a new instance of the class. + /// + /// An of items to be contained in the . + public SortableBindingList(IList list) + : base(list) + { + this.RaiseListChangedEvents = true; + } + + /// + /// Gets a value indicating whether the list supports sorting. + /// + protected override bool SupportsSortingCore + { + get { return true; } + } + + /// + /// Gets a value indicating whether the list is sorted. + /// + protected override bool IsSortedCore + { + get { return _isSorted; } + } + + /// + /// Gets the direction the list is sorted. + /// + protected override ListSortDirection SortDirectionCore + { + get { return _sortDirection; } + } + + /// + /// Gets the property descriptor that is used for sorting the list if sorting is implemented in a derived class; otherwise, returns null + /// + protected override PropertyDescriptor SortPropertyCore + { + get { return _sortProperty; } + } + + /// + /// Removes any sort applied with ApplySortCore if sorting is implemented + /// + protected override void RemoveSortCore() + { + _sortDirection = ListSortDirection.Ascending; + _sortProperty = null; + _isSorted = false; //thanks Luca + } + + /// + /// Sorts the items if overridden in a derived class + /// + /// + /// + protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction) + { + _sortProperty = prop; + _sortDirection = direction; + + List list = Items as List; + if (list == null) return; + + list.Sort(Compare); + + _isSorted = true; + //fire an event that the list has been changed. + OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); + } + + + private int Compare(T lhs, T rhs) + { + var result = OnComparison(lhs, rhs); + //invert if descending + if (_sortDirection == ListSortDirection.Descending) + result = -result; + return result; + } + + private int OnComparison(T lhs, T rhs) + { + object lhsValue = lhs == null ? null : _sortProperty.GetValue(lhs); + object rhsValue = rhs == null ? null : _sortProperty.GetValue(rhs); + if (lhsValue == null) + { + return (rhsValue == null) ? 0 : -1; //nulls are equal + } + if (rhsValue == null) + { + return 1; //first has value, second doesn't + } + if (lhsValue is IComparable) + { + return ((IComparable)lhsValue).CompareTo(rhsValue); + } + if (lhsValue.Equals(rhsValue)) + { + return 0; //both are the same + } + //not comparable, compare ToString + return lhsValue.ToString().CompareTo(rhsValue.ToString()); + } + } +}