risu 1.4.3

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 (47) hide show
  1. data/KNOWNISSUES.markdown +50 -0
  2. data/LICENSE +25 -0
  3. data/NEWS.markdown +112 -0
  4. data/README.markdown +126 -0
  5. data/Rakefile +37 -0
  6. data/TODO.markdown +69 -0
  7. data/bin/risu +12 -0
  8. data/lib/nessusdb.rb +38 -0
  9. data/lib/nessusdb/cli.rb +9 -0
  10. data/lib/nessusdb/cli/application.rb +402 -0
  11. data/lib/nessusdb/cli/banner.rb +25 -0
  12. data/lib/nessusdb/exceptions.rb +8 -0
  13. data/lib/nessusdb/exceptions/invaliddocument.rb +10 -0
  14. data/lib/nessusdb/listener.rb +274 -0
  15. data/lib/nessusdb/models.rb +18 -0
  16. data/lib/nessusdb/models/familyselection.rb +12 -0
  17. data/lib/nessusdb/models/host.rb +359 -0
  18. data/lib/nessusdb/models/individualpluginselection.rb +14 -0
  19. data/lib/nessusdb/models/item.rb +183 -0
  20. data/lib/nessusdb/models/plugin.rb +98 -0
  21. data/lib/nessusdb/models/pluginspreference.rb +12 -0
  22. data/lib/nessusdb/models/policy.rb +17 -0
  23. data/lib/nessusdb/models/reference.rb +13 -0
  24. data/lib/nessusdb/models/report.rb +26 -0
  25. data/lib/nessusdb/models/serverpreference.rb +13 -0
  26. data/lib/nessusdb/models/version.rb +12 -0
  27. data/lib/nessusdb/nessusdocument.rb +66 -0
  28. data/lib/nessusdb/parsers.rb +8 -0
  29. data/lib/nessusdb/prawn_templater.rb +38 -0
  30. data/lib/nessusdb/schema.rb +145 -0
  31. data/lib/nessusdb/templates/assets.rb +21 -0
  32. data/lib/nessusdb/templates/cover_sheet.rb +42 -0
  33. data/lib/nessusdb/templates/data/nessuslogo.jpg +0 -0
  34. data/lib/nessusdb/templates/exec_summary.rb +56 -0
  35. data/lib/nessusdb/templates/executive_summary.rb +182 -0
  36. data/lib/nessusdb/templates/finding_statistics.rb +23 -0
  37. data/lib/nessusdb/templates/findings_host.rb +49 -0
  38. data/lib/nessusdb/templates/findings_summary.rb +68 -0
  39. data/lib/nessusdb/templates/findings_summary_with_pluginid.rb +68 -0
  40. data/lib/nessusdb/templates/graphs.rb +33 -0
  41. data/lib/nessusdb/templates/host_summary.rb +40 -0
  42. data/lib/nessusdb/templates/ms_patch_summary.rb +37 -0
  43. data/lib/nessusdb/templates/ms_update_summary.rb +43 -0
  44. data/lib/nessusdb/templates/pci_compliance.rb +66 -0
  45. data/lib/nessusdb/templates/technical_findings.rb +116 -0
  46. data/risu.gemspec +44 -0
  47. metadata +247 -0
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+
3
+ module NessusDB
4
+ module Models
5
+
6
+ # IndividualPluginSelection Model
7
+ #
8
+ # @author Jacob Hammack
9
+ class IndividualPluginSelection < ActiveRecord::Base
10
+ belongs_to :policy
11
+ has_many :plugins
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,183 @@
1
+ # encoding: utf-8
2
+
3
+ module NessusDB
4
+ module Models
5
+
6
+ # Item Model
7
+ #
8
+ # @author Jacob Hammack <jacob.hammack@hammackj.com>
9
+ class Item < ActiveRecord::Base
10
+ belongs_to :host
11
+ belongs_to :plugin
12
+
13
+ class << self
14
+
15
+ # Queries for all risks in the database
16
+ #
17
+ # @return [ActiveRecord::Relation] with the query results
18
+ def risks
19
+ where(:severity => [0,1,2,3])
20
+ end
21
+
22
+ # Queries for all the high risks in the database
23
+ #
24
+ # @return [ActiveRecord::Relation] with the query results
25
+ def high_risks
26
+ where(:severity => 3)
27
+ end
28
+
29
+ # Queries for all the medium risks in the database
30
+ #
31
+ # @return [ActiveRecord::Relation] with the query results
32
+ def medium_risks
33
+ where(:severity => 2)
34
+ end
35
+
36
+ # Queries for all the low risks in the database
37
+ #
38
+ # @return [ActiveRecord::Relation] with the query results
39
+ def low_risks
40
+ where(:severity => 1)
41
+ end
42
+
43
+ # Queries for all the info risks in the database
44
+ #
45
+ # @return [ActiveRecord::Relation] with the query results
46
+ def info_risks
47
+ where(:severity => 0)
48
+ end
49
+
50
+ # Queries for all the unique high risks in the database
51
+ #
52
+ # @return [ActiveRecord::Relation] with the query results
53
+ def high_risks_unique
54
+ where(:severity => 3).joins(:plugin).order("plugins.cvss_base_score").group(:plugin_id)
55
+ end
56
+
57
+ # Queries for all the unique high findings and sorts them by count
58
+ #
59
+ # @return [ActiveRecord::Relation] with the query results
60
+ def high_risks_unique_sorted
61
+ select("items.*").select("count(*) as count_all").where(:severity => 3).group(:plugin_id).order("count_all DESC")
62
+ end
63
+
64
+ # Queries for all the unique medium risks in the database
65
+ #
66
+ # @return [ActiveRecord::Relation] with the query results
67
+ def medium_risks_unique
68
+ where(:severity => 2).joins(:plugin).order(:cvss_base_score).group(:plugin_id)
69
+ end
70
+
71
+ # Queries for all the unique medium findings and sorts them by count
72
+ #
73
+ # @return [ActiveRecord::Relation] with the query results
74
+ def medium_risks_unique_sorted
75
+ select("items.*").select("count(*) as count_all").where(:severity => 2).group(:plugin_id).order("count_all DESC")
76
+ end
77
+
78
+ # Queries for all the unique low risks in the database
79
+ #
80
+ # @return [ActiveRecord::Relation] with the query results
81
+ def low_risks_unique
82
+ where(:severity => 1).joins(:plugin).order(:cvss_base_score).group(:plugin_id)
83
+ end
84
+
85
+ # Queries for all the unique low findings and sorts them by count
86
+ #
87
+ # @return [ActiveRecord::Relation] with the query results
88
+ def low_risks_unique_sorted
89
+ select("items.*").select("count(*) as count_all").where(:severity => 1).group(:plugin_id).order("count_all DESC")
90
+ end
91
+
92
+ # Queries for all the unique info risks in the database
93
+ #
94
+ # @return [ActiveRecord::Relation] with the query results
95
+ def info_risks_unique
96
+ where(:severity => 0).joins(:plugin).order(:cvss_base_score).group(:plugin_id)
97
+ end
98
+
99
+ # Queries for all the unique info findings and sorts them by count
100
+ #
101
+ # @return [ActiveRecord::Relation] with the query results
102
+ def info_risks_unique_sorted
103
+ select("items.*").select("count(*) as count_all").where(:severity => 0).group(:plugin_id).order("count_all DESC")
104
+ end
105
+
106
+ # Queries for all the risks grouped by service type, used for the Vulnerbilities by Service graph
107
+ #
108
+ # @return [ActiveRecord::Relation] with the query results
109
+ def risks_by_service(limit=10)
110
+ select("items.*").select("count(*) as count_all").where("svc_name != 'unknown' and svc_name != 'general'").group(:svc_name).order("count_all DESC").limit(limit)
111
+ end
112
+
113
+ # Queries for all the high risks by plugin
114
+ #
115
+ # @todo update this
116
+ #
117
+ # @return [ActiveRecord::Relation] with the query results
118
+ def risks_by_plugin(limit=10)
119
+ select("items.*").select("count(*) as count_all").joins(:plugin).where("plugin_id != 1").where(:severity => 3).group(:plugin_id).order("count_all DESC").limit(limit)
120
+ end
121
+
122
+ # Queries for all the risks by host
123
+ #
124
+ # @return [ActiveRecord::Relation] with the query results
125
+ def risks_by_host(limit=10)
126
+ select("items.*").select("count(*) as count_all").joins(:host).where("plugin_id != 1").where(:severity => [3, 2]).group(:host_id).order("count_all DESC").limit(limit)
127
+ end
128
+
129
+ # Queries for all the hosts with the Microsoft patch summary plugin (38153)
130
+ #
131
+ # @return [ActiveRecord::Relation] with the query results
132
+ def ms_patches
133
+ where(:plugin_id => 38153).joins(:host)
134
+ end
135
+
136
+ # Queries for all host with the Microsoft Update Summary plugin(12028)
137
+ #
138
+ # @return [ActiveRecord::Relation] with the query results
139
+ def ms_update
140
+ where(:plugin_id => 12028).joins(:host)
141
+ end
142
+
143
+ # Generates a Graph of all the risks by service
144
+ #
145
+ # @return [StringIO] Object containing the generated PNG image
146
+ def risks_by_service_graph(limit=10)
147
+ g = Gruff::Pie.new(GRAPH_WIDTH)
148
+ g.title = sprintf "Top %d Findings By Service", Item.risks_by_service(limit).all.count
149
+ g.sort = false
150
+ g.theme = {
151
+ :colors => %w(red green blue orange yellow purple black grey brown pink),
152
+ :background_colors => %w(white white)
153
+ }
154
+
155
+ Item.risks_by_service(limit).all.each { |service|
156
+ g.data(service.svc_name, Item.find(:all, :conditions => {:svc_name => service.svc_name}).count)
157
+ }
158
+
159
+ StringIO.new(g.to_blob)
160
+ end
161
+
162
+ # Generates a Graph of all the risks by severity
163
+ #
164
+ # @return [StringIO] Object containing the generated PNG image
165
+ def risks_by_severity_graph
166
+ g = Gruff::Bar.new(GRAPH_WIDTH)
167
+ g.title = "Findings By Severity"
168
+ g.sort = false
169
+ g.theme = {
170
+ :background_colors => %w(white white)
171
+ }
172
+
173
+ g.data("High", Item.high_risks.count, "red") unless Item.high_risks.count == 0
174
+ g.data("Medium", Item.medium_risks.count, "orange") unless Item.medium_risks.count == 0
175
+ g.data("Low", Item.low_risks.count, "yellow") unless Item.low_risks.count == 0
176
+ g.data("Info", Item.info_risks.count, "blue") unless Item.info_risks.count == 0
177
+
178
+ StringIO.new(g.to_blob)
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,98 @@
1
+ # encoding: utf-8
2
+
3
+ module NessusDB
4
+ module Models
5
+
6
+ # Plugin Model
7
+ #
8
+ # @author Jacob Hammack
9
+ class Plugin < ActiveRecord::Base
10
+ has_many :items
11
+ belongs_to :family
12
+ has_many :references
13
+ has_many :individual_plugin_selections
14
+
15
+ class << self
16
+
17
+ # Queries for all risks based on Plugin.risk_factor
18
+ #
19
+ # @return [Array] of all risks
20
+ def risks
21
+ critical_risks + high_risks + medium_risks + low_risks + none_risks
22
+ end
23
+
24
+ # Queries for all the critical risks based on Plugin.risk_factor
25
+ #
26
+ # @return [ActiveRelation] of Critical Risks
27
+ def critical_risks
28
+ where(:risk_factor => "Critical")
29
+ end
30
+
31
+ #Queries for all the critical risks based on Plugin.risk_factor
32
+ #
33
+ # @return [ActiveRelation] of High Risks
34
+ def high_risks
35
+ where(:risk_factor => "High")
36
+ end
37
+
38
+ # Queries for all the critical risks based on Plugin.risk_factor
39
+ #
40
+ # @return [ActiveRelation] of Medium Risks
41
+ def medium_risks
42
+ where(:risk_factor => "Medium")
43
+ end
44
+
45
+ # Queries for all the critical risks based on Plugin.risk_factor
46
+ #
47
+ # @return [ActiveRelation] of Low Risks
48
+ def low_risks
49
+ where(:risk_factor => "Low")
50
+ end
51
+
52
+ # Queries for all the critical risks based on Plugin.risk_factor
53
+ #
54
+ # @return [ActiveRelation] of None Risks
55
+ def none_risks
56
+ where(:risk_factor => "None")
57
+ end
58
+
59
+ # Creates a graph based on the top plugins sorted by count
60
+ #
61
+ # @return Filename of the created graph
62
+ def top_by_count_graph(limit=10)
63
+ g = Gruff::Bar.new(GRAPH_WIDTH)
64
+ g.title = sprintf "Top %d High Findings By Plugin", Item.risks_by_plugin(limit).all.count
65
+ g.sort = false
66
+ g.theme = {
67
+ :colors => %w(red green blue orange yellow purple black grey brown pink),
68
+ :background_colors => %w(white white)
69
+ }
70
+
71
+ Item.risks_by_plugin(limit).all.each do |plugin|
72
+ plugin_name = Plugin.find_by_id(plugin.plugin_id).plugin_name
73
+
74
+ #We need to filter the names a little to make everything look nice on the graph
75
+ plugin_name = case plugin.plugin_id
76
+ when 35362 then plugin_name.split(":")[0]
77
+ when 34477 then plugin_name.split(":")[0]
78
+ when 35635 then plugin_name.split(":")[0]
79
+ when 21564 then "VNC Remote Authentication Bypass"
80
+ when 38664 then "Intel Common Base Agent Remote Command Execution"
81
+ when 42411 then "Windows SMB Shares Unprivileged Access"
82
+ else
83
+ plugin_name = Plugin.find_by_id(plugin.plugin_id).plugin_name
84
+ end
85
+
86
+ if plugin_name =~ /^(MS\d{2}-\d{3}):/
87
+ plugin_name = $1
88
+ end
89
+
90
+ g.data(plugin_name, Item.where(:plugin_id => plugin.plugin_id).count)
91
+ end
92
+
93
+ StringIO.new(g.to_blob)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ module NessusDB
4
+ module Models
5
+ # PluginPreference Model
6
+ #
7
+ # @author Jacob Hammack
8
+ class PluginsPreference < ActiveRecord::Base
9
+ belongs_to :policy
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ module NessusDB
4
+ module Models
5
+
6
+ # Policy Model
7
+ #
8
+ # @author Jacob Hammack
9
+ class Policy < ActiveRecord::Base
10
+ has_many :family_selections
11
+ has_many :individual_plugin_selections
12
+ has_many :reports
13
+ has_many :plugins_preferences
14
+ has_many :server_preferences
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ module NessusDB
4
+ module Models
5
+
6
+ # Reference Model
7
+ #
8
+ # @author Jacob Hammack
9
+ class Reference < ActiveRecord::Base
10
+ has_many :plugins
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ module NessusDB
4
+ module Models
5
+
6
+ # Report Model
7
+ #
8
+ # @author Jacob Hammack <jacob.hammack@hammackj.com>
9
+ class Report < ActiveRecord::Base
10
+ has_many :hosts
11
+ belongs_to :policy
12
+
13
+ class << self
14
+
15
+ attr_accessor :title, :author, :company, :classification
16
+
17
+ #
18
+ #@scan_date = Host.where("start is not null").first[:start].to_s
19
+ #
20
+ def scan_date
21
+ Host.where("start is not null").first[:start].to_s
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ module NessusDB
4
+ module Models
5
+
6
+ # ServerPreference Model
7
+ #
8
+ # @author Jacob Hammack
9
+ class ServerPreference < ActiveRecord::Base
10
+ belongs_to :policy
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ module NessusDB
4
+ module Models
5
+
6
+ # Version Model for the DB
7
+ #
8
+ # @author Jacob Hammack
9
+ class Version < ActiveRecord::Base
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+
3
+ module NessusDB
4
+
5
+ # A Object to represet the Nessus xml file in memory
6
+ #
7
+ # @author Jacob Hammack <jacob.hammack@hammackj.com>
8
+ class NessusDocument
9
+
10
+ # Creates a instance of the NessusDocument class
11
+ #
12
+ def initialize document
13
+ @document = document
14
+ end
15
+
16
+ # Checks the validness of a NessusDocument
17
+ #
18
+ # @return [Boolean] True if valid, False if invalid
19
+ def valid?
20
+ if File.exist?(@document)
21
+ @parser = LibXML::XML::Parser.file @document
22
+ doc = @parser.parse
23
+
24
+ if doc.root.name == nil
25
+ return false
26
+ end
27
+
28
+ if doc.root.name == "NessusClientData_v2"
29
+ return true
30
+ elsif doc.root.name == "NessusClientData"
31
+ return false
32
+ else
33
+ return false
34
+ end
35
+ else
36
+ return false
37
+ end
38
+ end
39
+
40
+ # Invokes the SAX parser on the XML document
41
+ #
42
+ def parse
43
+ @parser = LibXML::XML::SaxParser.file @document
44
+ @parser.callbacks = NessusSaxListener.new
45
+ @parser.parse
46
+ end
47
+
48
+ # Fixes the ip field if nil and replaces it with the name if its an ip
49
+ #
50
+ def fix_ips
51
+ @hosts = Host.all
52
+
53
+ @hosts.each do |host|
54
+ if host.ip == nil
55
+ begin
56
+ ip = IPAddr.new host.name
57
+ host.ip = ip.to_string
58
+ host.save
59
+ rescue ArgumentError => ae
60
+ next
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end