Class: Nessus

Inherits:
Object
  • Object
show all
Defined in:
lib/audit/lib/nessus_new.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Nessus

Returns a new instance of Nessus.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/audit/lib/nessus_new.rb', line 10

def initialize(options = {})
	options = {:host => "127.0.0.1", :port => "8834", :timeout => "120"}.merge(options)
	
	@host = options[:host]
	@port = options[:port]
	@timeout = options[:timeout]
	
	if options[:logger] then
		@logger = options[:logger]
	else
		@logger = Logger.new(STDOUT)
	end
	
	@token = nil
end

Instance Attribute Details

#tokenObject (readonly)

Returns the value of attribute token.



8
9
10
# File 'lib/audit/lib/nessus_new.rb', line 8

def token
  @token
end

Class Method Details

.get_policy_name_from_file(file) ⇒ Object



227
228
229
# File 'lib/audit/lib/nessus_new.rb', line 227

def self.get_policy_name_from_file(file)
	return (Document.new(File.new(file)).elements.inject("NessusClientData_v2/Policy/policyName", []) {|r, x| r << x.text})[0]
end

Instance Method Details

#delete_policy(id) ⇒ Object



81
82
83
84
85
# File 'lib/audit/lib/nessus_new.rb', line 81

def delete_policy(id)
	return execute_command("policy/delete", 
   	                           [{:value => "policy_id=#{id}"},
      	                         {:type => :cookie, :value => "token=#{@token}"}])
end

#delete_report(uuid) ⇒ Object



174
175
176
177
178
# File 'lib/audit/lib/nessus_new.rb', line 174

def delete_report(uuid)
	report = execute_command("report/delete",
	                       [{:value => "report=#{uuid}"},
	                        {:type => :cookie, :value => "token=#{@token}"}])
end

#execute_command(command, parameters, fail_on_error = true) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/audit/lib/nessus_new.rb', line 26

def execute_command(command, parameters,  fail_on_error = true)
	cmd = "curl --silent --max-time #{@timeout} --insecure "
	parameters.each do|param|
		if param[:type].nil? || param[:type] == :data then
			cmd << "--data \"#{param[:value]}\" "
		elsif param[:type] == :form then
			cmd << "--form \"#{param[:value]}\" "
		elsif param[:type] == :cookie then
			cmd << "--cookie \"#{param[:value]}\" "
		else
			raise "unknown parameter type #{param[:type]}"
		end
	end

	cmd << "https://#{@host}:#{@port}/#{command}"

	@logger.info {"Executing command: #{cmd}"}

	result_str = `#{cmd}`

	raise "unexpected return value from nessus command #{command} " unless result_str.class == String

	result = Document.new(result_str)
	status = result.elements.inject("reply/status", []) {|r, x| r << x.text}
	
	if fail_on_error then
		raise "Invalid return status for nessus command #{command}" unless status && status.length == 1 && status[0] == "OK"
	end
	
	return result
end

#get_policy_ids(policy_name) ⇒ Object



87
88
89
90
91
# File 'lib/audit/lib/nessus_new.rb', line 87

def get_policy_ids(policy_name)
	policies = execute_command("policy/list",
   	                               [{:type => :cookie, :value => "token=#{@token}"}])
	return policies.elements.inject("/reply/contents/policies/policy[policyName='#{policy_name}']/policyID", []) {|r, x| r << x.text}
end

#import_policy(filename) ⇒ Object



75
76
77
78
79
# File 'lib/audit/lib/nessus_new.rb', line 75

def import_policy(filename)
	return execute_command("file/policy/import", 
   	                           [{:value => "file=#{filename}"},
      	                         {:type => :cookie, :value => "token=#{@token}"}])
end

#import_policy_file(file) ⇒ Object



94
95
96
97
98
99
100
101
102
103
# File 'lib/audit/lib/nessus_new.rb', line 94

def import_policy_file(file)
	policy_name = Nessus.get_policy_name_from_file(file)
	policy_ids = get_policy_ids(policy_name)
	policy_ids.each do|id|
		delete_policy(id)
	end
	uploaded_name = upload_file(file)
	policies = import_policy(uploaded_name)
	return policies.elements.inject("/reply/contents/policies/policy[policyName='#{policy_name}']/policyID", []) {|r, x| r << x.text}[0]
end

#list_plugin_familiesObject



269
270
271
272
273
# File 'lib/audit/lib/nessus_new.rb', line 269

def list_plugin_families()
	reply = execute_command("plugins/list",
	                        [{:type => :cookie, :value => "token=#{@token}"}])
	return reply.elements.inject("/reply/contents/pluginFamilyList/family/familyName", []) {|r, x| r << x.text}
end

#list_plugin_family(family_name) ⇒ Object



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/audit/lib/nessus_new.rb', line 275

def list_plugin_family(family_name)
	reply = execute_command("plugins/list/family",
	                        [{:type => :cookie, :value => "token=#{@token}"},
	                         {:value => "family=#{family_name}"}])
	results = []
	
	reply.elements.each("/reply/contents/pluginList/plugin") do|elem|
		result = {}
		elem.elements.each do|subelem|
			result[subelem.name] = subelem.text
		end
		results << result
	end
	return results
end

#list_report(uuid) ⇒ Object



152
153
154
155
156
157
158
159
160
# File 'lib/audit/lib/nessus_new.rb', line 152

def list_report(uuid)
	reports = list_reports()
	
	report = reports.reject {|rep| rep["name"] != uuid}
	
	return nil if report.length != 1
	
	return report[0]
end

#list_reportsObject



137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/audit/lib/nessus_new.rb', line 137

def list_reports()
	reply = execute_command("report/list", [{:type => :cookie, :value => "token=#{@token}"}])
	
	results = []
	reply.elements.each("/reply/contents/reports/report") do|elem|
		result = {}
		elem.elements.each do|subelem|
			result[subelem.name] = subelem.text
		end
		results << result
	end
	
	return results
end

#login(username, password) ⇒ Object



58
59
60
61
62
63
64
# File 'lib/audit/lib/nessus_new.rb', line 58

def (username, password)
	result = execute_command("login", [{:value => "login=#{username}"}, {:value => "password=#{password}"}])
	token = result.elements.inject("reply/contents/token", []) {|r, x| r << x.text}
	raise "Invalid token during nessus login" unless token && token.length == 1

	@token = token[0]
end

#logoutObject



221
222
223
224
225
# File 'lib/audit/lib/nessus_new.rb', line 221

def logout()
	execute_command("logout",
	                [{:type => :cookie, :value => "token=#{@token}"}])
	@token = nil
end

#new_policy(options) ⇒ Object

Allowed values for configuration keys are:

"policy_name": "some_name"
"policy_shared": "0" if the policy is not shared, or "1" if it is shared
"SSH settings[entry]:SSH user name :": "root"
"SSH settings[file]:SSH private key to use :": {:type => :file, :file => "/tmp/key.pem"}
"plugin_selection.family.Service detection": "enabled", "disabled", "mixed"
"plugin_selection.individual_plugin.19679": "enabled", "disabled"
"SSH settings[radio]:Elevate privileges with :": "Nothing", "sudo", "su", ...


240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/audit/lib/nessus_new.rb', line 240

def new_policy(options)
	raise "missing option \"policy_name\"" unless options["policy_name"]
	raise "missing option \"policy_shared\"" unless options["policy_shared"]
	
	cmd_params = [{:type => :cookie, :value => "token=#{@token}"}]
	
	options.each do|key,value|
		if value.class() == String then
			cmd_params << {:value => "#{key}=#{value}"}
		elsif value.class() == Hash then
			if value[:type] == :file then
				filename = upload_file(value[:file])
				cmd_params << {:value => "#{key}=#{filename}"}
			else
				raise "Unknown value hash type '#{value[:type]}' for policy configuration key '#{key}'"
			end
		else
			raise "Unknown value type '#{value.class()} for policy configuration key '#{key}'"
		end
	end
	
	reply = execute_command("policy/add", cmd_params)
	policy_ids = reply.elements.inject("/reply/contents/policy/policyID", []) {|r, x| r << x.text}
	
	raise "Policy '#{options["policy_name"]}' already exists or was not imported (#{policy_ids.length()})" unless policy_ids.length() == 1
	
	return policy_ids[0]
end

#save_report(uuid, file) ⇒ Object



162
163
164
165
166
167
168
169
170
171
172
# File 'lib/audit/lib/nessus_new.rb', line 162

def save_report(uuid, file)
	report = execute_command("file/report/download",
	                       [{:value => "report=#{uuid}"},
	                        {:type => :cookie, :value => "token=#{@token}"}],
	                       false)
	if report.elements.inject("/NessusClientData_v2", []) {|r, x| r << x}.empty? then
		raise "error during save_report"
	end
	
	File.open(file, 'w') {|file| file << report.to_s}
end

#scan_status(uuid) ⇒ Object



130
131
132
133
134
135
# File 'lib/audit/lib/nessus_new.rb', line 130

def scan_status(uuid)
	report = list_report(uuid)
	return "unknown" unless report
	
	return report["status"]
end

#scan_targets(options) ⇒ Object

Scan targets completely and return when scan is finished

Note: Only one of :policy_id, :policy_name or :policy_file needs to be specified.

Parameters:

  • :targets

    Hosts to scan (String or Array of String)

  • :report_file

    Path to report file (String)

  • :policy_id

    Policy ID of policy to use (String)

  • :policy_name

    Name of policy to use (String)

  • :policy_file

    Path to policy file to import and use (String)

  • :delete_policy (optional)

    Delete policy when scan is finished (Boolean)

  • :delete_report (optional)

    Delete report when scan is finished (Boolean)



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/audit/lib/nessus_new.rb', line 190

def scan_targets(options)
	raise "Need parameter :targets" unless options[:targets]
	raise "Need parameter :report_file" unless options[:report_file]
	
	policy_id = ""
	
	if options[:policy_id] then
		policy_id = options[:policy_id]
	elsif options[:policy_name] then
		policy_ids = get_policy_ids(policy_name)
	
		raise "No policy with this name (#{policy_name}) found" if policy_ids.empty?
		raise "Several policies with this name (#{policy_name}) found" if policy_ids.length > 1
		policy_id = policy_ids[0]
	elsif options[:policy_file] then
		policy_id = import_policy_file(options[:policy_file])
	else
		raise "Need parameter :policy_file, :policy_name or :policy_id" 	
	end
	
	scan_uuid = start_scan(:policy_id => policy_id, :targets => options[:targets])
	while scan_status(scan_uuid) != "completed" do
		sleep 30
	end
	save_report(scan_uuid, options[:report_file])
	delete_policy(policy_id) if options[:delete_policy]
	delete_report(scan_uuid) if options[:delete_report]
	
	return scan_uuid
end

#start_scan(options) ⇒ Object

Start a new nessus scan

Parameters:

  • :policy_id

    The policy id of the policy to use (String)

  • :targets

    The scan targets (String or Array of String)



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/audit/lib/nessus_new.rb', line 109

def start_scan(options)
	raise "Missing parameter :policy_id" unless options[:policy_id]
	raise "Missing parameter :targets" unless options[:targets]
	
	scan_opts = [{:value => "policy_id=#{options[:policy_id]}"},
	             {:type => :cookie, :value => "token=#{@token}"}]
	
	if options[:targets].class() == String then
		scan_opts << {:value => "target=#{options[:targets]}"}
	elsif options[:targets].class() == Array then
		options[:targets].each do|target|
			scan_opts << {:value => "target=#{target}"}
		end
	else
		raise "Unknown target type #{options[:targets].class().name()}"
	end
	
	scan_reply = execute_command("scan/new",  scan_opts)
	return scan_reply.elements.inject("/reply/contents/scan/uuid", []) {|r, x| r << x.text}[0]
end

#upload_file(file) ⇒ Object



66
67
68
69
70
71
72
73
# File 'lib/audit/lib/nessus_new.rb', line 66

def upload_file(file)
	raise "File '#{file}' is not accessible" unless File.exist?(file) && File.readable?(file)

	reply = execute_command("file/upload", 
   	                    [{:type => :cookie, :value => "token=#{@token}"}, 
      	                  {:type => :form, :value => "Filedata=@#{file}"}])
	return reply.elements.inject("/reply/contents/fileUploaded", []) {|r, x| r << x.text}[0]
end