risu 1.4.9 → 1.5.0

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