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.
- 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
|