librex 0.0.33 → 0.0.34

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.
@@ -3,7 +3,7 @@
3
3
  A non-official re-packaging of the Rex library as a gem for easy of usage of the Metasploit REX framework in a non Metasploit application. I received permission from HDM to create this package.
4
4
 
5
5
  Currently based on:
6
- SVN Revision: 12724
6
+ SVN Revision: 12756
7
7
 
8
8
  # Credits
9
9
  The Metasploit development team <http://www.metasploit.com>
@@ -0,0 +1,363 @@
1
+ require File.join(File.expand_path(File.dirname(__FILE__)),"nokogiri_doc_mixin")
2
+
3
+ module Rex
4
+ module Parser
5
+
6
+ # If Nokogiri is available, define Template document class.
7
+ load_nokogiri && class NexposeRawDocument < Nokogiri::XML::SAX::Document
8
+
9
+ include NokogiriDocMixin
10
+
11
+ attr_reader :tests
12
+
13
+ # Triggered every time a new element is encountered. We keep state
14
+ # ourselves with the @state variable, turning things on when we
15
+ # get here (and turning things off when we exit in end_element()).
16
+ def start_element(name=nil,attrs=[])
17
+ attrs = normalize_attrs(attrs)
18
+ block = @block
19
+ @state[:current_tag][name] = true
20
+ case name
21
+ when "nodes" # There are two main sections, nodes and VulnerabilityDefinitions
22
+ @tests = []
23
+ when "node"
24
+ record_host(attrs)
25
+ when "name"
26
+ @state[:has_text] = true
27
+ when "endpoint"
28
+ record_service(attrs)
29
+ when "service"
30
+ record_service_info(attrs)
31
+ when "fingerprint"
32
+ record_service_fingerprint(attrs)
33
+ when "os"
34
+ record_os_fingerprint(attrs)
35
+ when "test" # All the vulns tested for
36
+ record_host_test(attrs)
37
+ record_service_test(attrs)
38
+ when "vulnerability"
39
+ record_vuln(attrs)
40
+ when "reference"
41
+ @state[:has_text] = true
42
+ record_reference(attrs)
43
+ end
44
+ end
45
+
46
+ # This breaks on xml-encoded characters, so need to append
47
+ def characters(text)
48
+ return unless @state[:has_text]
49
+ @text ||= ""
50
+ @text << text
51
+ end
52
+
53
+ # When we exit a tag, this is triggered.
54
+ def end_element(name=nil)
55
+ block = @block
56
+ case name
57
+ when "node" # Wrap it up
58
+ collect_host_data
59
+ host_object = report_host &block
60
+ report_services(host_object)
61
+ report_fingerprint(host_object)
62
+ # Reset the state once we close a host
63
+ @state.delete_if {|k| k.to_s !~ /^(current_tag|in_nodes)$/}
64
+ @report_data = {:wspace => @args[:wspace]}
65
+ when "name"
66
+ collect_hostname
67
+ @state[:has_text] = false
68
+ when "endpoint"
69
+ collect_service_data
70
+ when "os"
71
+ collect_os_fingerprints
72
+ when "test"
73
+ save_test
74
+ when "vulnerability"
75
+ collect_vuln_info
76
+ report_vuln(&block)
77
+ @state.delete_if {|k| k.to_s !~ /^(current_tag|in_vulndefs)$/}
78
+ when "reference"
79
+ @state[:has_text] = false
80
+ collect_reference
81
+ @text = nil
82
+ end
83
+ @state[:current_tag].delete name
84
+ end
85
+
86
+ def collect_reference
87
+ return unless in_tag("references")
88
+ return unless in_tag("vulnerability")
89
+ @state[:ref][:value] = @text.to_s.strip
90
+ @report_data[:refs] ||= []
91
+ @report_data[:refs] << @state[:ref]
92
+ @state[:ref] = nil
93
+ end
94
+
95
+ def collect_vuln_info
96
+ return unless in_tag("VulnerabilityDefinitions")
97
+ return unless in_tag("vulnerability")
98
+ return unless @state[:vuln]
99
+ vuln = @state[:vuln]
100
+ vuln[:refs] = @report_data[:refs]
101
+ @report_data[:vuln] = vuln
102
+ @state[:vuln] = nil
103
+ @report_data[:refs] = nil
104
+ end
105
+
106
+ def report_vuln(&block)
107
+ return unless in_tag("VulnerabilityDefinitions")
108
+ return unless @report_data[:vuln]
109
+ return unless @report_data[:vuln][:matches].kind_of? Array
110
+ refs = normalize_references(@report_data[:vuln][:refs])
111
+ refs << "NEXPOSE-#{report_data[:vuln]["id"]}"
112
+ db.emit(:vuln, [refs.last,@report_data[:vuln][:matches].size], &block)
113
+ data = {
114
+ :workspace => @args[:wspace],
115
+ :name => refs.last,
116
+ :info => @report_data[:vuln]["title"],
117
+ :refs => refs.uniq
118
+ }
119
+ hosts_keys = {}
120
+ @report_data[:vuln][:matches].each do |match|
121
+ host_data = data.dup
122
+ host_data[:host] = match[:host]
123
+ host_data[:port] = match[:port] if match[:port]
124
+ host_data[:proto] = match[:protocol] if match[:protocol]
125
+ db.report_vuln(host_data)
126
+ if match[:key]
127
+ hosts_keys[host_data[:host]] ||= []
128
+ hosts_keys[host_data[:host]] << match[:key]
129
+ end
130
+ end
131
+ report_key_note(hosts_keys,data)
132
+ @report_data[:vuln] = nil
133
+ end
134
+
135
+ def report_key_note(hosts_keys,data)
136
+ return if hosts_keys.empty?
137
+ hosts_keys.each do |key_host,key_values|
138
+ key_note = {
139
+ :workspace => @args[:wspace],
140
+ :host => key_host,
141
+ :type => "host.vuln.nexpose_keys",
142
+ :data => {},
143
+ :update => :unique_data
144
+ }
145
+ key_values.each do |key_value|
146
+ key_note[:data][data[:name]] ||= []
147
+ next if key_note[:data][data[:name]].include? key_value
148
+ key_note[:data][data[:name]] << key_value
149
+ end
150
+ db.report_note(key_note)
151
+ end
152
+ end
153
+
154
+ def record_reference(attrs)
155
+ return unless in_tag("VulnerabilityDefinitions")
156
+ return unless in_tag("vulnerability")
157
+ @state[:ref] = attr_hash(attrs)
158
+ end
159
+
160
+ def record_vuln(attrs)
161
+ return unless in_tag("VulnerabilityDefinitions")
162
+ vuln = attr_hash(attrs)
163
+ matching_tests = @tests.select {|x| x[:id] == vuln["id"].downcase}
164
+ return if matching_tests.empty?
165
+ @state[:vuln] = vuln
166
+ @state[:vuln][:matches] = matching_tests
167
+ end
168
+
169
+ def save_test
170
+ return unless in_tag("nodes")
171
+ return unless in_tag("node")
172
+ return unless @state[:test]
173
+ test = { :id => @state[:test][:id]}
174
+ test[:host] = @state[:address]
175
+ test[:port] = @state[:test][:port] if @state[:test][:port]
176
+ test[:protocol] = @state[:test][:protocol] if @state[:test][:protocol]
177
+ test[:key] = @state[:test][:key] if @state[:test][:key]
178
+ @tests << test
179
+ @state[:test] = nil
180
+ end
181
+
182
+ def record_os_fingerprint(attrs)
183
+ return unless in_tag("nodes")
184
+ return unless in_tag("fingerprints")
185
+ return unless in_tag("node")
186
+ return if in_tag("service")
187
+ @state[:os] = attr_hash(attrs)
188
+ end
189
+
190
+ # Just keep the highest scoring, which is usually the most vague. :(
191
+ def collect_os_fingerprints
192
+ @report_data[:os] ||= {}
193
+ return unless @state[:os]["certainty"].to_f > 0
194
+ return if @report_data[:os]["os_certainty"].to_f > @state[:os]["certainty"].to_f
195
+ @report_data[:os] = {} # Zero it out if we're replacing it.
196
+ @report_data[:os]["os_certainty"] = @state[:os]["certainty"]
197
+ @report_data[:os]["os_vendor"] = @state[:os]["vendor"]
198
+ @report_data[:os]["os_family"] = @state[:os]["family"]
199
+ @report_data[:os]["os_product"] = @state[:os]["product"]
200
+ @report_data[:os]["os_version"] = @state[:os]["version"]
201
+ @report_data[:os]["os_arch"] = @state[:os]["arch"]
202
+ end
203
+
204
+ # Just taking the first one.
205
+ def collect_hostname
206
+ if in_tag("node")
207
+ @state[:hostname] ||= @text.to_s.strip if @text
208
+ @text = nil
209
+ end
210
+ end
211
+
212
+ def record_service_fingerprint(attrs)
213
+ return unless in_tag("nodes")
214
+ return unless in_tag("node")
215
+ return unless in_tag("service")
216
+ return unless in_tag("fingerprint")
217
+ @state[:service_fingerprint] = attr_hash(attrs)
218
+ end
219
+
220
+ def record_service_info(attrs)
221
+ return unless in_tag("nodes")
222
+ return unless in_tag("node")
223
+ return unless in_tag("service")
224
+ @state[:service].merge! attr_hash(attrs)
225
+ end
226
+
227
+ def report_fingerprint(host_object)
228
+ return unless host_object.kind_of? ::Msf::DBManager::Host
229
+ return unless @report_data[:os].kind_of? Hash
230
+ note = {
231
+ :workspace => host_object.workspace,
232
+ :host => host_object,
233
+ :type => "host.os.nexpose_fingerprint",
234
+ :data => {
235
+ :family => @report_data[:os]["os_family"],
236
+ :certainty => @report_data[:os]["os_certainty"]
237
+ }
238
+ }
239
+ note[:data][:vendor] = @report_data[:os]["os_vendor"] if @report_data[:os]["os_vendor"]
240
+ note[:data][:product] = @report_data[:os]["os_product"] if @report_data[:os]["os_prduct"]
241
+ note[:data][:version] = @report_data[:os]["os_version"] if @report_data[:os]["os_version"]
242
+ note[:data][:arch] = @report_data[:os]["os_arch"] if @report_data[:os]["os_arch"]
243
+ db.report_note(note)
244
+ end
245
+
246
+ def report_services(host_object)
247
+ return unless host_object.kind_of? ::Msf::DBManager::Host
248
+ return unless @report_data[:ports]
249
+ return if @report_data[:ports].empty?
250
+ reported = []
251
+ @report_data[:ports].each do |svc|
252
+ reported << db.report_service(svc.merge(:host => host_object))
253
+ end
254
+ reported
255
+ end
256
+
257
+ def record_service(attrs)
258
+ return unless in_tag("nodes")
259
+ return unless in_tag("node")
260
+ return unless in_tag("endpoint")
261
+ @state[:service] = attr_hash(attrs)
262
+ end
263
+
264
+ def collect_service_data
265
+ return unless in_tag("node")
266
+ return unless in_tag("endpoint")
267
+ port_hash = {}
268
+ @report_data[:ports] ||= []
269
+ @state[:service].each do |k,v|
270
+ case k
271
+ when "protocol"
272
+ port_hash[:protocol] = v
273
+ when "port"
274
+ port_hash[:port] = v
275
+ when "status"
276
+ port_hash[:status] = (v == "open" ? Msf::ServiceState::Open : Msf::ServiceState::Closed)
277
+ end
278
+ end
279
+ if @state[:service]
280
+ port_hash[:name] = @state[:service]["name"] if @state[:service]["name"] != "<unknown>"
281
+ end
282
+ if @state[:service_fingerprint]
283
+ info = []
284
+ info << @state[:service_fingerprint]["product"] if @state[:service_fingerprint]["product"]
285
+ info << @state[:service_fingerprint]["version"] if @state[:service_fingerprint]["version"]
286
+ port_hash[:info] = info.join(" ") if info[0]
287
+ end
288
+ @report_data[:ports] << port_hash
289
+ end
290
+
291
+ def actually_vulnerable(test)
292
+ return false unless test.has_key? "status"
293
+ return false unless test.has_key? "id"
294
+ ['vulnerable-exploited', 'vulnerable-version', 'potential'].include? test["status"]
295
+ end
296
+
297
+ def record_host_test(attrs)
298
+ return unless in_tag("nodes")
299
+ return unless in_tag("node")
300
+ return if in_tag("service")
301
+ return unless in_tag("tests")
302
+ test = attr_hash(attrs)
303
+ return unless actually_vulnerable(test)
304
+ @state[:test] = {:id => test["id"].downcase}
305
+ @state[:test][:key] = test["key"] if test["key"]
306
+ end
307
+
308
+ def record_service_test(attrs)
309
+ return unless in_tag("nodes")
310
+ return unless in_tag("node")
311
+ return unless in_tag("service")
312
+ return unless in_tag("tests")
313
+ test = attr_hash(attrs)
314
+ return unless actually_vulnerable(test)
315
+ @state[:test] = {
316
+ :id => test["id"].downcase,
317
+ :port => @state[:service]["port"],
318
+ :protocol => @state[:service]["protocol"],
319
+ }
320
+ @state[:test][:key] = test["key"] if test["key"]
321
+ end
322
+
323
+ def record_host(attrs)
324
+ return unless in_tag("nodes")
325
+ host_attrs = attr_hash(attrs)
326
+ if host_attrs["status"] == "alive"
327
+ @state[:host_is_alive] = true
328
+ @state[:address] = host_attrs["address"]
329
+ @state[:mac] = host_attrs["hardware-address"] if host_attrs["hardware-address"]
330
+ end
331
+ end
332
+
333
+ def collect_host_data
334
+ return unless in_tag("node")
335
+ @report_data[:host] = @state[:address]
336
+ @report_data[:state] = Msf::HostState::Alive
337
+ @report_data[:name] = @state[:hostname] if @state[:hostname]
338
+ if @state[:mac]
339
+ if @state[:mac] =~ /[0-9a-fA-f]{12}/
340
+ @report_data[:mac] = @state[:mac].scan(/.{2}/).join(":")
341
+ else
342
+ @report_data[:mac] = @state[:mac]
343
+ end
344
+ end
345
+ end
346
+
347
+ def report_host(&block)
348
+ if host_is_okay
349
+ db.emit(:address,@report_data[:host],&block) if block
350
+ host_object = db.report_host( @report_data.merge(
351
+ :workspace => @args[:wspace] ) )
352
+ if host_object
353
+ db.report_import_note(host_object.workspace, host_object)
354
+ end
355
+ host_object
356
+ end
357
+ end
358
+
359
+ end
360
+
361
+ end
362
+ end
363
+
@@ -0,0 +1,329 @@
1
+ require File.join(File.expand_path(File.dirname(__FILE__)),"nokogiri_doc_mixin")
2
+
3
+ module Rex
4
+ module Parser
5
+
6
+ # If Nokogiri is available, define Nexpose document class.
7
+ load_nokogiri && class NexposeSimpleDocument < Nokogiri::XML::SAX::Document
8
+
9
+ include NokogiriDocMixin
10
+
11
+ attr_reader :text
12
+
13
+ # Triggered every time a new element is encountered. We keep state
14
+ # ourselves with the @state variable, turning things on when we
15
+ # get here (and turning things off when we exit in end_element()).
16
+ def start_element(name=nil,attrs=[])
17
+ attrs = normalize_attrs(attrs)
18
+ block = @block
19
+ @state[:current_tag][name] = true
20
+ case name
21
+ when "device"
22
+ record_device(attrs)
23
+ when "service"
24
+ record_service(attrs)
25
+ when "fingerprint"
26
+ record_service_fingerprint(attrs)
27
+ record_host_fingerprint(attrs)
28
+ when "description"
29
+ @state[:has_text] = true
30
+ record_host_fingerprint_data(name,attrs)
31
+ when "vendor", "family", "product", "version", "architecture"
32
+ @state[:has_text] = true
33
+ record_host_fingerprint_data(name,attrs)
34
+ when "vulnerability"
35
+ record_service_vuln(attrs)
36
+ record_host_vuln(attrs)
37
+ when "id"
38
+ @state[:has_text] = true
39
+ record_service_vuln_id(attrs)
40
+ record_host_vuln_id(attrs)
41
+ end
42
+ end
43
+
44
+ # This breaks xml-encoded characters, so need to append
45
+ def characters(text)
46
+ return unless @state[:has_text]
47
+ @text ||= ""
48
+ @text << text
49
+ end
50
+
51
+ # When we exit a tag, this is triggered.
52
+ def end_element(name=nil)
53
+ block = @block
54
+ case name
55
+ when "device" # Wrap it up
56
+ collect_device_data
57
+ host_object = report_host &block
58
+ report_services(host_object)
59
+ report_host_fingerprint(host_object)
60
+ report_vulns(host_object)
61
+ # Reset the state once we close a host
62
+ @state.delete_if {|k| k != :current_tag}
63
+ @report_data = {:wspace => @args[:wspace]}
64
+ when "description"
65
+ @state[:has_text] = false
66
+ collect_service_fingerprint_description
67
+ collect_host_fingerprint_data(name)
68
+ @text = nil
69
+ when "vendor", "family", "product", "version", "architecture"
70
+ @state[:has_text] = false
71
+ collect_host_fingerprint_data(name)
72
+ @text = nil
73
+ when "service"
74
+ collect_service_data
75
+ when "id"
76
+ @state[:has_text] = false
77
+ collect_service_vuln_id
78
+ collect_host_vuln_id
79
+ @text = nil
80
+ when "vulnerability"
81
+ collect_service_vuln
82
+ collect_host_vuln
83
+ @state[:references] = nil
84
+ end
85
+ @state[:current_tag].delete name
86
+ end
87
+
88
+ def report_vulns(host_object)
89
+ vuln_count = 0
90
+ block = @block
91
+ return unless host_object.kind_of? Msf::DBManager::Host
92
+ return unless @report_data[:vulns]
93
+ @report_data[:vulns].each do |vuln|
94
+ if vuln[:refs]
95
+ vuln[:refs] << vuln[:name]
96
+ else
97
+ vuln[:refs] = [vuln[:name]]
98
+ end
99
+ vuln[:refs].uniq!
100
+ data = {
101
+ :workspace => host_object.workspace,
102
+ :host => host_object,
103
+ :name => vuln[:name],
104
+ :info => vuln[:info],
105
+ :refs => vuln[:refs]
106
+ }
107
+ if vuln[:port] && vuln[:proto]
108
+ data[:port] = vuln[:port]
109
+ data[:proto] = vuln[:proto]
110
+ end
111
+ db.report_vuln(data)
112
+ end
113
+
114
+ end
115
+
116
+ def collect_host_vuln_id
117
+ return unless in_tag("device")
118
+ return unless in_tag("vulnerability")
119
+ return if in_tag("service")
120
+ return unless @state[:host_vuln_id]
121
+ @state[:references] ||= []
122
+ ref = normalize_ref( @state[:host_vuln_id]["type"], @text )
123
+ @state[:references] << ref if ref
124
+ @state[:host_vuln_id] = nil
125
+ @text = nil
126
+ end
127
+
128
+ def collect_service_vuln_id
129
+ return unless in_tag("device")
130
+ return unless in_tag("vulnerability")
131
+ return unless in_tag("service")
132
+ return unless @state[:service_vuln_id]
133
+ @state[:references] ||= []
134
+ ref = normalize_ref( @state[:service_vuln_id]["type"], @text )
135
+ @state[:references] << ref if ref
136
+ @state[:service_vuln_id] = nil
137
+ @text = nil
138
+ end
139
+
140
+ def collect_service_vuln
141
+ return unless in_tag("device")
142
+ return unless in_tag("vulnerability")
143
+ return unless in_tag("service")
144
+ @report_data[:vulns] ||= []
145
+ return unless actually_vulnerable(@state[:service_vuln])
146
+ return if @state[:service]["port"].to_i == 0
147
+ vid = @state[:service_vuln]["id"].to_s.downcase
148
+ vuln = {
149
+ :name => "NEXPOSE-#{vid}",
150
+ :info => vid,
151
+ :refs => @state[:references],
152
+ :port => @state[:service]["port"].to_i,
153
+ :proto => @state[:service]["protocol"]
154
+ }
155
+ @report_data[:vulns] << vuln
156
+ end
157
+
158
+ def collect_host_vuln
159
+ return unless in_tag("vulnerability")
160
+ return unless in_tag("device")
161
+ return if in_tag("service")
162
+ @report_data[:vulns] ||= []
163
+ return unless actually_vulnerable(@state[:host_vuln])
164
+ vid = @state[:host_vuln]["id"].to_s.downcase
165
+ vuln = {
166
+ :name => "NEXPOSE-#{vid}",
167
+ :info => vid,
168
+ :refs => @state[:references]
169
+ }
170
+ @report_data[:vulns] << vuln
171
+ end
172
+
173
+ def record_host_vuln_id(attrs)
174
+ return unless in_tag("device")
175
+ return if in_tag("service")
176
+ @state[:host_vuln_id] = attr_hash(attrs)
177
+ end
178
+
179
+ def record_host_vuln(attrs)
180
+ return unless in_tag("device")
181
+ return if in_tag("service")
182
+ @state[:host_vuln] = attr_hash(attrs)
183
+ end
184
+
185
+ def record_service_vuln_id(attrs)
186
+ return unless in_tag("device")
187
+ return unless in_tag("service")
188
+ @state[:service_vuln_id] = attr_hash(attrs)
189
+ end
190
+
191
+ def record_service_vuln(attrs)
192
+ return unless in_tag("device")
193
+ return unless in_tag("service")
194
+ @state[:service_vuln] = attr_hash(attrs)
195
+ end
196
+
197
+ def actually_vulnerable(vuln)
198
+ vuln_result = vuln["resultCode"]
199
+ vuln_result =~ /^V[VE]$/
200
+ end
201
+
202
+ def record_device(attrs)
203
+ attrs.each do |k,v|
204
+ next unless k == "address"
205
+ @state[:address] = v
206
+ end
207
+ end
208
+
209
+ def record_host_fingerprint(attrs)
210
+ return unless in_tag("device")
211
+ return if in_tag("service")
212
+ @state[:host_fingerprint] = attr_hash(attrs)
213
+ end
214
+
215
+ def collect_device_data
216
+ return unless in_tag("device")
217
+ @report_data[:host] = @state[:address]
218
+ @report_data[:state] = Msf::HostState::Alive # always
219
+ end
220
+
221
+ def record_host_fingerprint_data(name, attrs)
222
+ return unless in_tag("device")
223
+ return if in_tag("service")
224
+ return unless in_tag("fingerprint")
225
+ @state[:host_fingerprint] ||= {}
226
+ @state[:host_fingerprint].merge! attr_hash(attrs)
227
+ end
228
+
229
+ def collect_host_fingerprint_data(name)
230
+ return unless in_tag("device")
231
+ return if in_tag("service")
232
+ return unless in_tag("fingerprint")
233
+ return unless @text
234
+ @report_data[:host_fingerprint] ||= {}
235
+ @report_data[:host_fingerprint].merge!(@state[:host_fingerprint])
236
+ @report_data[:host_fingerprint][name] = @text.to_s.strip
237
+ @text = nil
238
+ end
239
+
240
+ def report_host(&block)
241
+ if host_is_okay
242
+ db.emit(:address,@report_data[:host],&block) if block
243
+ host_object = db.report_host( @report_data.merge(
244
+ :workspace => @args[:wspace] ) )
245
+ if host_object
246
+ db.report_import_note(host_object.workspace, host_object)
247
+ end
248
+ host_object
249
+ end
250
+ end
251
+
252
+ def report_host_fingerprint(host_object)
253
+ return unless host_object.kind_of? ::Msf::DBManager::Host
254
+ return unless @report_data[:host_fingerprint].kind_of? Hash
255
+ @report_data[:host_fingerprint].reject! {|k,v| v.nil? || v.empty?}
256
+ return if @report_data[:host_fingerprint].empty?
257
+ note = {
258
+ :workspace => host_object.workspace,
259
+ :host => host_object,
260
+ :type => "host.os.nexpose_fingerprint"
261
+ }
262
+ data = {
263
+ :desc => @report_data[:host_fingerprint]["description"],
264
+ :vendor => @report_data[:host_fingerprint]["vendor"],
265
+ :family => @report_data[:host_fingerprint]["family"],
266
+ :product => @report_data[:host_fingerprint]["product"],
267
+ :version => @report_data[:host_fingerprint]["version"],
268
+ :arch => @report_data[:host_fingerprint]["architecture"]
269
+ }
270
+ db.report_note(note.merge(:data => data))
271
+ end
272
+
273
+ def record_service(attrs)
274
+ return unless in_tag("device")
275
+ @state[:service] = attr_hash(attrs)
276
+ end
277
+
278
+ def record_service_fingerprint(attrs)
279
+ return unless in_tag("device")
280
+ return unless in_tag("service")
281
+ @state[:service][:fingerprint] = attr_hash(attrs)
282
+ end
283
+
284
+ def collect_service_data
285
+ return unless in_tag("device")
286
+ port_hash = {}
287
+ @report_data[:ports] ||= []
288
+ @state[:service].each do |k,v|
289
+ case k
290
+ when "protocol"
291
+ port_hash[:protocol] = v
292
+ when "port"
293
+ port_hash[:port] = v
294
+ when "name"
295
+ port_hash[:name] = v.to_s.downcase.split("(")[0].strip
296
+ port_hash.delete(:name) if port_hash[:name] == "<unknown>"
297
+ end
298
+ end
299
+ if @state[:service_fingerprint]
300
+ port_hash[:info] = "#{@state[:service_fingerprint]}"
301
+ end
302
+ @report_data[:ports] << port_hash
303
+ end
304
+
305
+ def collect_service_fingerprint_description
306
+ return unless in_tag("device")
307
+ return unless in_tag("service")
308
+ return unless in_tag("fingerprint")
309
+ return unless @text
310
+ @state[:service_fingerprint] = @text.to_s.strip
311
+ @text = nil
312
+ end
313
+
314
+ def report_services(host_object)
315
+ return unless host_object.kind_of? ::Msf::DBManager::Host
316
+ return unless @report_data[:ports]
317
+ return if @report_data[:ports].empty?
318
+ reported = []
319
+ @report_data[:ports].each do |svc|
320
+ reported << db.report_service(svc.merge(:host => host_object))
321
+ end
322
+ reported
323
+ end
324
+
325
+ end
326
+
327
+ end
328
+ end
329
+
@@ -36,12 +36,6 @@ module Rex
36
36
  block = @block
37
37
  @state[:current_tag][name] = true
38
38
  case name
39
- when "host"
40
- @state[:in_host] = true
41
- when "os"
42
- if @state[:in_host]
43
- @state[:in_os] = true
44
- end
45
39
  when "status"
46
40
  record_host_status(attrs)
47
41
  when "address"
@@ -71,32 +65,65 @@ module Rex
71
65
  end
72
66
  end
73
67
 
68
+ # When we exit a tag, this is triggered.
69
+ def end_element(name=nil)
70
+ block = @block
71
+ case name
72
+ when "os"
73
+ collect_os_data
74
+ @state[:os] = {}
75
+ when "port"
76
+ collect_port_data
77
+ @state[:port] = {}
78
+ when "script"
79
+ if in_tag("host")
80
+ if in_tag("port")
81
+ @state[:portscripts] = {}
82
+ else
83
+ @state[:hostscripts] = {}
84
+ end
85
+ end
86
+ when "host" # Roll everything up now
87
+ collect_host_data
88
+ host_object = report_host &block
89
+ if host_object
90
+ db.report_import_note(@args[:wspace],host_object)
91
+ report_services(host_object,&block)
92
+ report_fingerprint(host_object)
93
+ report_uptime(host_object)
94
+ report_traceroute(host_object)
95
+ end
96
+ @state.delete_if {|k| k != :current_tag}
97
+ @report_data = {:wspace => @args[:wspace]}
98
+ end
99
+ @state[:current_tag].delete name
100
+ end
101
+
74
102
  # We can certainly get fancier with self.send() magic, but
75
103
  # leaving this pretty simple for now.
76
104
 
77
105
  def record_host_hop(attrs)
78
- return unless @state[:in_host]
79
- return unless @state[:in_trace]
106
+ return unless in_tag("host")
107
+ return unless in_tag("trace")
80
108
  hops = attr_hash(attrs)
81
109
  hops["name"] = hops.delete "host"
82
110
  @state[:trace][:hops] << hops
83
111
  end
84
112
 
85
113
  def record_host_trace(attrs)
86
- return unless @state[:in_host]
87
- @state[:in_trace] = true
114
+ return unless in_tag("host")
88
115
  @state[:trace] = attr_hash(attrs)
89
116
  @state[:trace][:hops] = []
90
117
  end
91
118
 
92
119
  def record_host_uptime(attrs)
93
- return unless @state[:in_host]
120
+ return unless in_tag("host")
94
121
  @state[:uptime] = attr_hash(attrs)
95
122
  end
96
123
 
97
124
  def record_host_osmatch(attrs)
98
- return unless @state[:in_host]
99
- return unless @state[:in_os]
125
+ return unless in_tag("host")
126
+ return unless in_tag("os")
100
127
  temp_hash = attr_hash(attrs)
101
128
  if temp_hash["accuracy"].to_i == 100
102
129
  @state[:os]["osmatch"] = temp_hash["name"]
@@ -104,8 +131,8 @@ module Rex
104
131
  end
105
132
 
106
133
  def record_host_osclass(attrs)
107
- return unless @state[:in_host]
108
- return unless @state[:in_os]
134
+ return unless in_tag("host")
135
+ return unless in_tag("os")
109
136
  @state[:os] ||= {}
110
137
  temp_hash = attr_hash(attrs)
111
138
  if better_os_match(@state[:os],temp_hash)
@@ -114,15 +141,15 @@ module Rex
114
141
  end
115
142
 
116
143
  def record_hostname(attrs)
117
- return unless @state[:in_host]
144
+ return unless in_tag("host")
118
145
  if attr_hash(attrs)["type"] == "PTR"
119
146
  @state[:hostname] = attr_hash(attrs)["name"]
120
147
  end
121
148
  end
122
149
 
123
150
  def record_host_script(attrs)
124
- return unless @state[:in_host]
125
- return if @state[:in_port]
151
+ return unless in_tag("host")
152
+ return if in_tag("port")
126
153
  temp_hash = attr_hash(attrs)
127
154
  @state[:hostscripts] ||= {}
128
155
  @state[:hostscripts].merge! temp_hash
@@ -131,8 +158,8 @@ module Rex
131
158
  end
132
159
 
133
160
  def record_port_script(attrs)
134
- return unless @state[:in_host]
135
- return unless @state[:in_port]
161
+ return unless in_tag("host")
162
+ return unless in_tag("port")
136
163
  temp_hash = attr_hash(attrs)
137
164
  @state[:portscripts] ||= {}
138
165
  @state[:portscripts].merge! temp_hash
@@ -142,8 +169,8 @@ module Rex
142
169
  end
143
170
 
144
171
  def record_port_service(attrs)
145
- return unless @state[:in_host]
146
- return unless @state[:in_port]
172
+ return unless in_tag("host")
173
+ return unless in_tag("port")
147
174
  svc = attr_hash(attrs)
148
175
  if svc["name"] && @args[:fix_services]
149
176
  svc["name"] = db.nmap_msf_service_map(svc["name"])
@@ -152,22 +179,21 @@ module Rex
152
179
  end
153
180
 
154
181
  def record_port_state(attrs)
155
- return unless @state[:in_host]
156
- return unless @state[:in_port]
182
+ return unless in_tag("host")
183
+ return unless in_tag("port")
157
184
  temp_hash = attr_hash(attrs)
158
185
  @state[:port] = @state[:port].merge(temp_hash)
159
186
  end
160
187
 
161
188
  def record_port(attrs)
162
- return unless @state[:in_host]
163
- @state[:in_port] = true
189
+ return unless in_tag("host")
164
190
  @state[:port] ||= {}
165
191
  svc = attr_hash(attrs)
166
192
  @state[:port] = @state[:port].merge(svc)
167
193
  end
168
194
 
169
195
  def record_host_status(attrs)
170
- return unless @state[:in_host]
196
+ return unless in_tag("host")
171
197
  attrs.each do |k,v|
172
198
  next unless k == "state"
173
199
  @state[:host_alive] = (v == "up")
@@ -175,7 +201,7 @@ module Rex
175
201
  end
176
202
 
177
203
  def record_address(attrs)
178
- return unless @state[:in_host]
204
+ return unless in_tag("host")
179
205
  @state[:addresses] ||= {}
180
206
  address = nil
181
207
  type = nil
@@ -189,55 +215,8 @@ module Rex
189
215
  @state[:addresses][type] = address
190
216
  end
191
217
 
192
- # When we exit a tag, this is triggered.
193
- def end_element(name=nil)
194
- block = @block
195
- @state[:current_tag].delete name
196
- case name
197
- when "os"
198
- collect_os_data
199
- @state[:in_os] = false
200
- @state[:os] = {}
201
- when "port"
202
- collect_port_data
203
- @state[:in_port] = false
204
- @state[:port] = {}
205
- when "script"
206
- if @state[:in_host]
207
- if @state[:in_port]
208
- @state[:portscripts] = {}
209
- else
210
- @state[:hostscripts] = {}
211
- end
212
- end
213
- when "trace"
214
- @state[:in_trace] = false
215
- when "host" # Roll everything up now
216
- collect_host_data
217
- host_object = report_host &block
218
- if host_object
219
- db.report_import_note(@args[:wspace],host_object)
220
- report_services(host_object,&block)
221
- report_fingerprint(host_object)
222
- report_uptime(host_object)
223
- report_traceroute(host_object)
224
- end
225
- @state.delete_if {|k| k != :current_tag}
226
- end
227
- end
228
-
229
- def end_document
230
- block = @block
231
- unless @state[:current_tag].empty?
232
- missing_ends = @state[:current_tag].keys.map {|x| "'#{x}'"}.join(", ")
233
- msg = "Warning, the provided file is incomplete, and there may be missing\n"
234
- msg << "data. The following tags were not closed: #{missing_ends}."
235
- db.emit(:warning,msg,&block)
236
- end
237
- end
238
-
239
218
  def collect_os_data
240
- return unless @state[:in_host]
219
+ return unless in_tag("host")
241
220
  if @state[:os]
242
221
  @report_data[:os_fingerprint] = {
243
222
  :type => "host.os.nmap_fingerprint",
@@ -281,8 +260,8 @@ module Rex
281
260
  end
282
261
  end
283
262
 
284
- def collect_port_data(&block)
285
- return unless @state[:in_host]
263
+ def collect_port_data
264
+ return unless in_tag("host")
286
265
  if @args[:fix_services]
287
266
  if @state[:port]["state"] == "filtered"
288
267
  return
@@ -24,76 +24,132 @@ module Rex
24
24
  !!@nokogiri_loaded
25
25
  end
26
26
 
27
- module NokogiriDocMixin
28
-
29
- # Set up the getters and instance variables for the document
30
- eval("attr_reader :args, :db, :state, :block, :report_data")
31
-
32
- def initialize(args,db,&block)
33
- @args = args
34
- @db = db
35
- @state = {}
36
- @state[:current_tag] = {}
37
- @block = block if block
38
- @report_data = {:wspace => args[:wspace]}
39
- super()
40
- end
27
+ # Useful during development, shouldn't be used in normal operation.
28
+ def self.reload(fname)
29
+ $stdout.puts "Reloading #{fname}..."
30
+ load __FILE__
31
+ load File.join(File.expand_path(File.dirname(__FILE__)),fname)
32
+ end
41
33
 
42
- # Turn XML attribute pairs in to more workable hashes (there
43
- # are better Enumerable tricks in Ruby 1.9, but ignoring for now)
44
- def attr_hash(attrs)
45
- h = {}
46
- attrs.each {|k,v| h[k] = v}
47
- h
48
- end
34
+ end
35
+ end
49
36
 
50
- def valid_ip(addr)
51
- valid = false
52
- valid = ::Rex::Socket::RangeWalker.new(addr).valid? rescue false
53
- !!valid
54
- end
37
+ module Rex
38
+ module Parser
39
+
40
+ load_nokogiri && module NokogiriDocMixin
41
+
42
+ # Set up the getters and instance variables for the document
43
+ eval("attr_reader :args, :db, :state, :block, :report_data")
44
+
45
+ def initialize(args,db,&block)
46
+ @args = args
47
+ @db = db
48
+ @state = {}
49
+ @state[:current_tag] = {}
50
+ @block = block if block
51
+ @report_data = {:wspace => args[:wspace]}
52
+ super()
53
+ end
54
+
55
+ # Turn XML attribute pairs in to more workable hashes (there
56
+ # are better Enumerable tricks in Ruby 1.9, but ignoring for now)
57
+ def attr_hash(attrs)
58
+ h = {}
59
+ attrs.each {|k,v| h[k] = v}
60
+ h
61
+ end
62
+
63
+ def valid_ip(addr)
64
+ valid = false
65
+ valid = ::Rex::Socket::RangeWalker.new(addr).valid? rescue false
66
+ !!valid
67
+ end
55
68
 
56
- # If there's an address, it's not on the blacklist,
57
- # it has ports, and the port list isn't
58
- # empty... it's okay.
59
- def host_is_okay
60
- return false unless @report_data[:host]
61
- return false unless valid_ip(@report_data[:host])
62
- return false unless @report_data[:state] == Msf::HostState::Alive
63
- if @args[:blacklist]
64
- return false if @args[:blacklist].include?(@report_data[:host])
69
+ def normalize_ref(ref_type, ref_value)
70
+ return if ref_type.nil? || ref_type.empty? || ref_value.nil? || ref_value.empty?
71
+ ref_value = ref_value.strip
72
+ ref_type = ref_type.strip.upcase
73
+ ret = case ref_type
74
+ when "CVE"
75
+ ref_value.gsub("CAN", "CVE")
76
+ when "MS"
77
+ "MSB-MS-#{ref_value}"
78
+ when "URL", "BID"
79
+ "#{ref_type}-#{ref_value}"
80
+ else # Handle others?
81
+ "#{ref_type}-#{ref_value}"
65
82
  end
66
- return false unless @report_data[:ports]
67
- return false if @report_data[:ports].empty?
68
- return true
69
- end
83
+ return ret
84
+ end
70
85
 
71
- # XXX: Define this
72
- def determine_port_state(v)
73
- return v
86
+ def normalize_references(orig_refs)
87
+ return [] unless orig_refs
88
+ refs = []
89
+ orig_refs.each do |ref_hash|
90
+ ref_hash_sym = Hash[ref_hash.map {|k, v| [k.to_sym, v] }]
91
+ ref_type = ref_hash_sym[:source].to_s.strip.upcase
92
+ ref_value = ref_hash_sym[:value].to_s.strip
93
+ refs << normalize_ref(ref_type, ref_value)
74
94
  end
95
+ return refs.compact.uniq
96
+ end
75
97
 
76
- # Nokogiri 1.4.4 (and presumably beyond) generates attrs as pairs,
77
- # like [["value1","foo"],["value2","bar"]] (but not hashes for some
78
- # reason). 1.4.3.1 (and presumably 1.4.3.x and prior) generates attrs
79
- # as a flat array of strings. We want array_pairs.
80
- def normalize_attrs(attrs)
81
- attr_pairs = []
82
- case attrs.first
83
- when Array, NilClass
84
- attr_pairs = attrs
85
- when String
86
- attrs.each_index {|i|
87
- next if i % 2 == 0
88
- attr_pairs << [attrs[i-1],attrs[i]]
89
- }
90
- else # Wow, yet another format! It's either from the distant past or distant future.
91
- raise ::Msf::DBImportError.new("Unknown format for XML attributes. Please check your Nokogiri version.")
92
- end
93
- return attr_pairs
98
+ def in_tag(tagname)
99
+ @state[:current_tag].keys.include? tagname
100
+ end
101
+
102
+ # If there's an address, it's not on the blacklist,
103
+ # it has ports, and the port list isn't
104
+ # empty... it's okay.
105
+ def host_is_okay
106
+ return false unless @report_data[:host]
107
+ return false unless valid_ip(@report_data[:host])
108
+ return false unless @report_data[:state] == Msf::HostState::Alive
109
+ if @args[:blacklist]
110
+ return false if @args[:blacklist].include?(@report_data[:host])
94
111
  end
112
+ return false unless @report_data[:ports]
113
+ return false if @report_data[:ports].empty?
114
+ return true
115
+ end
95
116
 
117
+ # XXX: Define this
118
+ def determine_port_state(v)
119
+ return v
120
+ end
96
121
 
122
+ # Nokogiri 1.4.4 (and presumably beyond) generates attrs as pairs,
123
+ # like [["value1","foo"],["value2","bar"]] (but not hashes for some
124
+ # reason). 1.4.3.1 (and presumably 1.4.3.x and prior) generates attrs
125
+ # as a flat array of strings. We want array_pairs.
126
+ def normalize_attrs(attrs)
127
+ attr_pairs = []
128
+ case attrs.first
129
+ when Array, NilClass
130
+ attr_pairs = attrs
131
+ when String
132
+ attrs.each_index {|i|
133
+ next if i % 2 == 0
134
+ attr_pairs << [attrs[i-1],attrs[i]]
135
+ }
136
+ else # Wow, yet another format! It's either from the distant past or distant future.
137
+ raise ::Msf::DBImportError.new("Unknown format for XML attributes. Please check your Nokogiri version.")
138
+ end
139
+ return attr_pairs
140
+ end
141
+
142
+ def end_document
143
+ block = @block
144
+ unless @state[:current_tag].empty?
145
+ missing_ends = @state[:current_tag].keys.map {|x| "'#{x}'"}.join(", ")
146
+ msg = "Warning, the provided file is incomplete, and there may be missing\n"
147
+ msg << "data. The following tags were not closed: #{missing_ends}."
148
+ db.emit(:warning,msg,&block)
149
+ end
97
150
  end
151
+
98
152
  end
153
+
154
+ end
99
155
  end
@@ -1,3 +1,5 @@
1
+ require 'metasm'
2
+
1
3
  module Rex
2
4
  module PeScan
3
5
  module Scanner
@@ -27,8 +29,15 @@ module Scanner
27
29
  msg = hit[1].is_a?(Array) ? hit[1].join(" ") : hit[1]
28
30
  $stdout.puts pe.ptr_s(vma) + " " + msg
29
31
  if(param['disasm'])
30
- ::Rex::Assembly::Nasm.disassemble([msg].pack("H*")).split("\n").each do |line|
31
- $stdout.puts "\t#{line.strip}"
32
+ insns = []
33
+ d2 = Metasm::Shellcode.decode(msg, Metasm::Ia32.new).disassembler
34
+ addr = 0
35
+ while ((di = d2.disassemble_instruction(addr)))
36
+ insns << di.instruction
37
+ disasm = "0x%08x\t" % (vma + addr)
38
+ disasm << di.instruction.to_s
39
+ $stdout.puts disasm
40
+ addr = di.next_addr
32
41
  end
33
42
  end
34
43
  end
@@ -3,44 +3,50 @@ module PeScan
3
3
  module Search
4
4
 
5
5
  require "rex/assembly/nasm"
6
-
6
+
7
7
  class DumpRVA
8
8
  attr_accessor :pe
9
-
9
+
10
10
  def initialize(pe)
11
11
  self.pe = pe
12
12
  end
13
-
13
+
14
14
  def config(param)
15
15
  @address = pe.vma_to_rva(param['args'])
16
16
  end
17
-
17
+
18
18
  def scan(param)
19
19
  config(param)
20
-
20
+
21
21
  $stdout.puts "[#{param['file']}]"
22
-
22
+
23
23
  # Adjust based on -A and -B flags
24
24
  pre = param['before'] || 0
25
25
  suf = param['after'] || 16
26
-
26
+
27
27
  @address -= pre
28
28
  @address = 0 if (@address < 0 || ! @address)
29
-
29
+
30
30
  begin
31
31
  buf = pe.read_rva(@address, suf)
32
32
  rescue ::Rex::PeParsey::WtfError
33
33
  return
34
34
  end
35
-
35
+
36
36
  $stdout.puts pe.ptr_s(pe.rva_to_vma(@address)) + " " + buf.unpack("H*")[0]
37
37
  if(param['disasm'])
38
- ::Rex::Assembly::Nasm.disassemble(buf).split("\n").each do |line|
39
- $stdout.puts "\t#{line.strip}"
38
+ insns = []
39
+ d2 = Metasm::Shellcode.decode(buf, Metasm::Ia32.new).disassembler
40
+ addr = 0
41
+ while ((di = d2.disassemble_instruction(addr)))
42
+ insns << di.instruction
43
+ disasm = "0x%08x\t" % (pe.rva_to_vma(@address) + addr)
44
+ disasm << di.instruction.to_s
45
+ $stdout.puts disasm
46
+ addr = di.next_addr
40
47
  end
41
48
  end
42
-
43
- end
49
+ end
44
50
  end
45
51
 
46
52
  class DumpOffset < DumpRVA
@@ -50,7 +56,7 @@ module Search
50
56
  rescue Rex::PeParsey::BoundsError
51
57
  end
52
58
  end
53
- end
59
+ end
54
60
  end
55
61
  end
56
62
  end
@@ -149,9 +149,12 @@ class Jar < Archive
149
149
  # directly supported by keytool for some unfathomable reason
150
150
  # http://www.agentbob.info/agentbob/79-AB.html
151
151
  #
152
- def sign(key, cert)
152
+ def sign(key, cert, ca_certs=nil)
153
153
  m = self.entries.find { |e| e.name == "META-INF/MANIFEST.MF" }
154
154
  raise RuntimeError.new("Jar has no manifest") unless m
155
+
156
+ ca_certs ||= [ cert ]
157
+
155
158
  new_manifest = ''
156
159
  sigdata = "Signature-Version: 1.0\r\n"
157
160
  sigdata << "Created-By: 1.6.0_18 (Sun Microsystems Inc.)\r\n"
@@ -192,25 +195,25 @@ class Jar < Archive
192
195
  flags = 0
193
196
  flags |= OpenSSL::PKCS7::BINARY
194
197
  flags |= OpenSSL::PKCS7::DETACHED
195
- # SMIME and ATTRs are technically valid in the signature but they both
196
- # screw up the java verifier, so don't include them.
198
+ # SMIME and ATTRs are technically valid in the signature but they
199
+ # both screw up the java verifier, so don't include them.
197
200
  flags |= OpenSSL::PKCS7::NOSMIMECAP
198
201
  flags |= OpenSSL::PKCS7::NOATTR
199
202
 
200
- signature = OpenSSL::PKCS7.sign(cert, key, sigdata, [cert], flags)
203
+ signature = OpenSSL::PKCS7.sign(cert, key, sigdata, ca_certs, flags)
201
204
  sigalg = case key
202
205
  when OpenSSL::PKey::RSA; "RSA"
203
206
  when OpenSSL::PKey::DSA; "DSA"
204
207
  # Don't really know what to do if it's not DSA or RSA. Can
205
208
  # OpenSSL::PKCS7 actually sign stuff with it in that case?
206
- # Regardless, the java spec says signatures can only be RSA, DSA,
207
- # or PGP, so just assume it's PGP and hope for the best
209
+ # Regardless, the java spec says signatures can only be RSA,
210
+ # DSA, or PGP, so just assume it's PGP and hope for the best
208
211
  else; "PGP"
209
212
  end
210
213
 
211
214
  # SIGNFILE is the default name in documentation. MYKEY is probably
212
- # more common, though because that's what keytool defaults to. We can
213
- # probably randomize this with no ill effects.
215
+ # more common, though because that's what keytool defaults to. We
216
+ # can probably randomize this with no ill effects.
214
217
  add_file("META-INF/SIGNFILE.SF", sigdata)
215
218
  add_file("META-INF/SIGNFILE.#{sigalg}", signature.to_der)
216
219
 
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: librex
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.33
5
+ version: 0.0.34
6
6
  platform: ruby
7
7
  authors:
8
8
  - Metasploit Development Team
@@ -11,11 +11,11 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2011-05-25 00:00:00 -05:00
14
+ date: 2011-05-27 00:00:00 -05:00
15
15
  default_executable:
16
16
  dependencies: []
17
17
 
18
- description: Rex provides a variety of classes useful for security testing and exploit development. Based on SVN Revision 12724
18
+ description: Rex provides a variety of classes useful for security testing and exploit development. Based on SVN Revision 12756
19
19
  email:
20
20
  - hdm@metasploit.com
21
21
  - jacob.hammack@hammackj.com
@@ -159,6 +159,8 @@ files:
159
159
  - lib/rex/parser/ip360_xml.rb
160
160
  - lib/rex/parser/nessus_xml.rb
161
161
  - lib/rex/parser/netsparker_xml.rb
162
+ - lib/rex/parser/nexpose_raw_nokogiri.rb
163
+ - lib/rex/parser/nexpose_simple_nokogiri.rb
162
164
  - lib/rex/parser/nexpose_xml.rb
163
165
  - lib/rex/parser/nmap_nokogiri.rb
164
166
  - lib/rex/parser/nmap_xml.rb