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.
- 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
data/lib/nessusdb/cli.rb
ADDED
@@ -0,0 +1,402 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module NessusDB
|
4
|
+
module CLI
|
5
|
+
|
6
|
+
# Application class for NessusDB
|
7
|
+
#
|
8
|
+
# @author Jacob Hammack <jacob.hammack@hammackj.com>
|
9
|
+
class Application
|
10
|
+
attr_accessor :database
|
11
|
+
|
12
|
+
#
|
13
|
+
#
|
14
|
+
def initialize
|
15
|
+
@options = {}
|
16
|
+
@database = {}
|
17
|
+
@report = {}
|
18
|
+
@blacklist = {}
|
19
|
+
|
20
|
+
@options[:debug] = false
|
21
|
+
end
|
22
|
+
|
23
|
+
# Creates a blank config file
|
24
|
+
#
|
25
|
+
def create_config(file=CONFIG_FILE)
|
26
|
+
File.open(file, 'w+') do |f|
|
27
|
+
f.write("report:\n")
|
28
|
+
f.write(" author: \n")
|
29
|
+
f.write(" title: \n")
|
30
|
+
f.write(" company: \n")
|
31
|
+
f.write(" classification: \n\n")
|
32
|
+
f.write("database:\n")
|
33
|
+
f.write(" adapter: \n")
|
34
|
+
f.write(" host: \n")
|
35
|
+
f.write(" port: \n")
|
36
|
+
f.write(" database: \n")
|
37
|
+
f.write(" username: \n")
|
38
|
+
f.write(" password: \n")
|
39
|
+
f.write(" timeout: \n\n")
|
40
|
+
#TODO blacklisting
|
41
|
+
#f.write("blacklist:\n")
|
42
|
+
#f.write(" ips: \n")
|
43
|
+
#f.write(" macs: \n")
|
44
|
+
#f.write(" plugins: \n\n")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Loads the configuration file
|
49
|
+
#
|
50
|
+
def load_config(file=CONFIG_FILE, in_memory_config=false)
|
51
|
+
if File.exists?(file) == true or in_memory_config == true
|
52
|
+
begin
|
53
|
+
if in_memory_config
|
54
|
+
yaml = YAML::load(file)
|
55
|
+
else
|
56
|
+
yaml = YAML::load(File.open(file))
|
57
|
+
end
|
58
|
+
|
59
|
+
@database = yaml["database"]
|
60
|
+
@report = yaml["report"]
|
61
|
+
|
62
|
+
puts @database.inspect if @options[:debug]
|
63
|
+
|
64
|
+
#If no values were entered put a default value in
|
65
|
+
@report.each do |k, v|
|
66
|
+
if v == nil
|
67
|
+
@report[k] = "No #{k}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
#@blacklist = yaml["blacklist"]
|
72
|
+
rescue => e
|
73
|
+
puts "[!] Error loading config! - #{e.message}"
|
74
|
+
exit
|
75
|
+
end
|
76
|
+
else
|
77
|
+
puts "[!] Config file does not exist!"
|
78
|
+
exit
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Initiator for [ActiveRecord] migrations.
|
83
|
+
#
|
84
|
+
def migrate(direction)
|
85
|
+
begin
|
86
|
+
if @database["adapter"] == nil
|
87
|
+
return false, "[!] Invalid database adapter, please check your config file"
|
88
|
+
end
|
89
|
+
|
90
|
+
ActiveRecord::Base.establish_connection(@database)
|
91
|
+
require 'nessusdb/schema'
|
92
|
+
Schema.migrate(direction)
|
93
|
+
|
94
|
+
if direction == :up
|
95
|
+
ver = Version.create
|
96
|
+
ver.version = NessusDB::VERSION
|
97
|
+
ver.save
|
98
|
+
end
|
99
|
+
|
100
|
+
rescue ActiveRecord::AdapterNotSpecified => ans
|
101
|
+
puts "[!] Database adapter not found, please check your config file"
|
102
|
+
puts "#{ans.message}\n #{ans.backtrace}" if @options[:debug]
|
103
|
+
|
104
|
+
exit
|
105
|
+
rescue ActiveRecord::AdapterNotFound => anf
|
106
|
+
puts "[!] Database adapter not found, please check your config file"
|
107
|
+
puts "#{ans.message}\n #{ans.backtrace}" if @options[:debug]
|
108
|
+
|
109
|
+
exit
|
110
|
+
rescue => e
|
111
|
+
puts "[!] Exception! #{e.message}\n#{e.backtrace}"
|
112
|
+
exit
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
#
|
118
|
+
def db_connect
|
119
|
+
begin
|
120
|
+
if @database["adapter"] == nil
|
121
|
+
puts "[!] #{@database['adapter']}" if @options[:debug]
|
122
|
+
|
123
|
+
return false, "[!] Invalid database adapter, please check your config file"
|
124
|
+
end
|
125
|
+
|
126
|
+
ActiveRecord::Base.establish_connection(@database)
|
127
|
+
ActiveRecord::Base.connection
|
128
|
+
|
129
|
+
rescue ActiveRecord::AdapterNotSpecified => ans
|
130
|
+
puts "[!] Database adapter not found, please check your config file"
|
131
|
+
|
132
|
+
puts "#{ans.message}\n #{ans.backtrace}" if @options[:debug]
|
133
|
+
|
134
|
+
exit
|
135
|
+
rescue ActiveRecord::AdapterNotFound => anf
|
136
|
+
puts "[!] Database adapter not found, please check your config file"
|
137
|
+
|
138
|
+
puts "#{anf.message}\n #{anf.backtrace}" if @options[:debug]
|
139
|
+
|
140
|
+
exit
|
141
|
+
rescue => e
|
142
|
+
puts "[!] Exception! #{e.message}\n #{e.backtrace}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
#
|
147
|
+
#
|
148
|
+
def test_connection?
|
149
|
+
begin
|
150
|
+
|
151
|
+
db_connect
|
152
|
+
|
153
|
+
if ActiveRecord::Base.connected? == true
|
154
|
+
return true, "[*] Connection Test Successful"
|
155
|
+
else
|
156
|
+
return false, "[!] Connection Test Failed"
|
157
|
+
end
|
158
|
+
rescue => e
|
159
|
+
puts "[!] Exception! #{e.message}\n #{e.backtrace}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Starts a console and executes anything in a block sent to it
|
164
|
+
#
|
165
|
+
def consolize &block
|
166
|
+
|
167
|
+
yield
|
168
|
+
|
169
|
+
IRB.setup(nil)
|
170
|
+
IRB.conf[:USE_READLINE] = true
|
171
|
+
IRB.conf[:PROMPT_MODE] = :SIMPLE
|
172
|
+
|
173
|
+
irb = IRB::Irb.new
|
174
|
+
IRB.conf[:MAIN_CONTEXT] = irb.context
|
175
|
+
|
176
|
+
irb.context.evaluate("require 'irb/completion'", 0)
|
177
|
+
|
178
|
+
trap("SIGINT") do
|
179
|
+
irb.signal_handle
|
180
|
+
end
|
181
|
+
catch(:IRB_EXIT) do
|
182
|
+
irb.eval_input
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Parses all the command line
|
187
|
+
#
|
188
|
+
def parse_options
|
189
|
+
begin
|
190
|
+
opts = OptionParser.new do |opt|
|
191
|
+
opt.banner = "#{APP_NAME} v#{VERSION}\nJacob Hammack\nhttp://www.hammackj.com\n\n"
|
192
|
+
opt.banner << "Usage: #{APP_NAME} [options] [files_to_parse]"
|
193
|
+
opt.separator('')
|
194
|
+
opt.separator("Reporting Options")
|
195
|
+
|
196
|
+
opt.on('-t','--template FILE','The filename of the template to use') do |option|
|
197
|
+
@options[:template] = option
|
198
|
+
end
|
199
|
+
|
200
|
+
opt.on('-o','--output-file FILE','The filename to output the generated report to') do |option|
|
201
|
+
@options[:output_file] = option
|
202
|
+
end
|
203
|
+
|
204
|
+
opt.separator('')
|
205
|
+
opt.separator('Configuration Options')
|
206
|
+
|
207
|
+
opt.on('--config-file FILE', "Loads configuration settings for the specified file. By default #{APP_NAME} loads #{CONFIG_FILE}") do |option|
|
208
|
+
if File.exists?(option) == true
|
209
|
+
@options[:config_file] = option
|
210
|
+
else
|
211
|
+
puts "[!] Specified config file does not exist. Please specify a file that exists."
|
212
|
+
exit
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
opt.on('--create-config-file [FILE]',"Creates a config file in the current directory with the specified name, Default is #{CONFIG_FILE}") do |option|
|
217
|
+
if option == nil
|
218
|
+
option = CONFIG_FILE
|
219
|
+
end
|
220
|
+
|
221
|
+
if File.exists?(option) == true
|
222
|
+
puts "[!] Config file already exists; If you wish to over-write this file please delete it."
|
223
|
+
else
|
224
|
+
if option == nil
|
225
|
+
create_config
|
226
|
+
else
|
227
|
+
create_config option
|
228
|
+
end
|
229
|
+
|
230
|
+
exit
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
opt.separator('')
|
235
|
+
opt.separator('Database Options')
|
236
|
+
|
237
|
+
opt.on('--test-connection','Tests the database connection settings') do |option|
|
238
|
+
@options[:test_connection] = option
|
239
|
+
end
|
240
|
+
|
241
|
+
opt.on('--create-tables','Creates the tables required for NessusDB') do |option|
|
242
|
+
@options[:create_tables] = option
|
243
|
+
end
|
244
|
+
|
245
|
+
opt.on('--drop-tables','Deletes the tables and data from NessusDB') do |option|
|
246
|
+
@options[:drop_tables] = option
|
247
|
+
end
|
248
|
+
|
249
|
+
opt.separator ''
|
250
|
+
opt.separator 'Other Options'
|
251
|
+
|
252
|
+
opt.on_tail('-v', '--version', "Shows application version information") do
|
253
|
+
puts "#{APP_NAME} - #{VERSION}"
|
254
|
+
exit
|
255
|
+
end
|
256
|
+
|
257
|
+
opt.on('-d','--debug','Enable Debug Mode (More verbose output)') do |option|
|
258
|
+
@options[:debug] = true
|
259
|
+
end
|
260
|
+
|
261
|
+
opt.on('--console', 'Starts an ActiveRecord console into the configured database') do |option|
|
262
|
+
@options[:console] = option
|
263
|
+
end
|
264
|
+
|
265
|
+
opt.on_tail("-?", "--help", "Show this message") do
|
266
|
+
puts opt.to_s + "\n"
|
267
|
+
exit
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
if ARGV.length != 0
|
272
|
+
opts.parse!
|
273
|
+
else
|
274
|
+
puts opts.to_s + "\n"
|
275
|
+
exit
|
276
|
+
end
|
277
|
+
rescue OptionParser::MissingArgument => m
|
278
|
+
puts opts.to_s + "\n"
|
279
|
+
exit
|
280
|
+
rescue OptionParser::InvalidOption => i
|
281
|
+
puts opts.to_s + "\n"
|
282
|
+
exit
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
#
|
287
|
+
#
|
288
|
+
def run
|
289
|
+
parse_options
|
290
|
+
|
291
|
+
if @options[:debug] == true
|
292
|
+
puts "[*] Enabling Debug Mode"
|
293
|
+
end
|
294
|
+
|
295
|
+
if @options[:config_file] != nil
|
296
|
+
load_config @options[:config_file]
|
297
|
+
else
|
298
|
+
load_config
|
299
|
+
end
|
300
|
+
|
301
|
+
db_connect
|
302
|
+
|
303
|
+
if @options[:console] != nil
|
304
|
+
consolize do
|
305
|
+
puts NessusDB::CLI::Banner
|
306
|
+
puts "NessusDB Console v#{VERSION}"
|
307
|
+
end
|
308
|
+
exit
|
309
|
+
end
|
310
|
+
|
311
|
+
if @options[:test_connection] != nil
|
312
|
+
result = test_connection?
|
313
|
+
|
314
|
+
puts "#{result[1]}"
|
315
|
+
exit
|
316
|
+
end
|
317
|
+
|
318
|
+
if @options[:create_tables] != nil
|
319
|
+
migrate(:up)
|
320
|
+
exit
|
321
|
+
end
|
322
|
+
|
323
|
+
if @options[:drop_tables] != nil
|
324
|
+
migrate(:down)
|
325
|
+
exit
|
326
|
+
end
|
327
|
+
|
328
|
+
if @options[:template] != nil and @options[:output_file] != nil
|
329
|
+
if File.exists?(@options[:template]) == false
|
330
|
+
puts "[!] Template \"#{@options[:template]}\" does not exist. Please check the filename"
|
331
|
+
exit
|
332
|
+
end
|
333
|
+
|
334
|
+
@findings = Report
|
335
|
+
|
336
|
+
@findings.author = @report["author"]
|
337
|
+
@findings.title = @report["title"]
|
338
|
+
@findings.company = @report["company"]
|
339
|
+
@findings.classification = @report["classification"]
|
340
|
+
|
341
|
+
template = PrawnTemplater.new(@options[:template], @findings, @options[:output_file])
|
342
|
+
template.generate
|
343
|
+
end
|
344
|
+
|
345
|
+
ARGV.each do |file|
|
346
|
+
begin
|
347
|
+
parse_file file
|
348
|
+
|
349
|
+
rescue NessusDB::Exceptions::InvalidDocument => id
|
350
|
+
puts "[!] #{id.message}"
|
351
|
+
next
|
352
|
+
rescue ActiveRecord::StatementInvalid => si
|
353
|
+
puts "[!] Please run nessusdb --create-tables, to create the required database schema!"
|
354
|
+
exit
|
355
|
+
rescue => e
|
356
|
+
puts e.inspect
|
357
|
+
puts "[!] Error: #{file}"
|
358
|
+
next
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
# Handles the parsing of a single file
|
364
|
+
#
|
365
|
+
def parse_file file
|
366
|
+
begin
|
367
|
+
puts "[*] Parsing #{file}..."
|
368
|
+
tstart = Time.new
|
369
|
+
|
370
|
+
if File.exists?(file) == false
|
371
|
+
raise NessusDB::Exceptions::InvalidDocument, "[!] Document does not exist - #{file}"
|
372
|
+
end
|
373
|
+
|
374
|
+
doc = NessusDocument.new file
|
375
|
+
if doc.valid? == true
|
376
|
+
doc.parse
|
377
|
+
|
378
|
+
puts "[*] Fixing IP Address field"
|
379
|
+
|
380
|
+
doc.fix_ips
|
381
|
+
|
382
|
+
else
|
383
|
+
raise NessusDB::Exceptions::InvalidDocument, "[!] Invalid Document - #{file}"
|
384
|
+
end
|
385
|
+
|
386
|
+
printf "[*] Finished parsing %s. Parse took %.02f seconds\n", file, Time.now - tstart
|
387
|
+
rescue Interrupt => i
|
388
|
+
puts "[!] Parse cancelled!"
|
389
|
+
exit(1)
|
390
|
+
rescue Mysql::Error => m
|
391
|
+
if m.errno == 1146
|
392
|
+
puts "[!] Error: Tables were not created. Please run nessusdb --create-tables"
|
393
|
+
exit(1)
|
394
|
+
end
|
395
|
+
rescue => e
|
396
|
+
puts "[!] #{e.message}\n #{e.backtrace.join("\n")}\n"
|
397
|
+
exit(1)
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#Cool random banner stuff for the cli, based on the metasploit random banner stuff
|
4
|
+
|
5
|
+
module NessusDB
|
6
|
+
module CLI
|
7
|
+
module Banner
|
8
|
+
Banners =
|
9
|
+
[
|
10
|
+
'
|
11
|
+
_ _
|
12
|
+
_ __ ___ ___ ___ _ _ ___ __| | |__
|
13
|
+
| \'_ \ / _ \/ __/ __| | | / __|/ _` | \'_ \
|
14
|
+
| | | | __/\__ \__ \ |_| \__ \ (_| | |_) |
|
15
|
+
|_| |_|\___||___/___/\__,_|___/\__,_|_.__/
|
16
|
+
|
17
|
+
'
|
18
|
+
]
|
19
|
+
|
20
|
+
def self.to_s
|
21
|
+
Banners[rand(Banners.length)]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|