risu 1.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/KNOWNISSUES.markdown +50 -0
- data/LICENSE +25 -0
- data/NEWS.markdown +112 -0
- data/README.markdown +126 -0
- data/Rakefile +37 -0
- data/TODO.markdown +69 -0
- data/bin/risu +12 -0
- data/lib/nessusdb.rb +38 -0
- data/lib/nessusdb/cli.rb +9 -0
- data/lib/nessusdb/cli/application.rb +402 -0
- data/lib/nessusdb/cli/banner.rb +25 -0
- data/lib/nessusdb/exceptions.rb +8 -0
- data/lib/nessusdb/exceptions/invaliddocument.rb +10 -0
- data/lib/nessusdb/listener.rb +274 -0
- data/lib/nessusdb/models.rb +18 -0
- data/lib/nessusdb/models/familyselection.rb +12 -0
- data/lib/nessusdb/models/host.rb +359 -0
- data/lib/nessusdb/models/individualpluginselection.rb +14 -0
- data/lib/nessusdb/models/item.rb +183 -0
- data/lib/nessusdb/models/plugin.rb +98 -0
- data/lib/nessusdb/models/pluginspreference.rb +12 -0
- data/lib/nessusdb/models/policy.rb +17 -0
- data/lib/nessusdb/models/reference.rb +13 -0
- data/lib/nessusdb/models/report.rb +26 -0
- data/lib/nessusdb/models/serverpreference.rb +13 -0
- data/lib/nessusdb/models/version.rb +12 -0
- data/lib/nessusdb/nessusdocument.rb +66 -0
- data/lib/nessusdb/parsers.rb +8 -0
- data/lib/nessusdb/prawn_templater.rb +38 -0
- data/lib/nessusdb/schema.rb +145 -0
- data/lib/nessusdb/templates/assets.rb +21 -0
- data/lib/nessusdb/templates/cover_sheet.rb +42 -0
- data/lib/nessusdb/templates/data/nessuslogo.jpg +0 -0
- data/lib/nessusdb/templates/exec_summary.rb +56 -0
- data/lib/nessusdb/templates/executive_summary.rb +182 -0
- data/lib/nessusdb/templates/finding_statistics.rb +23 -0
- data/lib/nessusdb/templates/findings_host.rb +49 -0
- data/lib/nessusdb/templates/findings_summary.rb +68 -0
- data/lib/nessusdb/templates/findings_summary_with_pluginid.rb +68 -0
- data/lib/nessusdb/templates/graphs.rb +33 -0
- data/lib/nessusdb/templates/host_summary.rb +40 -0
- data/lib/nessusdb/templates/ms_patch_summary.rb +37 -0
- data/lib/nessusdb/templates/ms_update_summary.rb +43 -0
- data/lib/nessusdb/templates/pci_compliance.rb +66 -0
- data/lib/nessusdb/templates/technical_findings.rb +116 -0
- data/risu.gemspec +44 -0
- metadata +247 -0
@@ -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,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,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,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
|