dradis-burp 3.18.0

Sign up to get free protection for your applications and to get access to all the features.
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