risu 1.4.4 → 1.4.5
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS.markdown +14 -1
- data/README.markdown +23 -41
- data/TODO.markdown +48 -39
- data/lib/risu.rb +4 -9
- data/lib/risu/base.rb +15 -0
- data/lib/risu/base/prawn_templater.rb +37 -0
- data/lib/risu/{schema.rb → base/schema.rb} +34 -15
- data/lib/risu/base/template_base.rb +23 -0
- data/lib/risu/base/template_manager.rb +106 -0
- data/lib/risu/base/templater.rb +37 -0
- data/lib/risu/cli/application.rb +28 -8
- data/lib/risu/models.rb +1 -2
- data/lib/risu/models/host.rb +147 -23
- data/lib/risu/models/item.rb +131 -43
- data/lib/risu/models/plugin.rb +1 -1
- data/lib/risu/models/report.rb +11 -1
- data/lib/risu/models/serverpreference.rb +0 -2
- data/lib/risu/models/servicedescription.rb +10 -0
- data/lib/risu/parsers.rb +2 -3
- data/lib/risu/parsers/nessus/nessus_document.rb +69 -0
- data/lib/risu/parsers/nessus/nessus_sax_listener.rb +278 -0
- data/lib/risu/templates/assets.rb +45 -18
- data/lib/risu/templates/cover_sheet.rb +70 -42
- data/lib/risu/templates/exec_summary.rb +64 -45
- data/lib/risu/templates/executive_summary.rb +185 -161
- data/lib/risu/templates/finding_statistics.rb +44 -17
- data/lib/risu/templates/findings_host.rb +70 -46
- data/lib/risu/templates/findings_summary.rb +78 -54
- data/lib/risu/templates/findings_summary_with_pluginid.rb +80 -54
- data/lib/risu/templates/graphs.rb +46 -19
- data/lib/risu/templates/host_summary.rb +62 -39
- data/lib/risu/templates/ms_patch_summary.rb +59 -35
- data/lib/risu/templates/ms_update_summary.rb +59 -35
- data/lib/risu/templates/pci_compliance.rb +88 -64
- data/lib/risu/templates/technical_findings.rb +132 -106
- data/lib/risu/templates/template.rb +24 -0
- metadata +12 -6
- data/lib/risu/listener.rb +0 -274
- data/lib/risu/nessusdocument.rb +0 -66
- data/lib/risu/prawn_templater.rb +0 -38
data/lib/risu/models/item.rb
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
|
3
3
|
module Risu
|
4
4
|
module Models
|
5
|
-
|
5
|
+
|
6
6
|
# Item Model
|
7
7
|
#
|
8
8
|
# @author Jacob Hammack <jacob.hammack@hammackj.com>
|
9
9
|
class Item < ActiveRecord::Base
|
10
10
|
belongs_to :host
|
11
11
|
belongs_to :plugin
|
12
|
-
|
12
|
+
|
13
13
|
class << self
|
14
14
|
|
15
15
|
# Queries for all risks in the database
|
@@ -18,98 +18,98 @@ module Risu
|
|
18
18
|
def risks
|
19
19
|
where(:severity => [0,1,2,3])
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
# Queries for all the high risks in the database
|
23
|
-
#
|
23
|
+
#
|
24
24
|
# @return [ActiveRecord::Relation] with the query results
|
25
25
|
def high_risks
|
26
26
|
where(:severity => 3)
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
# Queries for all the medium risks in the database
|
30
30
|
#
|
31
|
-
# @return [ActiveRecord::Relation] with the query results
|
31
|
+
# @return [ActiveRecord::Relation] with the query results
|
32
32
|
def medium_risks
|
33
33
|
where(:severity => 2)
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
# Queries for all the low risks in the database
|
37
37
|
#
|
38
38
|
# @return [ActiveRecord::Relation] with the query results
|
39
39
|
def low_risks
|
40
40
|
where(:severity => 1)
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
# Queries for all the info risks in the database
|
44
44
|
#
|
45
45
|
# @return [ActiveRecord::Relation] with the query results
|
46
46
|
def info_risks
|
47
47
|
where(:severity => 0)
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
# Queries for all the unique high risks in the database
|
51
51
|
#
|
52
52
|
# @return [ActiveRecord::Relation] with the query results
|
53
53
|
def high_risks_unique
|
54
54
|
where(:severity => 3).joins(:plugin).order("plugins.cvss_base_score").group(:plugin_id)
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
# Queries for all the unique high findings and sorts them by count
|
58
|
-
#
|
58
|
+
#
|
59
59
|
# @return [ActiveRecord::Relation] with the query results
|
60
60
|
def high_risks_unique_sorted
|
61
61
|
select("items.*").select("count(*) as count_all").where(:severity => 3).group(:plugin_id).order("count_all DESC")
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
# Queries for all the unique medium risks in the database
|
65
65
|
#
|
66
66
|
# @return [ActiveRecord::Relation] with the query results
|
67
67
|
def medium_risks_unique
|
68
68
|
where(:severity => 2).joins(:plugin).order(:cvss_base_score).group(:plugin_id)
|
69
69
|
end
|
70
|
-
|
70
|
+
|
71
71
|
# Queries for all the unique medium findings and sorts them by count
|
72
|
-
#
|
72
|
+
#
|
73
73
|
# @return [ActiveRecord::Relation] with the query results
|
74
74
|
def medium_risks_unique_sorted
|
75
75
|
select("items.*").select("count(*) as count_all").where(:severity => 2).group(:plugin_id).order("count_all DESC")
|
76
76
|
end
|
77
|
-
|
77
|
+
|
78
78
|
# Queries for all the unique low risks in the database
|
79
79
|
#
|
80
80
|
# @return [ActiveRecord::Relation] with the query results
|
81
81
|
def low_risks_unique
|
82
82
|
where(:severity => 1).joins(:plugin).order(:cvss_base_score).group(:plugin_id)
|
83
83
|
end
|
84
|
-
|
84
|
+
|
85
85
|
# Queries for all the unique low findings and sorts them by count
|
86
|
-
#
|
86
|
+
#
|
87
87
|
# @return [ActiveRecord::Relation] with the query results
|
88
88
|
def low_risks_unique_sorted
|
89
89
|
select("items.*").select("count(*) as count_all").where(:severity => 1).group(:plugin_id).order("count_all DESC")
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
# Queries for all the unique info risks in the database
|
93
93
|
#
|
94
94
|
# @return [ActiveRecord::Relation] with the query results
|
95
95
|
def info_risks_unique
|
96
96
|
where(:severity => 0).joins(:plugin).order(:cvss_base_score).group(:plugin_id)
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
# Queries for all the unique info findings and sorts them by count
|
100
|
-
#
|
100
|
+
#
|
101
101
|
# @return [ActiveRecord::Relation] with the query results
|
102
102
|
def info_risks_unique_sorted
|
103
103
|
select("items.*").select("count(*) as count_all").where(:severity => 0).group(:plugin_id).order("count_all DESC")
|
104
104
|
end
|
105
|
-
|
105
|
+
|
106
106
|
# Queries for all the risks grouped by service type, used for the Vulnerbilities by Service graph
|
107
107
|
#
|
108
108
|
# @return [ActiveRecord::Relation] with the query results
|
109
109
|
def risks_by_service(limit=10)
|
110
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
111
|
end
|
112
|
-
|
112
|
+
|
113
113
|
# Queries for all the high risks by plugin
|
114
114
|
#
|
115
115
|
# @todo update this
|
@@ -118,64 +118,152 @@ module Risu
|
|
118
118
|
def risks_by_plugin(limit=10)
|
119
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
120
|
end
|
121
|
-
|
121
|
+
|
122
122
|
# Queries for all the risks by host
|
123
123
|
#
|
124
124
|
# @return [ActiveRecord::Relation] with the query results
|
125
125
|
def risks_by_host(limit=10)
|
126
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
127
|
end
|
128
|
-
|
128
|
+
|
129
129
|
# Queries for all the hosts with the Microsoft patch summary plugin (38153)
|
130
130
|
#
|
131
131
|
# @return [ActiveRecord::Relation] with the query results
|
132
132
|
def ms_patches
|
133
133
|
where(:plugin_id => 38153).joins(:host)
|
134
134
|
end
|
135
|
-
|
135
|
+
|
136
136
|
# Queries for all host with the Microsoft Update Summary plugin(12028)
|
137
137
|
#
|
138
138
|
# @return [ActiveRecord::Relation] with the query results
|
139
139
|
def ms_update
|
140
140
|
where(:plugin_id => 12028).joins(:host)
|
141
141
|
end
|
142
|
-
|
142
|
+
|
143
143
|
# Generates a Graph of all the risks by service
|
144
|
-
#
|
144
|
+
#
|
145
145
|
# @return [StringIO] Object containing the generated PNG image
|
146
146
|
def risks_by_service_graph(limit=10)
|
147
147
|
g = Gruff::Pie.new(GRAPH_WIDTH)
|
148
|
-
g.title = sprintf "Top %d
|
148
|
+
g.title = sprintf "Top %d Services By Vulnerability", Item.risks_by_service(limit).all.count
|
149
149
|
g.sort = false
|
150
150
|
g.theme = {
|
151
|
-
:colors => %w(red
|
151
|
+
:colors => %w(red orange yellow blue green purple black grey brown pink),
|
152
152
|
:background_colors => %w(white white)
|
153
153
|
}
|
154
154
|
|
155
|
-
Item.risks_by_service(limit).all.each { |service|
|
155
|
+
Item.risks_by_service(limit).all.each { |service|
|
156
156
|
g.data(service.svc_name, Item.find(:all, :conditions => {:svc_name => service.svc_name}).count)
|
157
157
|
}
|
158
158
|
|
159
159
|
StringIO.new(g.to_blob)
|
160
160
|
end
|
161
161
|
|
162
|
+
def risks_by_service_graph_text
|
163
|
+
"This graph is a representation of the findings found by service. This graph can help " +
|
164
|
+
"understand what services are running on the network and if they are vulnerable, where " +
|
165
|
+
"the risks are and how they should be protected."
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
|
162
170
|
# Generates a Graph of all the risks by severity
|
163
171
|
#
|
164
172
|
# @return [StringIO] Object containing the generated PNG image
|
165
173
|
def risks_by_severity_graph
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
174
|
+
g = Gruff::Bar.new(GRAPH_WIDTH)
|
175
|
+
g.title = "Risks By Severity"
|
176
|
+
g.sort = false
|
177
|
+
g.theme = {
|
178
|
+
:colors => %w(red orange yellow blue green purple black grey brown pink),
|
179
|
+
:background_colors => %w(white white)
|
180
|
+
}
|
181
|
+
|
182
|
+
high = Item.high_risks.count
|
183
|
+
medium = Item.medium_risks.count
|
184
|
+
low = Item.low_risks.count
|
185
|
+
info = Item.info_risks.count
|
186
|
+
|
187
|
+
if high == nil then high = 0 end
|
188
|
+
if medium == nil then medium = 0 end
|
189
|
+
if low == nil then low = 0 end
|
190
|
+
if info == nil then info = 0 end
|
191
|
+
|
192
|
+
g.data("High", high, "red")
|
193
|
+
g.data("Medium", medium, "orange")
|
194
|
+
g.data("Low", low, "yellow")
|
195
|
+
g.data("Open Ports", info, "blue")
|
196
|
+
|
197
|
+
StringIO.new(g.to_blob)
|
198
|
+
end
|
199
|
+
|
200
|
+
# @todo change Report.title to a real variable
|
201
|
+
#
|
202
|
+
def risks_by_severity_graph_text
|
203
|
+
high = Item.high_risks.count
|
204
|
+
medium = Item.medium_risks.count
|
205
|
+
|
206
|
+
if high == nil then high = 0 end
|
207
|
+
if medium == nil then medium = 0 end
|
208
|
+
|
209
|
+
percentage = high
|
210
|
+
|
211
|
+
adjective = case percentage
|
212
|
+
when 0..5
|
213
|
+
"excellent"
|
214
|
+
when 6..10
|
215
|
+
"great"
|
216
|
+
when 11..20
|
217
|
+
"very good"
|
218
|
+
when 21..30
|
219
|
+
"good"
|
220
|
+
when 31..40
|
221
|
+
"fair"
|
222
|
+
else
|
223
|
+
"poor"
|
224
|
+
end
|
225
|
+
|
226
|
+
hosts_with_high = Hash.new
|
227
|
+
|
228
|
+
Item.high_risks.all.each do |item|
|
229
|
+
ip = Host.find_by_id(item.host_id).name
|
230
|
+
if hosts_with_high[ip] == nil
|
231
|
+
hosts_with_high[ip] = 1
|
232
|
+
end
|
233
|
+
|
234
|
+
hosts_with_high[ip] = hosts_with_high[ip] + 1
|
235
|
+
end
|
236
|
+
|
237
|
+
host_percent = (hosts_with_high.count.to_f / Host.all.count.to_f) * 100
|
238
|
+
|
239
|
+
percent_text = case host_percent
|
240
|
+
when 0..5
|
241
|
+
"This implies that only a handful of computers are missing patches, and the current patch management is working well."
|
242
|
+
when 6..20
|
243
|
+
"This implies that there is a minor patch management issue. If there is a patch management system, it should be checked for problems. " +
|
244
|
+
"Each host should also be inspected to be certain it can receive patches."
|
245
|
+
else
|
246
|
+
"This implies that there is a significant patch management problem on the network. Any patch management solutions should " +
|
247
|
+
"be inspected for issues and they should be correct as soon as possible. Each host should also be inspected to be certain it can receive patches."
|
248
|
+
end
|
249
|
+
|
250
|
+
graph_text = "This bar graph is a representation of the findings by severity; the " +
|
251
|
+
"graph shows that, overall, #{Report.title} has a #{adjective} handle on the patch " +
|
252
|
+
"management of the network.\n\n"
|
253
|
+
|
254
|
+
graph_text << "The majority of the high findings were found on #{host_percent.round}% of the total assessed computers. #{percent_text}\n\n"
|
255
|
+
|
256
|
+
graph_text << "The systems with high vulnerabilities represent the largest threat to the network, " +
|
257
|
+
"so patching this group is paramount to the overall network security. It only takes one high vulnerability " +
|
258
|
+
"to create a security incident.\n\n"
|
259
|
+
|
260
|
+
graph_text << "It should be noted that low findings and open ports represent the discovery "
|
261
|
+
graph_text << "of network services and open ports. Typically, these are not an indication of "
|
262
|
+
graph_text << "a serious problem and pose little to no threat. However, the correlation of "
|
263
|
+
graph_text << "data between the different severity levels could be used to determine degree "
|
264
|
+
graph_text << "of vulnerability for a given system.\n"
|
265
|
+
|
266
|
+
return graph_text
|
179
267
|
end
|
180
268
|
end
|
181
269
|
end
|
data/lib/risu/models/plugin.rb
CHANGED
@@ -64,7 +64,7 @@ module Risu
|
|
64
64
|
g.title = sprintf "Top %d High Findings By Plugin", Item.risks_by_plugin(limit).all.count
|
65
65
|
g.sort = false
|
66
66
|
g.theme = {
|
67
|
-
:colors => %w(red
|
67
|
+
:colors => %w(red orange yellow blue green purple black grey brown pink),
|
68
68
|
:background_colors => %w(white white)
|
69
69
|
}
|
70
70
|
|
data/lib/risu/models/report.rb
CHANGED
@@ -18,7 +18,17 @@ module Risu
|
|
18
18
|
#@scan_date = Host.where("start is not null").first[:start].to_s
|
19
19
|
#
|
20
20
|
def scan_date
|
21
|
-
Host.where("start is not null").first[:start]
|
21
|
+
Host.where("start is not null").first[:start]
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# @todo comment this
|
26
|
+
#
|
27
|
+
def scanner_nessus_ratings_text
|
28
|
+
text = "The vulnerability scanner used by #{Report.company} rates the findings as follows: High, Medium, Low and Open Ports. High findings represents a security hole, initially this is the highest rating a risk can get. These generally represent vulnerabilities that can lead to full system compromise due to missing security patches. High findings should be the first to be remediated as they generally leave the network wide open. Medium findings are considered a security warning; these are not as severe as high but should be evaluated on a risk-by-risk basis. These are typically configuration errors that can lead to information disclosures such as usernames, passwords, and configuration settings. Low findings are identified as security notes; these provide information the scanner discovered during the scanning process. The information includes items such as hostname, domain name, and MAC address. Open Port findings represent the open ports on each system that the scanner found during the scan process. These should be evaluated against firewall settings to test the firewall configurations.\n\n"
|
29
|
+
text << "After the scanner is complete, the scanner evaluates each finding and bases it on the Common Vulnerability Scoring System (CVSS) score assigned to each finding. Any findings with a CVSS base score of 10 are upgraded to a Critical finding. These represent vulnerabilities that are trivial to gain administrator access to the system, with little to no effort. For more information on the CVSS scoring system please visit: http://nvd.nist.gov/cvss.cfm.\n\n"
|
30
|
+
|
31
|
+
return text
|
22
32
|
end
|
23
33
|
end
|
24
34
|
end
|
data/lib/risu/parsers.rb
CHANGED
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Risu
|
4
|
+
module Parsers
|
5
|
+
module Nessus
|
6
|
+
# A Object to represet the Nessus xml file in memory
|
7
|
+
#
|
8
|
+
# @author Jacob Hammack <jacob.hammack@hammackj.com>
|
9
|
+
class NessusDocument
|
10
|
+
|
11
|
+
# Creates a instance of the NessusDocument class
|
12
|
+
#
|
13
|
+
def initialize document
|
14
|
+
@document = document
|
15
|
+
end
|
16
|
+
|
17
|
+
# Checks the validness of a NessusDocument
|
18
|
+
#
|
19
|
+
# @return [Boolean] True if valid, False if invalid
|
20
|
+
def valid?
|
21
|
+
if File.exist?(@document)
|
22
|
+
@parser = LibXML::XML::Parser.file @document
|
23
|
+
doc = @parser.parse
|
24
|
+
|
25
|
+
if doc.root.name == nil
|
26
|
+
return false
|
27
|
+
end
|
28
|
+
|
29
|
+
if doc.root.name == "NessusClientData_v2"
|
30
|
+
return true
|
31
|
+
elsif doc.root.name == "NessusClientData"
|
32
|
+
return false
|
33
|
+
else
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
else
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Invokes the SAX parser on the XML document
|
42
|
+
#
|
43
|
+
def parse
|
44
|
+
@parser = LibXML::XML::SaxParser.file @document
|
45
|
+
@parser.callbacks = NessusSaxListener.new
|
46
|
+
@parser.parse
|
47
|
+
end
|
48
|
+
|
49
|
+
# Fixes the ip field if nil and replaces it with the name if its an ip
|
50
|
+
#
|
51
|
+
def fix_ips
|
52
|
+
@hosts = Host.all
|
53
|
+
|
54
|
+
@hosts.each do |host|
|
55
|
+
if host.ip == nil
|
56
|
+
begin
|
57
|
+
ip = IPAddr.new host.name
|
58
|
+
host.ip = ip.to_string
|
59
|
+
host.save
|
60
|
+
rescue ArgumentError => ae
|
61
|
+
next
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,278 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'risu'
|
4
|
+
|
5
|
+
module Risu
|
6
|
+
module Parsers
|
7
|
+
module Nessus
|
8
|
+
# NessusSaxListener
|
9
|
+
#
|
10
|
+
#
|
11
|
+
# @author Jacob Hammack <jacob.hammack@hammackj.com>
|
12
|
+
class NessusSaxListener
|
13
|
+
include LibXML::XML::SaxParser::Callbacks
|
14
|
+
|
15
|
+
# Sets up a array of all valid xml fields
|
16
|
+
#
|
17
|
+
#
|
18
|
+
def initialize
|
19
|
+
@vals = Hash.new
|
20
|
+
|
21
|
+
@valid_elements = Array["see_also", "cve", "ReportItem", "xref", "bid", "plugin_version", "risk_factor",
|
22
|
+
"description", "cvss_base_score", "solution", "item", "plugin_output", "tag", "synopsis", "plugin_modification_date",
|
23
|
+
"FamilyName", "FamilyItem", "Status", "vuln_publication_date", "ReportHost", "HostProperties", "preferenceName",
|
24
|
+
"preferenceValues", "preferenceType", "fullName", "pluginId", "pluginName", "selectedValue", "selectedValue",
|
25
|
+
"name", "value", "preference", "plugin_publication_date", "cvss_vector", "patch_publication_date",
|
26
|
+
"NessusClientData_v2", "Policy", "PluginName", "ServerPreferences", "policyComments", "policyName", "PluginItem",
|
27
|
+
"Report", "Family", "Preferences", "PluginsPreferences", "FamilySelection", "IndividualPluginSelection", "PluginId",
|
28
|
+
"pci-dss-compliance", "exploitability_ease", "cvss_temporal_vector", "exploit_framework_core", "cvss_temporal_score",
|
29
|
+
"exploit_available", "metasploit_name", "exploit_framework_canvas", "canvas_package", "exploit_framework_metasploit",
|
30
|
+
"plugin_type", "cpe"]
|
31
|
+
|
32
|
+
# This makes adding new host properties really easy.
|
33
|
+
@valid_host_properties = {
|
34
|
+
"HOST_END" => :end ,
|
35
|
+
"mac-address" => :mac ,
|
36
|
+
"HOST_START" => :start ,
|
37
|
+
"operating-system" => :os,
|
38
|
+
"host-ip" => :ip ,
|
39
|
+
"host-fqdn" => :fqdn ,
|
40
|
+
"netbios-name" => :netbios ,
|
41
|
+
"local-checks-proto" => :local_checks_proto ,
|
42
|
+
"smb-login-used" => :smb_login_used ,
|
43
|
+
"ssh-auth-meth" => :ssh_auth_meth ,
|
44
|
+
"ssh-login-used" => :ssh_login_used ,
|
45
|
+
"pci-dss-compliance" => :pci_dss_compliance ,
|
46
|
+
"pci-dss-compliance:" => :pci_dss_compliance_ ,
|
47
|
+
"pcidss:compliance:failed" => :pcidss_compliance_failed,
|
48
|
+
"pcidss:compliance:passed" => :pcidss_compliance_passed,
|
49
|
+
"pcidss:deprecated_ssl" => :pcidss_deprecated_ssl,
|
50
|
+
"pcidss:expired_ssl_certificate" => :pcidss_expired_ssl_certificate,
|
51
|
+
"pcidss:high_risk_flaw" => :pcidss_high_risk_flaw,
|
52
|
+
"pcidss:medium_risk_flaw" => :pcidss_medium_risk_flaw,
|
53
|
+
"pcidss:reachable_db" => :pcidss_reachable_db,
|
54
|
+
"pcidss:www:xss" => :pcidss_www_xss,
|
55
|
+
"system-type" => :system_type
|
56
|
+
}
|
57
|
+
|
58
|
+
@valid_ms_patches = {
|
59
|
+
"MS11-030" => :ms11_030,
|
60
|
+
"MS11-026" => :ms11_026,
|
61
|
+
"MS11-034" => :ms11_034,
|
62
|
+
"MS11-021" => :ms11_021,
|
63
|
+
"MS11-029" => :ms11_029,
|
64
|
+
"MS11-023" => :ms11_023,
|
65
|
+
"MS11-022" => :ms11_022,
|
66
|
+
"MS09-027" => :ms09_027,
|
67
|
+
"MS11-033" => :ms11_033,
|
68
|
+
"MS11-019" => :ms11_019,
|
69
|
+
"MS11-024" => :ms11_024,
|
70
|
+
"MS11-031" => :ms11_031,
|
71
|
+
"MS11-020" => :ms11_020,
|
72
|
+
"MS11-018" => :ms11_018,
|
73
|
+
"MS11-028" => :ms11_028,
|
74
|
+
"MS11-032" => :ms11_032
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
# Callback for when the start of a xml element is reached
|
79
|
+
#
|
80
|
+
# @param element
|
81
|
+
# @param attributes
|
82
|
+
def on_start_element(element, attributes)
|
83
|
+
@tag = element
|
84
|
+
@vals[@tag] = ""
|
85
|
+
|
86
|
+
if !@valid_elements.include?(element)
|
87
|
+
puts "New XML element detected: #{element}. Please report this to #{Risu::EMAIL}"
|
88
|
+
end
|
89
|
+
|
90
|
+
case element
|
91
|
+
when "Policy"
|
92
|
+
@policy = Risu::Models::Policy.create
|
93
|
+
@policy.save
|
94
|
+
when "preference"
|
95
|
+
@sp = @policy.server_preferences.create
|
96
|
+
@sp.save
|
97
|
+
when "item"
|
98
|
+
@item = @policy.plugins_preferences.create
|
99
|
+
@item.save
|
100
|
+
when "FamilyItem"
|
101
|
+
@family = @policy.family_selections.create
|
102
|
+
@family.save
|
103
|
+
when "PluginItem"
|
104
|
+
@plugin_selection = @policy.individual_plugin_selections.create
|
105
|
+
@plugin_selection.save
|
106
|
+
when "Report"
|
107
|
+
@report = @policy.reports.create
|
108
|
+
@report.name = attributes["name"]
|
109
|
+
@report.save
|
110
|
+
when "ReportHost"
|
111
|
+
@rh = @report.hosts.create
|
112
|
+
@rh.name = attributes["name"]
|
113
|
+
@rh.save
|
114
|
+
when "tag"
|
115
|
+
unless attributes["name"] =~ /(MS\d\d-\d\d\d)/
|
116
|
+
@attr = if @valid_host_properties.keys.include?(attributes["name"])
|
117
|
+
attributes["name"]
|
118
|
+
else
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
puts "New HostProperties attribute: #{attributes["name"]}. Please report this to jacob.hammack@hammackj.com\n" if @attr.nil?
|
122
|
+
end
|
123
|
+
when "ReportItem"
|
124
|
+
@vals = Hash.new # have to clear this out or everything has the same references
|
125
|
+
@ri = @rh.items.create
|
126
|
+
if attributes["pluginID"] == "0"
|
127
|
+
@plugin = Risu::Models::Plugin.find_or_create_by_id(1)
|
128
|
+
else
|
129
|
+
@plugin = Risu::Models::Plugin.find_or_create_by_id(attributes["pluginID"])
|
130
|
+
end
|
131
|
+
|
132
|
+
@ri.port = attributes["port"]
|
133
|
+
@ri.svc_name = attributes["svc_name"]
|
134
|
+
@ri.protocol = attributes["protocol"]
|
135
|
+
@ri.severity = attributes["severity"]
|
136
|
+
|
137
|
+
@ri.plugin_id = @plugin.id
|
138
|
+
@plugin.plugin_name = attributes["pluginName"]
|
139
|
+
@plugin.family_name = attributes["pluginFamily"]
|
140
|
+
@plugin.save
|
141
|
+
@ri.save
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Called when the inner text of a element is reached
|
146
|
+
#
|
147
|
+
# @param text
|
148
|
+
def on_characters(text)
|
149
|
+
if @vals[@tag] == nil then
|
150
|
+
@vals[@tag] = text
|
151
|
+
else
|
152
|
+
@vals[@tag] << text
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Called when the end of the xml element is reached
|
157
|
+
#
|
158
|
+
# @param element
|
159
|
+
def on_end_element(element)
|
160
|
+
@tag = nil
|
161
|
+
case element
|
162
|
+
when "policyName"
|
163
|
+
@policy.attributes = {
|
164
|
+
:name => @vals["policyName"]
|
165
|
+
}
|
166
|
+
|
167
|
+
@policy.save
|
168
|
+
when "policyComments"
|
169
|
+
@policy.attributes = {
|
170
|
+
:comments => @vals["policyComments"]
|
171
|
+
}
|
172
|
+
|
173
|
+
@policy.save
|
174
|
+
when "preference"
|
175
|
+
@sp.attributes = {
|
176
|
+
:name => @vals["name"],
|
177
|
+
:value => @vals["value"]
|
178
|
+
}
|
179
|
+
@sp.save
|
180
|
+
|
181
|
+
#This takes a really long time, there is about 34,000 pluginIDs in this
|
182
|
+
#field and it takes about 36 minutes to parse just this info =\
|
183
|
+
#lets prepopulate the plugins table with the known pluginid's
|
184
|
+
#if @vals["name"] == "plugin_set"
|
185
|
+
# @all_plugins = @vals["value"].split(";")
|
186
|
+
#
|
187
|
+
# @all_plugins.each { |p|
|
188
|
+
# @plug = Plugin.find_or_create_by_id(p)
|
189
|
+
# @plug.save
|
190
|
+
# }
|
191
|
+
#end
|
192
|
+
when "item"
|
193
|
+
@item.attributes = {
|
194
|
+
:plugin_name => @vals["pluginName"],
|
195
|
+
:plugin_id => @vals["pluginId"],
|
196
|
+
:fullname => @vals["fullName"],
|
197
|
+
:preference_name => @vals["preferenceName"],
|
198
|
+
:preference_type => @vals["preferenceType"],
|
199
|
+
:preference_values => @vals["preferenceValues"],
|
200
|
+
:selected_values => @vals["selectedValue"]
|
201
|
+
}
|
202
|
+
|
203
|
+
@item.save
|
204
|
+
when "FamilyItem"
|
205
|
+
@family.attributes = {
|
206
|
+
:family_name => @vals["FamilyName"],
|
207
|
+
:status => @vals["Status"]
|
208
|
+
}
|
209
|
+
|
210
|
+
@family.save
|
211
|
+
when "PluginItem"
|
212
|
+
@plugin_selection.attributes = {
|
213
|
+
:plugin_id => @vals["PluginId"],
|
214
|
+
:plugin_name => @vals["PluginName"],
|
215
|
+
:family => @vals["Family"],
|
216
|
+
:status => @vals["Status"]
|
217
|
+
}
|
218
|
+
|
219
|
+
@plugin_selection.save
|
220
|
+
when "tag"
|
221
|
+
@rh.attributes = {@valid_host_properties[@attr] => @vals["tag"].gsub("\n", ",") } if @valid_host_properties.keys.include?(@attr)
|
222
|
+
@rh.save
|
223
|
+
#We cannot handle the references in the same block as the rest of the ReportItem tag because
|
224
|
+
#there tends to be more than of the different types of reference per ReportItem, this causes issue for a sax
|
225
|
+
#parser. To solve this we do the references before the final plugin data
|
226
|
+
when "cve"
|
227
|
+
@cve = @plugin.references.create
|
228
|
+
@cve.reference_name = "cve"
|
229
|
+
@cve.value = @vals["cve"]
|
230
|
+
@cve.save
|
231
|
+
when "bid"
|
232
|
+
@bid = @plugin.references.create
|
233
|
+
@bid.reference_name = "bid"
|
234
|
+
@bid.value = @vals["bid"]
|
235
|
+
@bid.save
|
236
|
+
when "see_also"
|
237
|
+
@see_also = @plugin.references.create
|
238
|
+
@see_also.reference_name = "see_also"
|
239
|
+
@see_also.value = @vals["see_also"]
|
240
|
+
@see_also.save
|
241
|
+
when "xref"
|
242
|
+
@xref = @plugin.references.create
|
243
|
+
@xref.reference_name = "xref"
|
244
|
+
@xref.value = @vals["xref"]
|
245
|
+
@xref.save
|
246
|
+
when "ReportItem"
|
247
|
+
@ri.plugin_output = @vals["plugin_output"]
|
248
|
+
@ri.save
|
249
|
+
|
250
|
+
@plugin.attributes = {
|
251
|
+
:solution => @vals["solution"],
|
252
|
+
:risk_factor => @vals["risk_factor"],
|
253
|
+
:description => @vals["description"],
|
254
|
+
:plugin_publication_date => @vals["plugin_publication_date"],
|
255
|
+
:synopsis => @vals["synopsis"],
|
256
|
+
:plugin_type => @vals["plugin_type"],
|
257
|
+
:cvss_vector => @vals["cvss_vector"],
|
258
|
+
:cvss_base_score => @vals["cvss_base_score"],
|
259
|
+
:vuln_publication_date => @vals["vuln_publication_date"],
|
260
|
+
:plugin_version => @vals["plugin_version"],
|
261
|
+
:cvss_temporal_score => @vals["cvss_temporal_score"],
|
262
|
+
:cvss_temporal_vector => @vals["cvss_temporal_vector"],
|
263
|
+
:exploitability_ease => @vals["exploitability_ease"],
|
264
|
+
:exploit_framework_core => @vals["exploit_framework_core"],
|
265
|
+
:exploit_available => @vals["exploit_available"],
|
266
|
+
:exploit_framework_metasploit => @vals["exploit_framework_metasploit"],
|
267
|
+
:metasploit_name => @vals["metasploit_name"],
|
268
|
+
:exploit_framework_canvas => @vals["exploit_framework_canvas"],
|
269
|
+
:canvas_package => @vals["canvas_package"],
|
270
|
+
:cpe => @vals["cpe"]
|
271
|
+
}
|
272
|
+
@plugin.save
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|