risu 1.4.9 → 1.5.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 (38) hide show
  1. data/Gemfile.ci +18 -0
  2. data/LICENSE +3 -3
  3. data/NEWS.markdown +45 -0
  4. data/README.markdown +10 -2
  5. data/Rakefile +15 -5
  6. data/TODO.markdown +45 -19
  7. data/lib/risu.rb +2 -1
  8. data/lib/risu/base/schema.rb +3 -4
  9. data/lib/risu/base/template_base.rb +11 -11
  10. data/lib/risu/cli/application.rb +23 -15
  11. data/lib/risu/exceptions.rb +1 -3
  12. data/lib/risu/models/host.rb +1 -1
  13. data/lib/risu/models/item.rb +90 -18
  14. data/lib/risu/models/plugin.rb +2 -2
  15. data/lib/risu/models/reference.rb +93 -0
  16. data/lib/risu/parsers/nessus/nessus_sax_listener.rb +28 -30
  17. data/lib/risu/renderers.rb +6 -0
  18. data/lib/risu/renderers/nilrenderer.rb +25 -0
  19. data/lib/risu/templates/assets.rb +5 -2
  20. data/lib/risu/templates/cover_sheet.rb +1 -1
  21. data/lib/risu/templates/exec_summary.rb +19 -4
  22. data/lib/risu/templates/executive_summary.rb +20 -5
  23. data/lib/risu/templates/finding_statistics.rb +1 -1
  24. data/lib/risu/templates/findings_host.rb +27 -8
  25. data/lib/risu/templates/findings_summary.rb +14 -1
  26. data/lib/risu/templates/findings_summary_with_pluginid.rb +1 -1
  27. data/lib/risu/templates/graphs.rb +5 -1
  28. data/lib/risu/templates/host_summary.rb +8 -4
  29. data/lib/risu/templates/ms_patch_summary.rb +1 -1
  30. data/lib/risu/templates/ms_update_summary.rb +1 -1
  31. data/lib/risu/templates/notable.rb +1 -2
  32. data/lib/risu/templates/notable_detailed.rb +8 -8
  33. data/lib/risu/templates/pci_compliance.rb +1 -1
  34. data/lib/risu/templates/stig_findings_summary.rb +135 -0
  35. data/lib/risu/templates/technical_findings.rb +5 -17
  36. data/lib/risu/templates/template.rb +1 -1
  37. data/risu.gemspec +1 -2
  38. metadata +43 -28
@@ -1,8 +1,6 @@
1
- # encoding: utf-8
2
-
3
1
  module Risu
4
2
  module Exceptions
5
3
  end
6
4
  end
7
5
 
8
- require 'risu/exceptions/invaliddocument'
6
+ require 'risu/exceptions/invaliddocument'
@@ -319,7 +319,7 @@ module Risu
319
319
  g.data("OSX", osx) unless osx == 0
320
320
  g.data("FreeBSD", freebsd) unless freebsd == 0
321
321
  g.data("NetBSD", netbsd) unless netbsd == 0
322
- g.data("Cisco ISO", cisco) unless cisco == 0
322
+ g.data("Cisco IOS", cisco) unless cisco == 0
323
323
  g.data("VxWorks", vxworks) unless vxworks == 0
324
324
  g.data("VMware", esx) unless esx == 0
325
325
  g.data("AIX", aix) unless aix == 0
@@ -14,9 +14,16 @@ module Risu
14
14
  #
15
15
  # @return [ActiveRecord::Relation] with the query results
16
16
  def risks
17
- where(:severity => [0,1,2,3])
17
+ where(:severity => [0,1,2,3,4])
18
18
  end
19
19
 
20
+ # Queries for all the high risks in the database
21
+ #
22
+ # @return [ActiveRecord::Relation] with the query results
23
+ def critical_risks
24
+ where(:severity => 4)
25
+ end
26
+
20
27
  # Queries for all the high risks in the database
21
28
  #
22
29
  # @return [ActiveRecord::Relation] with the query results
@@ -45,6 +52,13 @@ module Risu
45
52
  where(:severity => 0)
46
53
  end
47
54
 
55
+ # Queries for all the unique Critical risks in the database
56
+ #
57
+ # @return [ActiveRecord::Relation] with the query results
58
+ def critical_risks_unique
59
+ where(:severity => 4).joins(:plugin).order("plugins.cvss_base_score").group(:plugin_id)
60
+ end
61
+
48
62
  # Queries for all the unique high risks in the database
49
63
  #
50
64
  # @return [ActiveRecord::Relation] with the query results
@@ -52,6 +66,13 @@ module Risu
52
66
  where(:severity => 3).joins(:plugin).order("plugins.cvss_base_score").group(:plugin_id)
53
67
  end
54
68
 
69
+ # Queries for all the unique Critical findings and sorts them by count
70
+ #
71
+ # @return [ActiveRecord::Relation] with the query results
72
+ def critical_risks_unique_sorted
73
+ select("items.*").select("count(*) as count_all").where(:severity => 4).group(:plugin_id).order("count_all DESC")
74
+ end
75
+
55
76
  # Queries for all the unique high findings and sorts them by count
56
77
  #
57
78
  # @return [ActiveRecord::Relation] with the query results
@@ -108,16 +129,16 @@ module Risu
108
129
  select("items.*").select("count(*) as count_all").where("svc_name != 'unknown' and svc_name != 'general'").group(:svc_name).order("count_all DESC").limit(limit)
109
130
  end
110
131
 
111
- # Queries for all the high risks by plugin
132
+ # Queries for all the Critical risks by plugin
112
133
  #
113
134
  # @param limit Limits the result to a specific number, default 10
114
135
  #
115
136
  # @return [ActiveRecord::Relation] with the query results
116
137
  def risks_by_plugin(limit=10)
117
- 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)
138
+ select("items.*").select("count(*) as count_all").joins(:plugin).where("plugin_id != 1").where(:severity => 4).group(:plugin_id).order("count_all DESC").limit(limit)
118
139
  end
119
140
 
120
- # Queries for all the high risks by host
141
+ # Queries for all the Critical risks by host
121
142
  #
122
143
  # @param limit Limits the result to a specific number, default 10
123
144
  #
@@ -125,7 +146,7 @@ module Risu
125
146
  #
126
147
  # @return [ActiveRecord::Relation] with the query results
127
148
  def risks_by_host(limit=10)
128
- select("items.*").select("count(*) as count_all").joins(:host).where("plugin_id != 1").where(:severity => 3).group(:host_id).order("count_all DESC").limit(limit)
149
+ select("items.*").select("count(*) as count_all").joins(:host).where("plugin_id != 1").where(:severity => 4).group(:host_id).order("count_all DESC").limit(limit)
129
150
  end
130
151
 
131
152
  # Queries for all the hosts with the Microsoft patch summary plugin (38153)
@@ -182,16 +203,19 @@ module Risu
182
203
  :background_colors => %w(white white)
183
204
  }
184
205
 
206
+ crit = Item.critical_risks.count
185
207
  high = Item.high_risks.count
186
208
  medium = Item.medium_risks.count
187
209
  low = Item.low_risks.count
188
210
  info = Item.info_risks.count
189
211
 
212
+ if crit == nil then crit = 0 end
190
213
  if high == nil then high = 0 end
191
214
  if medium == nil then medium = 0 end
192
215
  if low == nil then low = 0 end
193
216
  if info == nil then info = 0 end
194
217
 
218
+ g.data("Critical", crit, "purple")
195
219
  g.data("High", high, "red")
196
220
  g.data("Medium", medium, "orange")
197
221
  g.data("Low", low, "yellow")
@@ -199,30 +223,65 @@ module Risu
199
223
 
200
224
  StringIO.new(g.to_blob)
201
225
  end
226
+
227
+ #
228
+ #
229
+ def stig_findings(categeory="I")
230
+ where('plugin_id IN (:plugins)', :plugins => Plugin.where(:stig_severity => categeory).select(:id)).order("severity DESC")
231
+ end
232
+
233
+ # Generates a Graph of all the risks by severity
234
+ #
235
+ # @return [StringIO] Object containing the generated PNG image
236
+ def stigs_severity_graph
237
+ g = Gruff::Bar.new(GRAPH_WIDTH)
238
+ g.title = "Stigs By Severity"
239
+ g.sort = false
240
+ g.theme = {
241
+ :colors => %w(purple red orange yellow blue green black grey brown pink),
242
+ :background_colors => %w(white white)
243
+ }
244
+
245
+ i = Item.stig_findings("I").count
246
+ ii = Item.stig_findings("II").count
247
+ iii = Item.stig_findings("III").count
248
+
249
+ if i == nil then i = 0 end
250
+ if ii == nil then ii = 0 end
251
+ if iii == nil then iii = 0 end
252
+
253
+ g.data("Cat I", i, "purple")
254
+ g.data("Cat II", ii, "red")
255
+ g.data("Cat III", iii, "orange")
256
+
257
+ StringIO.new(g.to_blob)
258
+ end
202
259
 
203
260
  # @todo change Report.title to a real variable
204
261
  # @todo rewite this
205
262
  def risks_by_severity_graph_text
206
- high = Item.high_risks.count
207
- medium = Item.medium_risks.count
263
+ #crit = Item.crit_risks.count
264
+ #high = Item.high_risks.count
265
+ #medium = Item.medium_risks.count
208
266
 
209
- if high == nil then high = 0 end
210
- if medium == nil then medium = 0 end
267
+ #if crit == nil then crit = 0 end
268
+ #if high == nil then high = 0 end
269
+ #if medium == nil then medium = 0 end
211
270
 
212
271
  #percentage = high
213
272
 
214
- hosts_with_high = Hash.new
273
+ hosts_with_critical = Hash.new
215
274
 
216
- Item.high_risks.all.each do |item|
275
+ Item.critical_risks.all.each do |item|
217
276
  ip = Host.find_by_id(item.host_id).name
218
- if hosts_with_high[ip] == nil
219
- hosts_with_high[ip] = 1
277
+ if hosts_with_critical[ip] == nil
278
+ hosts_with_critical[ip] = 1
220
279
  end
221
280
 
222
- hosts_with_high[ip] = hosts_with_high[ip] + 1
281
+ hosts_with_critical[ip] = hosts_with_critical[ip] + 1
223
282
  end
224
283
 
225
- host_percent = (hosts_with_high.count.to_f / Host.all.count.to_f) * 100
284
+ host_percent = (hosts_with_critical.count.to_f / Host.all.count.to_f) * 100
226
285
 
227
286
  adjective = case host_percent
228
287
  when 0..5
@@ -286,7 +345,7 @@ module Risu
286
345
 
287
346
  #sqlite only @todo @fix
288
347
  def top_10_sorted_raw
289
- raw = Item.joins(:plugin).where(:severity => 3).order("cast(plugins.cvss_base_score as real)").count(:all, :group => :plugin_id)
348
+ raw = Item.joins(:plugin).where(:severity => 4).order("cast(plugins.cvss_base_score as real)").count(:all, :group => :plugin_id)
290
349
  data = Array.new
291
350
 
292
351
  raw.each do |vuln|
@@ -308,7 +367,7 @@ module Risu
308
367
 
309
368
  def top_10_sorted
310
369
  #raw = Item.where(:severity => 3).count(:all, :group => :plugin_id)
311
- raw = Item.joins(:plugin).where(:severity => 3).order(:cvss_base_score).count(:all, :group => :plugin_id)
370
+ raw = Item.joins(:plugin).where(:severity => 4).order(:cvss_base_score).count(:all, :group => :plugin_id)
312
371
  data = Array.new
313
372
 
314
373
  raw.each do |vuln|
@@ -330,6 +389,12 @@ module Risu
330
389
  return data
331
390
  end
332
391
 
392
+ # Returns a prawn pdf table for the top 10 notable findings
393
+ #
394
+ # @todo change this method to return a array/table and let the template render it
395
+ # @todo rename to notable_table also
396
+ #
397
+ # @param output device to write the table to
333
398
  def top_10_table(output)
334
399
  headers = ["Description", "Count"]
335
400
  header_widths = {0 => (output.bounds.width - 50), 1 => 50}
@@ -339,7 +404,14 @@ module Risu
339
404
  output.table([headers] + data[0..9], :header => true, :column_widths => header_widths, :width => output.bounds.width) do
340
405
  row(0).style(:font_style => :bold, :background_color => 'cccccc')
341
406
  cells.borders = [:top, :bottom, :left, :right]
342
- end
407
+ end
408
+ end
409
+
410
+ # Queries for all unique risks and sorts them by count
411
+ #
412
+ # @return [ActiveRecord::Relation] with the query results
413
+ def all_risks_unique_sorted
414
+ select("items.*").select("count(*) as count_all").group(:plugin_id).order("count_all DESC")
343
415
  end
344
416
 
345
417
  end
@@ -59,7 +59,7 @@ module Risu
59
59
  # @return Filename of the created graph
60
60
  def top_by_count_graph(limit=10)
61
61
  g = Gruff::Bar.new(GRAPH_WIDTH)
62
- g.title = sprintf "Top %d High Findings By Plugin", Item.risks_by_plugin(limit).all.count
62
+ g.title = sprintf "Top %d Critical Findings By Plugin", Item.risks_by_plugin(limit).all.count
63
63
  g.sort = false
64
64
  g.theme = {
65
65
  :colors => %w(red orange yellow blue green purple black grey brown pink),
@@ -80,7 +80,7 @@ module Risu
80
80
  else
81
81
  plugin_name = Plugin.find_by_id(plugin.plugin_id).plugin_name
82
82
  end
83
-
83
+
84
84
  if plugin_name =~ /^(MS\d{2}-\d{3}):/
85
85
  plugin_name = $1
86
86
  end
@@ -6,6 +6,99 @@ module Risu
6
6
  # @author Jacob Hammack
7
7
  class Reference < ActiveRecord::Base
8
8
  has_many :plugins
9
+
10
+ class << self
11
+
12
+ # Queries all unique CVEs
13
+ #
14
+ def cve
15
+ where(:reference_name => "cve").select('DISTINCT value')
16
+ end
17
+
18
+ # Queries all unique CPE
19
+ #
20
+ def cpe
21
+ where(:reference_name => "cpe").select('DISTINCT value')
22
+ end
23
+
24
+ # Queries all unique BID
25
+ #
26
+ def bid
27
+ where(:reference_name => "bid").select('DISTINCT value')
28
+ end
29
+
30
+ # Queries all unique see_also
31
+ #
32
+ def see_also
33
+ where(:reference_name => "see_also").select('DISTINCT value')
34
+ end
35
+
36
+ # Queries all unique IAVA
37
+ #
38
+ def iava
39
+ where(:reference_name => "iava").select('DISTINCT value')
40
+ end
41
+
42
+ # Queries all unique MSFT
43
+ #
44
+ def msft
45
+ where(:reference_name => "msft").select('DISTINCT value')
46
+ end
47
+
48
+ # Queries all unique OSvdb
49
+ #
50
+ def osvdb
51
+ where(:reference_name => "osvdb").select('DISTINCT value')
52
+ end
53
+
54
+ # Queries all unqiue cert refs
55
+ #
56
+ def cert
57
+ where(:reference_name => "cert").select('DISTINCT value')
58
+ end
59
+
60
+ #
61
+ #
62
+ def edbid
63
+ where(:reference_name => "edb-id").select('DISTINCT value')
64
+ end
65
+
66
+ #
67
+ #
68
+ def rhsa
69
+ where(:reference_name => "rhsa").select('DISTINCT value')
70
+ end
71
+
72
+ #
73
+ #
74
+ def secunia
75
+ where(:reference_name => "secunia").select('DISTINCT value')
76
+ end
77
+
78
+ #
79
+ #
80
+ def suse
81
+ where(:reference_name => "suse").select('DISTINCT value')
82
+ end
83
+
84
+ #
85
+ #
86
+ def dsa
87
+ where(:reference_name => "dsa").select('DISTINCT value')
88
+ end
89
+
90
+ #
91
+ #
92
+ def owasp
93
+ where(:reference_name => "owasp").select('DISTINCT value')
94
+ end
95
+
96
+ #
97
+ #
98
+ def cwe
99
+ where(:reference_name => "cwe").select('DISTINCT value')
100
+ end
101
+ end
9
102
  end
10
103
  end
11
104
  end
@@ -1,7 +1,7 @@
1
- # encoding: utf-8
2
-
3
1
  require 'risu'
4
2
 
3
+ ActiveRecord::Migration.verbose = false
4
+
5
5
  module Risu
6
6
  module Parsers
7
7
  module Nessus
@@ -16,8 +16,13 @@ module Risu
16
16
  #
17
17
  def initialize
18
18
  @vals = Hash.new
19
+
20
+ @valid_references = Array[
21
+ "cpe", "bid", "see_also", "xref", "cve", "iava", "msft",
22
+ "osvdb", "cert", "edb-id", "rhsa", "secunia", "suse", "dsa",
23
+ "owasp", "cwe"]
19
24
 
20
- @valid_elements = Array["see_also", "cve", "ReportItem", "xref", "bid", "plugin_version", "risk_factor",
25
+ @valid_elements = Array["ReportItem", "plugin_version", "risk_factor",
21
26
  "description", "cvss_base_score", "solution", "item", "plugin_output", "tag", "synopsis", "plugin_modification_date",
22
27
  "FamilyName", "FamilyItem", "Status", "vuln_publication_date", "ReportHost", "HostProperties", "preferenceName",
23
28
  "preferenceValues", "preferenceType", "fullName", "pluginId", "pluginName", "selectedValue", "selectedValue",
@@ -26,8 +31,11 @@ module Risu
26
31
  "Report", "Family", "Preferences", "PluginsPreferences", "FamilySelection", "IndividualPluginSelection", "PluginId",
27
32
  "pci-dss-compliance", "exploitability_ease", "cvss_temporal_vector", "exploit_framework_core", "cvss_temporal_score",
28
33
  "exploit_available", "metasploit_name", "exploit_framework_canvas", "canvas_package", "exploit_framework_metasploit",
29
- "plugin_type", "cpe", "exploithub_sku", "exploit_framework_exploithub", "stig_severity"]
34
+ "plugin_type", "exploithub_sku", "exploit_framework_exploithub", "stig_severity", "plugin_name", "fname",
35
+ ]
30
36
 
37
+ @valid_elements = @valid_elements + @valid_references
38
+
31
39
  # This makes adding new host properties really easy, except for the
32
40
  #MS patch numbers, this are handled differently.
33
41
  @valid_host_properties = {
@@ -60,14 +68,15 @@ module Risu
60
68
  "pcidss:dns_zone_transfer" => :pcidss_dns_zone_transfer,
61
69
  "pcidss:unprotected_mssql_db" => :pcidss_unprotected_mssql_db,
62
70
  "pcidss:obsolete_software" => :pcidss_obsolete_software,
63
- "pcidss:www:sql_injection" => :pcidss_www_sql_injection
71
+ "pcidss:www:sql_injection" => :pcidss_www_sql_injection,
72
+ "fname" => :fname
64
73
  }
65
74
  end
66
75
 
67
76
  # Callback for when the start of a xml element is reached
68
77
  #
69
- # @param element
70
- # @param attributes
78
+ # @param element XML element
79
+ # @param attributes Attributes for the XML element
71
80
  def on_start_element(element, attributes)
72
81
  @tag = element
73
82
  @vals[@tag] = ""
@@ -227,29 +236,17 @@ module Risu
227
236
  end if @attr != nil
228
237
  #We cannot handle the references in the same block as the rest of the ReportItem tag because
229
238
  #there tends to be more than of the different types of reference per ReportItem, this causes issue for a sax
230
- #parser. To solve this we do the references before the final plugin data
231
- when "cve"
232
- @cve = @plugin.references.create
233
- @cve.reference_name = "cve"
234
- @cve.value = @vals["cve"]
235
- @cve.save
236
- when "bid"
237
- @bid = @plugin.references.create
238
- @bid.reference_name = "bid"
239
- @bid.value = @vals["bid"]
240
- @bid.save
241
- when "see_also"
242
- @see_also = @plugin.references.create
243
- @see_also.reference_name = "see_also"
244
- @see_also.value = @vals["see_also"]
245
- @see_also.save
246
- when "xref"
247
- @xref = @plugin.references.create
248
- @xref.reference_name = "xref"
249
- @xref.value = @vals["xref"]
250
- @xref.save
239
+ #parser. To solve this we do the references before the final plugin data, Valid references must be added
240
+ #the @valid_reference array at the top to be parsed.
241
+ # *@valid_reference, does a 'when' on each element of the @valid_references array, pure magic
242
+ when *@valid_references
243
+ @ref = @plugin.references.create
244
+ @ref.reference_name = element
245
+ @ref.value = @vals["#{element}"]
246
+ @ref.save
251
247
  when "ReportItem"
252
248
  @ri.plugin_output = @vals["plugin_output"]
249
+ @ri.plugin_name = @vals["plugin_name"]
253
250
  @ri.save
254
251
 
255
252
  @plugin.attributes = {
@@ -257,6 +254,7 @@ module Risu
257
254
  :risk_factor => @vals["risk_factor"],
258
255
  :description => @vals["description"],
259
256
  :plugin_publication_date => @vals["plugin_publication_date"],
257
+ :plugin_modification_date => @vals["plugin_modification_date"],
260
258
  :synopsis => @vals["synopsis"],
261
259
  :plugin_type => @vals["plugin_type"],
262
260
  :cvss_vector => @vals["cvss_vector"],
@@ -272,10 +270,10 @@ module Risu
272
270
  :metasploit_name => @vals["metasploit_name"],
273
271
  :exploit_framework_canvas => @vals["exploit_framework_canvas"],
274
272
  :canvas_package => @vals["canvas_package"],
275
- :cpe => @vals["cpe"],
276
273
  :exploit_framework_exploithub => @vals["exploit_framework_exploithub"],
277
274
  :exploithub_sku => @vals["exploithub_sku"],
278
- :stig_severity => @vals["stig_severity"]
275
+ :stig_severity => @vals["stig_severity"],
276
+ :fname => @vals["fname"]
279
277
  }
280
278
  @plugin.save
281
279
  end