librex 0.0.33 → 0.0.34
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +1 -1
- data/lib/rex/parser/nexpose_raw_nokogiri.rb +363 -0
- data/lib/rex/parser/nexpose_simple_nokogiri.rb +329 -0
- data/lib/rex/parser/nmap_nokogiri.rb +57 -78
- data/lib/rex/parser/nokogiri_doc_mixin.rb +116 -60
- data/lib/rex/pescan/scanner.rb +11 -2
- data/lib/rex/pescan/search.rb +20 -14
- data/lib/rex/zip/jar.rb +11 -8
- metadata +5 -3
data/README.markdown
CHANGED
@@ -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:
|
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
|
79
|
-
return unless
|
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
|
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
|
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
|
99
|
-
return unless
|
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
|
108
|
-
return unless
|
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
|
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
|
125
|
-
return if
|
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
|
135
|
-
return unless
|
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
|
146
|
-
return unless
|
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
|
156
|
-
return unless
|
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
|
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
|
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
|
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
|
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
|
285
|
-
return unless
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
43
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
67
|
-
|
68
|
-
return true
|
69
|
-
end
|
83
|
+
return ret
|
84
|
+
end
|
70
85
|
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
data/lib/rex/pescan/scanner.rb
CHANGED
@@ -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
|
-
|
31
|
-
|
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
|
data/lib/rex/pescan/search.rb
CHANGED
@@ -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
|
-
|
39
|
-
|
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
|
data/lib/rex/zip/jar.rb
CHANGED
@@ -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
|
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,
|
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,
|
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
|
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.
|
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-
|
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
|
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
|