dradis-burp 3.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.github/issue_template.md +16 -0
  3. data/.github/pull_request_template.md +36 -0
  4. data/.gitignore +10 -0
  5. data/.rspec +2 -0
  6. data/CHANGELOG.md +57 -0
  7. data/CONTRIBUTING.md +3 -0
  8. data/Gemfile +23 -0
  9. data/LICENSE +339 -0
  10. data/README.md +29 -0
  11. data/Rakefile +1 -0
  12. data/dradis-burp.gemspec +34 -0
  13. data/lib/burp/html/issue.rb +157 -0
  14. data/lib/burp/issue.rb +43 -0
  15. data/lib/burp/xml/issue.rb +127 -0
  16. data/lib/dradis-burp.rb +10 -0
  17. data/lib/dradis/plugins/burp.rb +12 -0
  18. data/lib/dradis/plugins/burp/engine.rb +25 -0
  19. data/lib/dradis/plugins/burp/field_processor.rb +27 -0
  20. data/lib/dradis/plugins/burp/gem_version.rb +19 -0
  21. data/lib/dradis/plugins/burp/html/importer.rb +144 -0
  22. data/lib/dradis/plugins/burp/version.rb +13 -0
  23. data/lib/dradis/plugins/burp/xml/importer.rb +144 -0
  24. data/lib/tasks/thorfile.rb +30 -0
  25. data/spec/burp_upload_spec.rb +220 -0
  26. data/spec/fixtures/files/burp.html +229 -0
  27. data/spec/fixtures/files/burp.xml +100 -0
  28. data/spec/fixtures/files/burp_issue_severity.xml +118 -0
  29. data/spec/fixtures/files/invalid-utf-issue.xml +21 -0
  30. data/spec/fixtures/files/without-base64.xml +709 -0
  31. data/spec/spec_helper.rb +9 -0
  32. data/templates/evidence.fields +8 -0
  33. data/templates/evidence.sample +76 -0
  34. data/templates/evidence.template +20 -0
  35. data/templates/html_evidence.fields +13 -0
  36. data/templates/html_evidence.sample +36 -0
  37. data/templates/html_evidence.template +50 -0
  38. data/templates/issue.fields +8 -0
  39. data/templates/issue.sample +23 -0
  40. data/templates/issue.template +30 -0
  41. metadata +174 -0
@@ -0,0 +1,29 @@
1
+ # Burp Scanner plugin for Dradis
2
+
3
+ [![Build Status](https://secure.travis-ci.org/dradis/dradis-burp.png?branch=master)](http://travis-ci.org/dradis/dradis-burp) [![Code Climate](https://codeclimate.com/github/dradis/dradis-burp.png)](https://codeclimate.com/github/dradis/dradis-burp.png)
4
+
5
+
6
+ Upload Burp Scanner XML export files into Dradis.
7
+
8
+ The add-on requires [Dradis CE](https://dradisframework.org/) > 3.0, or [Dradis Pro](https://dradisframework.com/pro/).
9
+
10
+
11
+
12
+ ## More information
13
+
14
+ See the Dradis Framework's [README.md](https://github.com/dradis/dradisframework/blob/master/README.md)
15
+
16
+
17
+ ## Contributing
18
+
19
+ See the Dradis Framework's [CONTRIBUTING.md](https://github.com/dradis/dradisframework/blob/master/CONTRIBUTING.md)
20
+
21
+
22
+ ## License
23
+
24
+ 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.
25
+
26
+
27
+ ## Feature requests and bugs
28
+
29
+ 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/burp/version'
3
+ version = Dradis::Plugins::Burp::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-burp'
9
+ spec.version = version
10
+ spec.summary = 'Burp Scanner upload plugin for the Dradis Framework.'
11
+ spec.description = 'This plugin allows you to upload and parse output produced from Portswigger\'s Burp 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'
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,157 @@
1
+ module Burp
2
+ module Html
3
+ # This class represents each of the issue elements in the Burp
4
+ # Scanner HTML document: all elemennts from a span.BODH0 until the next
5
+ # span.BODH0 (the next one excluded).
6
+ #
7
+ # It provides a convenient way to access the information scattered all over
8
+ # the HTML.
9
+ class Issue < ::Burp::Issue
10
+ # Accepts a Nokogiri::XML::NodeSet
11
+ def initialize(html)
12
+ @html = Nokogiri::HTML(html.to_s)
13
+ end
14
+
15
+ # List of supported tags
16
+ def supported_tags
17
+ [
18
+ # tags with contents retrieved from inside the span header
19
+ :name, :type,
20
+
21
+ # tags with contents retrieved following the span header
22
+ :background, :detail,
23
+ :references, :remediation_background, :remediation_detail,
24
+ :request, :request_1, :request_2, :request_3,
25
+ :response, :response_1, :response_2, :response_3,
26
+ :vulnerability_classifications
27
+ ] + summary_table_tags
28
+ end
29
+
30
+ def header
31
+ @header ||= @html.at_css('span')
32
+ end
33
+
34
+ def name
35
+ @name ||= header.text.gsub(/^\d+\.\S/, '')
36
+ end
37
+
38
+ # Link looks like: https://portswigger.net/kb/issues/00200400_flash-cross-domain-policy
39
+ # We use that 00200400 as type since in that page it calls it 'Type index'
40
+ def type
41
+ @type ||=
42
+ if header_link = header.at_css('a')
43
+ header_link.attr('href').to_s[/\/([0-9a-f]+)_.*/, 1].to_i(16)
44
+ else
45
+ nil
46
+ end
47
+ end
48
+
49
+ # This method is invoked by Ruby when a method that is not defined in this
50
+ # instance is called.
51
+ #
52
+ # In our case we inspect the @method@ parameter and try to find the
53
+ # corresponding header in our HTML, then return the following text.
54
+ def method_missing(method, *args)
55
+ # We could remove this check and return nil for any non-recognized tag.
56
+ # The problem would be that it would make tricky to debug problems with
57
+ # typos. For instance: <>.potr would return nil instead of raising an
58
+ # exception
59
+ unless supported_tags.include?(method)
60
+ super
61
+ return
62
+ end
63
+
64
+ # First we try the h2 headers.
65
+ translations_table = {
66
+ background: ['Issue background', 'Issue description'],
67
+ detail: 'Issue detail',
68
+ references: 'References',
69
+ remediation_background: ['Remediation background', 'Issue remediation'],
70
+ remediation_detail: 'Remediation detail',
71
+ request: 'Request',
72
+ request_1: 'Request 1',
73
+ request_2: 'Request 2',
74
+ request_3: 'Request 3',
75
+ response: 'Response',
76
+ response_1: 'Response 1',
77
+ response_2: 'Response 2',
78
+ response_3: 'Response 3',
79
+ serial_number: 'Serial number',
80
+ vulnerability_classifications: 'Vulnerability classifications'
81
+ }
82
+
83
+ # look for the h2 headers in the html fragment
84
+ method_names = translations_table.fetch(method, method.to_s)
85
+ method_names = [method_names].flatten
86
+
87
+ h2 = nil
88
+ method_names.each do |method_name|
89
+ h2 = @html.xpath("//h2[text()='#{method_name}']").first
90
+ break if h2
91
+ end
92
+
93
+ if h2
94
+ content =
95
+ if h2.text =~ /^(Request|Response)/
96
+ cleanup_request_response_html(h2.next_element.inner_html)
97
+ else
98
+ cleanup_html(h2.next_element.inner_html)
99
+ end
100
+
101
+ return content
102
+ end
103
+
104
+ # look inside the summary table in the html fragment
105
+ summary[method]
106
+ end
107
+
108
+ private
109
+
110
+ # In Request/Response html snippets we don't want to cleanup the whole
111
+ # html as we ususally do. The snippets may contain html code to be displayed,
112
+ # and we don't want to convert that to textile.
113
+ def cleanup_request_response_html(source)
114
+ result = source.dup
115
+
116
+ result.gsub!(/<b>(.*?)<\/b>/, '\1')
117
+ result.gsub!(/<br>|<\/br>/){"\n"}
118
+ result.gsub!(/<span.*?>/, '')
119
+ result.gsub!(/<\/span>/, '')
120
+
121
+ result.gsub!(/&quot;/, '"')
122
+ result.gsub!(/&amp;/, '&')
123
+ result.gsub!(/&lt;/, '<')
124
+ result.gsub!(/&gt;/, '>')
125
+ result.gsub!(/&nbsp;/, ' ')
126
+
127
+ result
128
+ end
129
+
130
+ # Returns the summary table in the HTML fragment as a Hash
131
+ def summary
132
+ @summary ||= begin
133
+ @summary = {}
134
+ h2 = @html.search("h2[text()='Summary']").first
135
+ return @summary if h2.nil?
136
+
137
+ table = h2.next_element
138
+
139
+ summary_table_tags.each do |tag|
140
+ td = table.search("td:starts-with('#{tag.to_s.capitalize}:')").first
141
+ @summary[tag] = td.next_element.text
142
+ end
143
+
144
+ @summary
145
+ end
146
+ end
147
+
148
+ # List of supported tags to obtain from the summary html table
149
+ def summary_table_tags
150
+ [
151
+ :confidence, :host, :path, :severity
152
+ ]
153
+ end
154
+
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,43 @@
1
+ module Burp
2
+ # We use this string to replace invalid UTF-8 bytes with.
3
+ INVALID_UTF_REPLACE = '<?>'
4
+
5
+ class Issue
6
+
7
+ # This allows external callers (and specs) to check for implemented
8
+ # properties
9
+ def respond_to?(method, include_private=false)
10
+ return true if supported_tags.include?(method.to_sym)
11
+ super
12
+ end
13
+
14
+ private
15
+
16
+ def cleanup_html(source)
17
+ result = source.dup
18
+ result.gsub!(/&quot;/, '"')
19
+ result.gsub!(/&amp;/, '&')
20
+ result.gsub!(/&lt;/, '<')
21
+ result.gsub!(/&gt;/, '>')
22
+ result.gsub!(/&nbsp;/, ' ')
23
+
24
+ result.gsub!(/<b>(.*?)<\/b>/, '*\1*')
25
+ result.gsub!(/<br>|<\/br>/){"\n"}
26
+ result.gsub!(/<font.*?>(.*?)<\/font>/m, '\1')
27
+ result.gsub!(/<h\d?>(.*?)<\/h\d?>/, '*\1*')
28
+ result.gsub!(/<i>(.*?)<\/i>/, '\1')
29
+ result.gsub!(/<p>|<\/p>/){"\n"}
30
+ result.gsub!(/<pre.*?>(.*?)<\/pre>/m){|m| "\n\nbc.. #{ $1 }\n\np. \n" }
31
+
32
+ result.gsub!(/<ul>(.*?)<\/ul>/m){|m| "#{ $1 }\n"}
33
+ result.gsub!(/<li>(.*?)<\/li>/m){|m| "\n* #{ $1 }"}
34
+ result.gsub!(/<a href=\"(.*?)\">(.*?)<\/a>/im) { "\"#{$2.strip}\":#{$1.strip}" }
35
+
36
+ result.gsub!(/<table>(.*?)<\/table>/m){|m| "\n\n#{ $1 }\n\n" }
37
+ result.gsub!(/<tr>(.*?)<\/tr>/m){|m| "|#{ $1 }\n" }
38
+ result.gsub!(/<td>(.*?)<\/td>/, '\1|')
39
+
40
+ result
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,127 @@
1
+ module Burp
2
+ module Xml
3
+
4
+ # This class represents each of the /issues/issue elements in the Burp
5
+ # Scanner XML document.
6
+ #
7
+ # It provides a convenient way to access the information scattered all over
8
+ # the XML in attributes and nested tags.
9
+ #
10
+ # Instead of providing separate methods for each supported property we rely
11
+ # on Ruby's #method_missing to do most of the work.
12
+ class Issue < ::Burp::Issue
13
+
14
+ # Accepts an XML node from Nokogiri::XML.
15
+ def initialize(xml_node)
16
+ @xml = xml_node
17
+ end
18
+
19
+ # List of supported tags. They can be attributes, simple descendants or
20
+ # collections (e.g. <references/>, <tags/>)
21
+ def supported_tags
22
+ [
23
+ # attributes
24
+
25
+ # simple tags
26
+ :background, :confidence, :detail, :host, :location, :name, :path,
27
+ :references, :remediation_background, :remediation_detail,
28
+ :serial_number, :severity, :type,
29
+ :vulnerability_classifications,
30
+
31
+ # nested tags
32
+ :request, :response
33
+ ]
34
+ end
35
+
36
+ # This method is invoked by Ruby when a method that is not defined in this
37
+ # instance is called.
38
+ #
39
+ # In our case we inspect the @method@ parameter and try to find the
40
+ # attribute, simple descendent or collection that it maps to in the XML
41
+ # tree.
42
+ def method_missing(method, *args)
43
+
44
+ # We could remove this check and return nil for any non-recognized tag.
45
+ # The problem would be that it would make tricky to debug problems with
46
+ # typos. For instance: <>.potr would return nil instead of raising an
47
+ # exception
48
+ unless supported_tags.include?(method)
49
+ super
50
+ return
51
+ end
52
+
53
+ # First we try the attributes. In Ruby we use snake_case, but in XML
54
+ # CamelCase is used for some attributes
55
+ translations_table = {
56
+ background: 'issueBackground',
57
+ detail: 'issueDetail',
58
+ remediation_background: 'remediationBackground',
59
+ remediation_detail: 'remediationDetail',
60
+ vulnerability_classifications: 'vulnerabilityClassifications',
61
+ serial_number: 'serialNumber'
62
+ }
63
+
64
+ method_name = translations_table.fetch(method, method.to_s)
65
+
66
+ # no attributes in the <issue> node
67
+ # return @xml.attributes[method_name].value if @xml.attributes.key?(method_name)
68
+
69
+ # Then we try simple children tags: name, type, ...
70
+ tag = @xml.xpath("./#{method_name}").first
71
+ if tag && !tag.text.blank?
72
+ if tags_with_html_content.include?(method)
73
+ return cleanup_html(tag.text)
74
+ else
75
+ return tag.text
76
+ end
77
+ end
78
+
79
+ if (['request', 'response'].include?(method_name))
80
+ requestresponse_child(method_name)
81
+ else
82
+ # nothing found, the tag is valid but not present in this ReportItem
83
+ return nil
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ # Some of the values have embedded HTML content that we need to strip
90
+ def tags_with_html_content
91
+ [:background, :detail, :remediation_background, :remediation_detail, :references, :vulnerability_classifications]
92
+ end
93
+
94
+ def requestresponse_child(field)
95
+ return 'n/a' unless @xml.at('requestresponse') && @xml.at("requestresponse/#{field}")
96
+
97
+ xml_node = @xml.at("requestresponse/#{field}")
98
+ result = "[unprocessable #{field}]"
99
+
100
+ if xml_node['base64'] == 'true'
101
+ result = Base64::strict_decode64(xml_node.text)
102
+
103
+ # don't pass binary data to the DB.
104
+ if result =~ /\0/
105
+ header, _ = result.split("\r\n\r\n")
106
+ result = header << "\r\n\r\n" << '[Binary Data Not Displayed]'
107
+ end
108
+ else
109
+ result = xml_node.text
110
+ end
111
+
112
+ # Just in case a null byte was left by Burp
113
+ result.gsub!(/\0/,'&#00;')
114
+
115
+ # We truncate the request/response because it can be pretty big.
116
+ # If it is > 1M MySQL will die when trying to INSERT
117
+ #
118
+ # TODO: maybe add a reference to this node's XPATH so the user can go
119
+ # back to the burp scanner file and look up the original request/response
120
+ result.truncate(50000, omission: '... (truncated)')
121
+
122
+ # Encode the string to UTF-8 to catch invalid bytes.
123
+ result.encode('utf-8', invalid: :replace, undef: :replace, replace: ::Burp::INVALID_UTF_REPLACE)
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,10 @@
1
+ # Hook to the framework base clases
2
+ require 'dradis-plugins'
3
+
4
+ # Load this add-on's engine
5
+ require 'dradis/plugins/burp'
6
+
7
+ # Load supporting Burp classes
8
+ require 'burp/issue'
9
+ require 'burp/html/issue'
10
+ require 'burp/xml/issue'
@@ -0,0 +1,12 @@
1
+ module Dradis
2
+ module Plugins
3
+ module Burp
4
+ end
5
+ end
6
+ end
7
+
8
+ require 'dradis/plugins/burp/engine'
9
+ require 'dradis/plugins/burp/field_processor'
10
+ require 'dradis/plugins/burp/html/importer'
11
+ require 'dradis/plugins/burp/version'
12
+ require 'dradis/plugins/burp/xml/importer'
@@ -0,0 +1,25 @@
1
+ module Dradis
2
+ module Plugins
3
+ module Burp
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace Dradis::Plugins::Burp
6
+
7
+ include ::Dradis::Plugins::Base
8
+ description 'Processes Burp Scanner output'
9
+ provides :upload
10
+
11
+ # Because this plugin provides two export modules, we have to overwrite
12
+ # the default .uploaders() method.
13
+ #
14
+ # See:
15
+ # Dradis::Plugins::Upload::Base in dradis-plugins
16
+ def self.uploaders
17
+ [
18
+ Dradis::Plugins::Burp::Html,
19
+ Dradis::Plugins::Burp::Xml
20
+ ]
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end