librex 0.0.33 → 0.0.34

Sign up to get free protection for your applications and to get access to all the features.
@@ -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