dradis-qualys 3.18.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
1
+ # Qualys plugin for Dradis
2
+
3
+ [![Build Status](https://secure.travis-ci.org/dradis/dradis-qualys.png?branch=master)](http://travis-ci.org/dradis/dradis-qualys) [![Code Climate](https://codeclimate.com/github/dradis/dradis-qualys.png)](https://codeclimate.com/github/dradis/dradis-qualys.png)
4
+
5
+ Upload Qualys files into Dradis.
6
+
7
+ The add-on requires [Dradis CE](https://dradisframework.org/) > 3.0, or [Dradis Pro](https://dradisframework.com/pro/).
8
+
9
+
10
+
11
+ ## More information
12
+
13
+ See the Dradis Framework's [README.md](https://github.com/dradis/dradisframework/blob/master/README.md)
14
+
15
+
16
+ ## Contributing
17
+
18
+ See the Dradis Framework's [CONTRIBUTING.md](https://github.com/dradis/dradisframework/blob/master/CONTRIBUTING.md)
19
+
20
+
21
+ ## License
22
+
23
+ Dradis Framework and all its components are released under [GNU General Public License version 2.0](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.
24
+
25
+
26
+ ## Feature requests and bugs
27
+
28
+ Please use the [Dradis Framework issue tracker](https://github.com/dradis/dradis-ce/issues) for add-on improvements and bug reports.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,34 @@
1
+ $:.push File.expand_path('../lib', __FILE__)
2
+ require 'dradis/plugins/qualys/version'
3
+ version = Dradis::Plugins::Qualys::VERSION::STRING
4
+
5
+ # Describe your gem and declare its dependencies:
6
+ Gem::Specification.new do |spec|
7
+ spec.platform = Gem::Platform::RUBY
8
+ spec.name = 'dradis-qualys'
9
+ spec.version = version
10
+ spec.summary = 'Qualys add-on for the Dradis Framework.'
11
+ spec.description = 'This add-on allows you to upload and parse output produced from Qualys Vulnerability Scanner into Dradis.'
12
+
13
+ spec.license = 'GPL-2'
14
+
15
+ spec.authors = ['Daniel Martin']
16
+ spec.email = ['etd@nomejortu.com']
17
+ spec.homepage = 'http://dradisframework.org'
18
+
19
+ spec.files = `git ls-files`.split($\)
20
+ spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
21
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
+
23
+ # By not including Rails as a dependency, we can use the gem with different
24
+ # versions of Rails (a sure recipe for disaster, I'm sure), which is needed
25
+ # until we bump Dradis Pro to 4.1.
26
+ # s.add_dependency 'rails', '~> 4.1.1'
27
+ spec.add_dependency 'dradis-plugins', '~> 3.6'
28
+ spec.add_dependency 'nokogiri', '~> 1.3'
29
+
30
+ spec.add_development_dependency 'bundler', '~> 1.6'
31
+ spec.add_development_dependency 'rake', '~> 10.0'
32
+ spec.add_development_dependency 'rspec-rails'
33
+ spec.add_development_dependency 'combustion', '~> 0.5.2'
34
+ end
@@ -0,0 +1,8 @@
1
+ # Hook to the framework base clases
2
+ require 'dradis-plugins'
3
+
4
+ # Load this add-on's engine
5
+ require 'dradis/plugins/qualys'
6
+
7
+ # Load supporting Qualys classes
8
+ require 'qualys/element'
@@ -0,0 +1,11 @@
1
+ module Dradis
2
+ module Plugins
3
+ module Qualys
4
+ end
5
+ end
6
+ end
7
+
8
+ require 'dradis/plugins/qualys/engine'
9
+ require 'dradis/plugins/qualys/field_processor'
10
+ require 'dradis/plugins/qualys/importer'
11
+ require 'dradis/plugins/qualys/version'
@@ -0,0 +1,13 @@
1
+ module Dradis
2
+ module Plugins
3
+ module Qualys
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace Dradis::Plugins::Qualys
6
+
7
+ include ::Dradis::Plugins::Base
8
+ description 'Processes Qualys output'
9
+ provides :upload
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,42 @@
1
+ module Dradis
2
+ module Plugins
3
+ module Qualys
4
+ class FieldProcessor < Dradis::Plugins::Upload::FieldProcessor
5
+
6
+ def post_initialize(args={})
7
+ @cat_object = data
8
+ @qualys_object = ::Qualys::Element.new(data.elements.first)
9
+ end
10
+
11
+ def value(args={})
12
+ field = args[:field]
13
+
14
+ # Fields in the template are of the form <foo>.<field>, where <foo>
15
+ # is common across all fields for a given template (and meaningless).
16
+ _, name = field.split('.')
17
+
18
+ if %w{cat_fqdn cat_misc cat_port cat_protocol cat_value}.include?(name)
19
+ attribute = name[4..-1]
20
+ @cat_object[attribute] || 'n/a'
21
+ else
22
+
23
+ if name.end_with?('entries')
24
+ # qualys_object.bid_entries
25
+ # qualys_object.cve_entries
26
+ # qualys_object.xref_entries
27
+ entries = @qualys_object.try(name)
28
+ if entries.any?
29
+ entries.to_a.join("\n")
30
+ else
31
+ 'n/a'
32
+ end
33
+ else
34
+ @qualys_object.try(name) || 'n/a'
35
+ end
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,19 @@
1
+ module Dradis
2
+ module Plugins
3
+ module Qualys
4
+ # Returns the version of the currently loaded Qualys as a <tt>Gem::Version</tt>
5
+ def self.gem_version
6
+ Gem::Version.new VERSION::STRING
7
+ end
8
+
9
+ module VERSION
10
+ MAJOR = 3
11
+ MINOR = 18
12
+ TINY = 0
13
+ PRE = nil
14
+
15
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,88 @@
1
+ module Dradis::Plugins::Qualys
2
+ class Importer < Dradis::Plugins::Upload::Importer
3
+
4
+ attr_accessor :host_node
5
+
6
+ # The framework will call this function if the user selects this plugin from
7
+ # the dropdown list and uploads a file.
8
+ # @returns true if the operation was successful, false otherwise
9
+ def import(params={})
10
+ file_content = File.read( params[:file] )
11
+
12
+ logger.info{'Parsing Qualys output file...'}
13
+ @doc = Nokogiri::XML( file_content )
14
+ logger.info{'Done.'}
15
+
16
+ if @doc.root.name != 'SCAN'
17
+ error = "No scan results were detected in the uploaded file. Ensure you uploaded a Qualys XML file."
18
+ logger.fatal{ error }
19
+ content_service.create_note text: error
20
+ return false
21
+ end
22
+
23
+ @doc.xpath('SCAN/IP').each do |xml_host|
24
+ process_ip(xml_host)
25
+ end
26
+
27
+ return true
28
+ end
29
+
30
+ private
31
+
32
+ def process_ip(xml_host)
33
+ host_ip = xml_host['value']
34
+ logger.info{ "Host: %s" % host_ip }
35
+
36
+ self.host_node = content_service.create_node(label: host_ip, type: :host)
37
+
38
+ host_node.set_property(:ip, host_ip)
39
+ host_node.set_property(:hostname, xml_host['name'])
40
+ if (xml_os = xml_host.xpath('OS')) && xml_os.any?
41
+ host_node.set_property(:os, xml_os.text)
42
+ end
43
+ host_node.save
44
+
45
+ # We treat INFOS, SERVICES, PRACTICES, and VULNS the same way
46
+ # All of these are imported into Dradis as Issues
47
+ ['INFOS', 'SERVICES', 'PRACTICES', 'VULNS'].each do |collection|
48
+ xml_host.xpath(collection).each do |xml_collection|
49
+ process_collection(collection, xml_collection)
50
+ end
51
+ end
52
+ end
53
+
54
+ def process_collection(collection, xml_collection)
55
+ xml_cats = xml_collection.xpath('CAT')
56
+
57
+ xml_cats.each do |xml_cat|
58
+ logger.info{ "\t#{ collection } - #{ xml_cat['value'] }" }
59
+
60
+ empty_dup_xml_cat = xml_cat.dup
61
+ empty_dup_xml_cat.children.remove
62
+
63
+ # For each INFOS/CAT/INFO, SERVICES/CAT/SERVICE, VULNS/CAT/VULN, etc.
64
+ xml_cat.xpath(collection.chop).each do |xml_element|
65
+ dup_xml_cat = empty_dup_xml_cat.dup
66
+ dup_xml_cat.add_child(xml_element.dup)
67
+ cat_number = xml_element[:number]
68
+
69
+ process_vuln(collection, cat_number, dup_xml_cat)
70
+
71
+ end
72
+ end
73
+ end
74
+
75
+ # Takes a <CAT> element containing a single <VULN> element and processes an
76
+ # Issue and Evidence template out of it.
77
+ def process_vuln(collection, vuln_number, xml_cat)
78
+ logger.info{ "\t\t => Creating new issue (plugin_id: #{ vuln_number })" }
79
+ issue_text = template_service.process_template(template: 'element', data: xml_cat)
80
+ issue_text << "\n\n#[qualys_collection]#\n#{ collection }"
81
+ issue = content_service.create_issue(text: issue_text, id: vuln_number)
82
+
83
+ logger.info{ "\t\t => Creating new evidence" }
84
+ evidence_content = template_service.process_template(template: 'evidence', data: xml_cat)
85
+ content_service.create_evidence(issue: issue, node: self.host_node, content: evidence_content)
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,13 @@
1
+ require_relative 'gem_version'
2
+
3
+ module Dradis
4
+ module Plugins
5
+ module Qualys
6
+ # Returns the version of the currently loaded Qualys as a
7
+ # <tt>Gem::Version</tt>.
8
+ def self.version
9
+ gem_version
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,114 @@
1
+ module Qualys
2
+ # This class represents each of the /SCAN/IP/[INFOS|SERVICES|VULNS|PRACTICES]/CAT/[INFO|SERVICE|VULN|PRACTICE]
3
+ # elements in the Qualys XML document.
4
+ #
5
+ # It provides a convenient way to access the information scattered all over
6
+ # the XML in attributes and nested tags.
7
+ #
8
+ # Instead of providing separate methods for each supported property we rely
9
+ # on Ruby's #method_missing to do most of the work.
10
+ class Element
11
+ # Accepts an XML node from Nokogiri::XML.
12
+ def initialize(xml_node)
13
+ @xml = xml_node
14
+ end
15
+
16
+ # List of supported tags. They can be attributes, simple descendans or
17
+ # collections (e.g. <references/>, <tags/>)
18
+ def supported_tags
19
+ [
20
+ # attributes
21
+ :number, :severity, :cveid,
22
+
23
+ # simple tags
24
+ :title, :last_update, :cvss_base, :cvss_temporal, :pci_flag, :diagnosis,
25
+ :consequence, :solution, :compliance, :result,
26
+
27
+ # multiple tags
28
+ :vendor_reference_list, :cve_id_list, :bugtraq_id_list
29
+ ]
30
+ end
31
+
32
+ # This allows external callers (and specs) to check for implemented
33
+ # properties
34
+ def respond_to?(method, include_private=false)
35
+ return true if supported_tags.include?(method.to_sym)
36
+ super
37
+ end
38
+
39
+ # This method is invoked by Ruby when a method that is not defined in this
40
+ # instance is called.
41
+ #
42
+ # In our case we inspect the @method@ parameter and try to find the
43
+ # attribute, simple descendent or collection that it maps to in the XML
44
+ # tree.
45
+ def method_missing(method, *args)
46
+ # We could remove this check and return nil for any non-recognized tag.
47
+ # The problem would be that it would make tricky to debug problems with
48
+ # typos. For instance: <>.potr would return nil instead of raising an
49
+ # exception
50
+ unless supported_tags.include?(method)
51
+ super
52
+ return
53
+ end
54
+
55
+ # First we try the attributes. In Ruby we use snake_case, but in XML
56
+ # CamelCase is used for some attributes
57
+ # translations_table = {
58
+ # :nexpose_id => 'id',
59
+ # :pci_severity => 'pciSeverity',
60
+ # :cvss_score => 'cvssScore',
61
+ # :cvss_vector =>'cvssVector'
62
+ # }
63
+ #
64
+ # method_name = translations_table.fetch(method, method.to_s)
65
+ method_name = method.to_s
66
+ return @xml.attributes[method_name].value if @xml.attributes.key?(method_name)
67
+
68
+ # Then we try simple children tags: TITLE, LAST_UPDATE, CVSS_BASE...
69
+ tag = @xml.xpath("./#{method_name.upcase}").first
70
+ if tag && !tag.text.blank?
71
+ if tags_with_html_content.include?(method)
72
+ return cleanup_html(tag.text)
73
+ else
74
+ return tag.text
75
+ end
76
+ else
77
+ 'n/a'
78
+ end
79
+
80
+ # Finally the enumerations: vendor_reference_list, cve_id_list, bugtraq_id_list
81
+ if method_name == 'references'
82
+ # @xml.xpath("./references/reference").collect{|entry| {:source => entry['source'], :text => entry.text} }
83
+ elsif method == 'tags'
84
+ # @xml.xpath("./tags/tag").collect(&:text)
85
+ else
86
+ # nothing found, the tag is valid but not present in this ReportItem
87
+ return nil
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def cleanup_html(source)
94
+ result = source.dup
95
+ result.gsub!(/&quot;/, '"')
96
+ result.gsub!(/&lt;/, '<')
97
+ result.gsub!(/&gt;/, '>')
98
+
99
+ result.gsub!(/<p>/i, "\n\n")
100
+ result.gsub!(/<br>/i, "\n")
101
+ result.gsub!(/ /, "")
102
+ result.gsub!(/<a href=\"(.*?)\" target=\"_blank\">(.*?)<\/a>/i) { "\"#{$2.strip}\":#{$1.strip}" }
103
+ result.gsub!(/<pre>(.*?)<\/pre>/im) { |m| "\n\nbc.. #{$1.strip}\n\np. \n" }
104
+ result.gsub!(/<b>(.*?)<\/b>/i) { "*#{$1.strip}*" }
105
+ result.gsub!(/<i>(.*?)<\/i>/i) { "_#{$1.strip}_" }
106
+ result
107
+ end
108
+
109
+ def tags_with_html_content
110
+ [:diagnosis, :solution]
111
+ end
112
+
113
+ end
114
+ end
@@ -0,0 +1,21 @@
1
+ class QualysTasks < Thor
2
+ include Rails.application.config.dradis.thor_helper_module
3
+
4
+ namespace "dradis:plugins:qualys"
5
+
6
+ desc "upload FILE", "upload Qualys XML results"
7
+ def upload(file_path)
8
+ require 'config/environment'
9
+
10
+ unless File.exists?(file_path)
11
+ $stderr.puts "** the file [#{file_path}] does not exist"
12
+ exit -1
13
+ end
14
+
15
+ detect_and_set_project_scope
16
+
17
+ importer = Dradis::Plugins::Qualys::Importer.new(task_options)
18
+ importer.import(file: file_path)
19
+ end
20
+
21
+ end
File without changes
@@ -0,0 +1,91 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+
3
+ <!DOCTYPE SCAN SYSTEM "https://qualysguard.qualys.de/scan-1.dtd">
4
+ <SCAN value="scan/1327124089.959">
5
+
6
+ <HEADER>
7
+ <KEY value="USERNAME">dradispro</KEY>
8
+ <KEY value="COMPANY"><![CDATA[Security Roots]]></KEY>
9
+ <KEY value="DATE">2011-12-20T12:00:00Z</KEY>
10
+ <KEY value="TITLE"><![CDATA[Sample_Test_Scan]]></KEY>
11
+ <KEY value="TARGET">10.0.155.157,10.0.155.160</KEY>
12
+ <KEY value="DURATION">03:42:36</KEY>
13
+ <KEY value="SCAN_HOST">62.210.136.186 (Scanner 4.14.30-1,Web 6.0 FR6 [build 6.3.94-1],Vulnsigs 1.22.62-1)</KEY>
14
+ <KEY value="NBHOST_ALIVE">2</KEY>
15
+ <KEY value="NBHOST_TOTAL">2</KEY>
16
+ <KEY value="REPORT_TYPE">Scheduled</KEY>
17
+ <KEY value="OPTIONS">Full TCP scan, Standard Password Brute Forcing, Load balancer detection OFF, Overall Performance: Custom, Hosts to Scan in Parallel - External Scanners: 1, Hosts to Scan in Parallel - Scanner Appliances: 1, Total Processes to Run in Parallel: 1, HTTP Processes to Run in Parallel: 1, Packet (Burst) Delay: Maximum</KEY>
18
+ <KEY value="STATUS">FINISHED</KEY>
19
+ <OPTION_PROFILE>
20
+ <OPTION_PROFILE_TITLE option_profile_default="0"><![CDATA[Payment Card Industry (PCI) Options]]></OPTION_PROFILE_TITLE>
21
+ </OPTION_PROFILE>
22
+ </HEADER>
23
+
24
+ <IP value="10.0.155.160" name="No registered hostname">
25
+ <OS><![CDATA[Linux 2.4-2.6]]></OS>
26
+ <VULNS>
27
+ <CAT value="TCP/IP">
28
+ <VULN number="82054" severity="2" cveid="CVE-2004-0230">
29
+ <TITLE><![CDATA[TCP Sequence Number Approximation Based Denial of Service]]></TITLE>
30
+ <LAST_UPDATE><![CDATA[2007-12-20T22:53:15Z]]></LAST_UPDATE>
31
+ <CVSS_BASE>5</CVSS_BASE>
32
+ <CVSS_TEMPORAL>4.2</CVSS_TEMPORAL>
33
+ <PCI_FLAG>0</PCI_FLAG>
34
+ <CVE_ID_LIST>
35
+ <CVE_ID>
36
+ <ID><![CDATA[CVE-2004-0230]]></ID>
37
+ <URL><![CDATA[http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2004-0230]]></URL>
38
+ </CVE_ID>
39
+ </CVE_ID_LIST>
40
+ <BUGTRAQ_ID_LIST>
41
+ <BUGTRAQ_ID>
42
+ <ID><![CDATA[10183]]></ID>
43
+ <URL><![CDATA[http://www.securityfocus.com/bid/10183]]></URL>
44
+ </BUGTRAQ_ID>
45
+ </BUGTRAQ_ID_LIST>
46
+ <DIAGNOSIS>
47
+ <![CDATA[TCP provides stateful communications between hosts on a network. TCP sessions are established by a three-way handshake and use random 32-bit sequence and acknowledgement numbers to ensure the validity of traffic. A vulnerability was reported that may permit TCP sequence numbers to be more easily approximated by remote attackers. This issue affects products released by multiple vendors.
48
+ <P>
49
+ The cause of the vulnerability is that affected implementations will accept TCP sequence numbers within a certain range, known as the acknowledgement range, of the expected sequence number for a packet in the session. This is determined by the TCP window size, which is negotiated during the three-way handshake for the session. Larger TCP window sizes may be set to allow for more throughput, but the larger the TCP window size, the more probable it is to guess a TCP sequence number that falls within an acceptable range. It was initially thought that guessing an acceptable sequence number was relatively difficult for most implementations given random distribution, making this type of attack impractical. However, some implementations may make it easier to successfully approximate an acceptable TCP sequence number, making these attacks possible with a number of protocols and implementations.
50
+ <P>
51
+ This is further compounded by the fact that some implementations may support the use of the TCP Window Scale Option, as described in RFC 1323, to extend the TCP window size to a maximum value of 1 billion.
52
+ <P>
53
+ This vulnerability will permit a remote attacker to inject a SYN or RST packet into the session, causing it to be reset and effectively allowing for denial of service attacks. An attacker would exploit this issue by sending a packet to a receiving implementation with an approximated sequence number and a forged source IP address and TCP port.
54
+ <P>
55
+ There are a few factors that may present viable target implementations, such as those which depend on long-lived TCP connections, those that have known or easily guessed IP address endpoints and those implementations with easily guessed TCP source ports. It has been noted that Border Gateway Protocol (BGP) is reported to be particularly vulnerable to this type of attack, due to the use of long-lived TCP sessions and the possibility that some implementations may use the TCP Window Scale Option. As a result, this issue is likely to affect a number of routing platforms.
56
+ <P>
57
+ Another factor to consider is the relative difficulty of injecting packets into TCP sessions, as a number of receiving implementations will reassemble packets in order, dropping any duplicates. This may make some implementations more resistant to attacks than others.
58
+ <P>
59
+ It should be noted that while a number of vendors have confirmed this issue in various products, investigations are ongoing and it is likely that many other vendors and products will turn out to be vulnerable as the issue is investigated further.]]>
60
+ </DIAGNOSIS>
61
+ <CONSEQUENCE><![CDATA[Successful exploitation of this issue could lead to denial of service attacks on the TCP based services of target hosts. Other consequences may also result, such as man-in-the-middle attacks.]]></CONSEQUENCE>
62
+ <SOLUTION>
63
+ <![CDATA[Please first check the results section below for the port number on which this vulnerability was detected. If that port number is known to be used for port-forwarding, then it is the backend host that is really vulnerable.
64
+ <P>
65
+ Various implementations and products including Check Point, Cisco, Cray Inc, Hitachi, Internet Initiative Japan, Inc (IIJ), Juniper Networks, NEC, Polycom, and Yamaha are currently undergoing review. Contact the vendors to obtain more information about affected products and fixes. <A HREF="http://packetstormsecurity.org/0404-advisories/246929.html" TARGET="_blank">NISCC Advisory 236929 - Vulnerability Issues in TCP</A> details the vendor patch status as of the time of the advisory, and identifies resolutions and workarounds.
66
+ <P>
67
+ The Internet Engineering Task Force (IETF) has developed an Internet-Draft titled <A HREF="http://www3.ietf.org/proceedings/05mar/slides/tcpm-4.pdf" TARGET="_blank">Transmission Control Protocol Security Considerations</A> that addresses this issue.
68
+ <P>
69
+ Workaround:
70
+ <P>
71
+ The following BGP-specific workaround information has been provided.
72
+ <P>
73
+ For BGP implementations that support it, the TCP MD5 Signature Option should be enabled. Passwords that the MD5 checksum is applied to should be set to strong values and changed on a regular basis.
74
+ <P>
75
+ Secure BGP configuration instructions have been provided for Cisco and Juniper at these locations:
76
+ <BR>
77
+ <A HREF="http://www.cymru.com/Documents/secure-bgp-template.html" TARGET="_blank">http://www.cymru.com/Documents/secure-bgp-template.html</A>
78
+ <BR>
79
+ <A HREF="http://www.qorbit.net/documents/junos-bgp-template.pdf" TARGET="_blank">http://www.qorbit.net/documents/junos-bgp-template.pdf</A>
80
+ ]]>
81
+ </SOLUTION>
82
+ <!--
83
+ This is the real test. We want to make sure the parse doesn't cry if this is missing.
84
+ <RESULT><![CDATA[Tested on port 80 with an injected SYN/RST offset by 16 bytes.]]></RESULT>
85
+ -->
86
+ </VULN>
87
+ </CAT>
88
+ </VULNS>
89
+ </IP>
90
+ </SCAN>
91
+ <!-- CONFIDENTIAL AND PROPRIETARY INFORMATION. Qualys provides the QualysGuard Service "As Is," without any warranty of any kind. Qualys makes no warranty that the information contained in this report is complete or error-free. Copyright 2008, Qualys, Inc. //-->