librex 0.0.34 → 0.0.35

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: 12756
6
+ SVN Revision: 12804
7
7
 
8
8
  # Credits
9
9
  The Metasploit development team <http://www.metasploit.com>
@@ -0,0 +1,340 @@
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 FoundstoneDocument < Nokogiri::XML::SAX::Document
8
+
9
+ include NokogiriDocMixin
10
+
11
+ def start_document
12
+ @report_type_ok = true # Optimistic
13
+ end
14
+
15
+ # Triggered every time a new element is encountered. We keep state
16
+ # ourselves with the @state variable, turning things on when we
17
+ # get here (and turning things off when we exit in end_element()).
18
+ def start_element(name=nil,attrs=[])
19
+ attrs = normalize_attrs(attrs)
20
+ block = @block
21
+ return unless @report_type_ok
22
+ @state[:current_tag][name] = true
23
+ case name
24
+ when "ReportInfo"
25
+ check_for_correct_report_type(attrs,&block)
26
+ when "Host"
27
+ record_host(attrs)
28
+ when "Service"
29
+ record_service(attrs)
30
+ when "Port", "Protocol", "Banner"
31
+ @state[:has_text] = true
32
+ when "Vuln" # under VulnsFound, ignore risk 0 things
33
+ record_vuln(attrs)
34
+ when "Risk" # for Vuln
35
+ @state[:has_text] = true
36
+ when "CVE" # Under Vuln
37
+ @state[:has_text] = true
38
+ end
39
+ end
40
+
41
+ # When we exit a tag, this is triggered.
42
+ def end_element(name=nil)
43
+ block = @block
44
+ return unless @report_type_ok
45
+ case name
46
+ when "Host" # Wrap it up
47
+ collect_host_data
48
+ host_object = report_host &block
49
+ if host_object
50
+ db.report_import_note(@args[:wspace],host_object)
51
+ report_fingerprint(host_object)
52
+ report_services(host_object)
53
+ report_vulns(host_object)
54
+ end
55
+ # Reset the state once we close a host
56
+ @state.delete_if {|k| k != :current_tag}
57
+ when "Port"
58
+ @state[:has_text] = false
59
+ collect_port
60
+ when "Protocol"
61
+ @state[:has_text] = false
62
+ collect_protocol
63
+ when "Banner"
64
+ collect_banner
65
+ @state[:has_text] = false
66
+ when "Service"
67
+ collect_service
68
+ when "Vuln"
69
+ collect_vuln
70
+ when "Risk"
71
+ @state[:has_text] = false
72
+ collect_risk
73
+ when "CVE"
74
+ @state[:has_text] = false
75
+ collect_cve
76
+ end
77
+ @state[:current_tag].delete name
78
+ end
79
+
80
+ # Nothing technically stopping us from parsing this as well,
81
+ # but saving this for later
82
+ def check_for_correct_report_type(attrs,&block)
83
+ report_type = attr_hash(attrs)["ReportType"]
84
+ if report_type == "Network Inventory"
85
+ @report_type_ok = true
86
+ else
87
+ if report_type == "Risk Data"
88
+ msg = "The Foundstone/Mcafee report type '#{report_type}' is not currently supported"
89
+ msg << ",\nso no data will be imported. Please use the 'Network Inventory' report instead."
90
+ else
91
+ msg = ".\nThe Foundstone/Macafee report type '#{report_type}' is unsupported."
92
+ end
93
+ db.emit(:warning,msg,&block) if block
94
+ @report_type_ok = false
95
+ end
96
+ end
97
+
98
+ def collect_risk
99
+ return unless in_tag("VulnsFound")
100
+ return unless in_tag("HostData")
101
+ return unless in_tag("Host")
102
+ risk = @text.to_s.to_i
103
+ @state[:vuln][:risk] = risk
104
+ @text = nil
105
+ end
106
+
107
+ def collect_cve
108
+ return unless in_tag("VulnsFound")
109
+ return unless in_tag("HostData")
110
+ return unless in_tag("Host")
111
+ cve = @text.to_s
112
+ @state[:vuln][:cves] ||= []
113
+ @state[:vuln][:cves] << cve unless cve == "CVE-MAP-NOMATCH"
114
+ @text = nil
115
+ end
116
+
117
+ # Determines if we should keep the vuln or not. Note that
118
+ # we cannot tie them to a service.
119
+ def collect_vuln
120
+ return unless in_tag("VulnsFound")
121
+ return unless in_tag("HostData")
122
+ return unless in_tag("Host")
123
+ return if @state[:vuln][:risk] == 0
124
+ @report_data[:vulns] ||= []
125
+ vuln_hash = {}
126
+ vuln_hash[:name] = @state[:vuln]["VulnName"]
127
+ refs = []
128
+ refs << "FID-#{@state[:vuln]["id"]}"
129
+ if @state[:vuln][:cves]
130
+ @state[:vuln][:cves].each {|cve| refs << cve}
131
+ end
132
+ vuln_hash[:refs] = refs
133
+ @report_data[:vulns] << vuln_hash
134
+ end
135
+
136
+ # These are per host.
137
+ def record_vuln(attrs)
138
+ return unless in_tag("VulnsFound")
139
+ return unless in_tag("HostData")
140
+ return unless in_tag("Host")
141
+ @state[:vulns] ||= []
142
+
143
+ @state[:vuln] = attr_hash(attrs) # id and VulnName
144
+ end
145
+
146
+ def record_service(attrs)
147
+ return unless in_tag("ServicesFound")
148
+ return unless in_tag("Host")
149
+ @state[:service] = attr_hash(attrs)
150
+ end
151
+
152
+ def collect_port
153
+ return unless in_tag("Service")
154
+ return unless in_tag("ServicesFound")
155
+ return unless in_tag("Host")
156
+ return if @text.nil? || @text.empty?
157
+ @state[:service][:port] = @text.strip
158
+ @text = nil
159
+ end
160
+
161
+ def collect_protocol
162
+ return unless in_tag("Service")
163
+ return unless in_tag("ServicesFound")
164
+ return unless in_tag("Host")
165
+ return if @text.nil? || @text.empty?
166
+ @state[:service][:proto] = @text.strip
167
+ @text = nil
168
+ end
169
+
170
+ def collect_banner
171
+ return unless in_tag("Service")
172
+ return unless in_tag("ServicesFound")
173
+ return unless in_tag("Host")
174
+ return if @text.nil? || @text.empty?
175
+ banner = normalize_foundstone_banner(@state[:service]["ServiceName"],@text)
176
+ unless banner.nil? || banner.empty?
177
+ @state[:service][:banner] = banner
178
+ end
179
+ @text = nil
180
+ end
181
+
182
+ def collect_service
183
+ return unless in_tag("ServicesFound")
184
+ return unless in_tag("Host")
185
+ return unless @state[:service][:port]
186
+ @report_data[:ports] ||= []
187
+ port_hash = {}
188
+ port_hash[:port] = @state[:service][:port]
189
+ port_hash[:proto] = @state[:service][:proto]
190
+ port_hash[:info] = @state[:service][:banner]
191
+ port_hash[:name] = db.nmap_msf_service_map(@state[:service]["ServiceName"])
192
+ @report_data[:ports] << port_hash
193
+ end
194
+
195
+ def record_host(attrs)
196
+ return unless in_tag("HostData")
197
+ @state[:host] = attr_hash(attrs)
198
+ end
199
+
200
+ def collect_host_data
201
+ @report_data[:host] = @state[:host]["IPAddress"]
202
+ if @state[:host]["NBName"] && !@state[:host]["NBName"].empty?
203
+ @report_data[:name] = @state[:host]["NBName"]
204
+ elsif @state[:host]["DNSName"] && !@state[:host]["DNSName"].empty?
205
+ @report_data[:name] = @state[:host]["DNSName"]
206
+ end
207
+ if @state[:host]["OSName"] && !@state[:host]["OSName"].empty?
208
+ @report_data[:os_fingerprint] = @state[:host]["OSName"]
209
+ end
210
+ @report_data[:state] = Msf::HostState::Alive
211
+ @report_data[:mac] = @state[:mac] if @state[:mac]
212
+ end
213
+
214
+ def report_host(&block)
215
+ return unless in_tag("HostData")
216
+ if host_is_okay
217
+ db.emit(:address,@report_data[:host],&block) if block
218
+ host_info = @report_data.merge(:workspace => @args[:wspace])
219
+ db_report(:host,host_info)
220
+ end
221
+ end
222
+
223
+ def report_fingerprint(host_object)
224
+ fp_note = {
225
+ :workspace => host_object.workspace,
226
+ :host => host_object,
227
+ :type => 'host.os.foundstone_fingerprint',
228
+ :data => {:os => @report_data[:os_fingerprint] }
229
+ }
230
+ db_report(:note, fp_note)
231
+ end
232
+
233
+ def report_services(host_object)
234
+ return unless in_tag("HostData")
235
+ return unless host_object.kind_of? Msf::DBManager::Host
236
+ return unless @report_data[:ports]
237
+ return if @report_data[:ports].empty?
238
+ @report_data[:ports].each do |svc|
239
+ db_report(:service, svc.merge(:host => host_object))
240
+ end
241
+ end
242
+
243
+ def report_vulns(host_object)
244
+ return unless in_tag("HostData")
245
+ return unless host_object.kind_of? Msf::DBManager::Host
246
+ return unless @report_data[:vulns]
247
+ return if @report_data[:vulns].empty?
248
+ @report_data[:vulns].each do |vuln|
249
+ db_report(:vuln, vuln.merge(:host => host_object))
250
+ end
251
+ end
252
+
253
+ # Foundstone's banners are pretty free-form
254
+ # and often not just banners. Clean them up
255
+ # for the :info field, delegate off for other
256
+ # protocol data we can use.
257
+ def normalize_foundstone_banner(service,banner)
258
+ return "" if(banner.nil? || banner.strip.empty?)
259
+ if first_line_only? service
260
+ return (first_line banner)
261
+ elsif needs_more_processing? service
262
+ return process_service(service,banner)
263
+ else
264
+ return (first_line banner)
265
+ end
266
+ end
267
+
268
+ # Services where we only care about the first
269
+ # line of the banner tag.
270
+ def first_line_only?(service)
271
+ svcs = %w{
272
+ vnc ftp ftps smtp oracle-tns nntp ssh ntp
273
+ }
274
+ 9.times {|i| svcs << "vnc-#{i}"}
275
+ svcs.include? service
276
+ end
277
+
278
+ # Services where we need to do more processing
279
+ # before handing the banner back.
280
+ def needs_more_processing?(service)
281
+ svcs = %w{
282
+ microsoft-ds loc-srv http https sunrpc netbios-ns
283
+ }
284
+ svcs.include? service
285
+ end
286
+
287
+ def first_line(str)
288
+ str.split("\n").first.to_s.strip
289
+ end
290
+
291
+ # XXX: Actually implement more of these
292
+ def process_service(service,banner)
293
+ meth = "process_service_#{service.gsub("-","_")}"
294
+ if self.respond_to? meth
295
+ self.send meth, banner
296
+ else
297
+ return (first_line banner)
298
+ end
299
+ end
300
+
301
+ # XXX: Register a proper netbios note as the regular
302
+ # scanner does.
303
+ def process_service_netbios_ns(banner)
304
+ mac_regex = /[0-9A-Fa-f:]{17}/
305
+ @state[:mac] = banner[mac_regex]
306
+ first_line banner
307
+ end
308
+
309
+ # XXX: Make this behave more like the smb scanner
310
+ def process_service_microsoft_ds(banner)
311
+ lm_regex = /Native LAN Manager/
312
+ lm_banner = nil
313
+ banner.each_line { |line|
314
+ if line[lm_regex]
315
+ lm_banner = line
316
+ break
317
+ end
318
+ }
319
+ lm_banner || first_line(banner)
320
+ end
321
+
322
+ def process_service_http(banner)
323
+ server = nil
324
+ banner.each_line do |line|
325
+ if line =~ /^Server:\s+(.*)/
326
+ server = $1
327
+ break
328
+ end
329
+ end
330
+ server || first_line(banner)
331
+ end
332
+
333
+ alias :process_service_https :process_service_http
334
+ alias :process_service_rtsp :process_service_http
335
+
336
+ end
337
+
338
+ end
339
+ end
340
+
@@ -0,0 +1,255 @@
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 MbsaDocument < Nokogiri::XML::SAX::Document
8
+
9
+ include NokogiriDocMixin
10
+
11
+ # Triggered every time a new element is encountered. We keep state
12
+ # ourselves with the @state variable, turning things on when we
13
+ # get here (and turning things off when we exit in end_element()).
14
+ def start_element(name=nil,attrs=[])
15
+ attrs = normalize_attrs(attrs)
16
+ block = @block
17
+ @state[:current_tag][name] = true
18
+ case name
19
+ when "SecScan"
20
+ record_host(attrs)
21
+ when "IP" # TODO: Check to see if IPList/IP is useful to import
22
+ when "Check" # A list of MBSA checks. They have an ID and a Name.
23
+ record_check(attrs)
24
+ when "Advice" # Check advice. Free form text about the check
25
+ @state[:has_text] = true
26
+ when "Detail" # Check/Detail is where missing fixes are.
27
+ record_detail(attrs)
28
+ when "UpdateData" # Info about installed/missing hotfixes
29
+ record_updatedata(attrs)
30
+ when "Title" # MSB Title
31
+ @state[:has_text] = true
32
+ when "InformationURL" # Only use this if we don't have a Bulletin ID
33
+ @state[:has_text] = true
34
+ end
35
+ end
36
+
37
+ # This breaks xml-encoded characters, so need to append
38
+ def characters(text)
39
+ return unless @state[:has_text]
40
+ @text ||= ""
41
+ @text << text
42
+ end
43
+
44
+ # When we exit a tag, this is triggered.
45
+ def end_element(name=nil)
46
+ block = @block
47
+ case name
48
+ when "SecScan" # Wrap it up
49
+ collect_host_data
50
+ host_object = report_host &block
51
+ if host_object
52
+ db.report_import_note(@args[:wspace],host_object)
53
+ report_fingerprint(host_object)
54
+ report_vulns(host_object,&block)
55
+ end
56
+ # Reset the state once we close a host
57
+ @state.delete_if {|k| k != :current_tag}
58
+ when "Check"
59
+ collect_check_data
60
+ when "Advice"
61
+ @state[:has_text] = false
62
+ collect_advice_data
63
+ when "Detail"
64
+ collect_detail_data
65
+ when "UpdateData"
66
+ collect_updatedata
67
+ when "Title"
68
+ @state[:has_text] = false
69
+ collect_title
70
+ when "InformationURL"
71
+ collect_url
72
+ @state[:has_text] = false
73
+ end
74
+ @state[:current_tag].delete name
75
+ end
76
+
77
+ def report_fingerprint(host_object)
78
+ return unless host_object.kind_of? Msf::DBManager::Host
79
+ return unless @report_data[:os_fingerprint]
80
+ fp_note = @report_data[:os_fingerprint].merge(
81
+ {
82
+ :workspace => host_object.workspace,
83
+ :host => host_object
84
+ })
85
+ db_report(:note, fp_note)
86
+ end
87
+
88
+ def collect_url
89
+ return unless in_tag("References")
90
+ return unless in_tag("UpdateData")
91
+ return unless in_tag("Detail")
92
+ return unless in_tag("Check")
93
+ @state[:update][:url] = @text.to_s.strip
94
+ @text = nil
95
+ end
96
+
97
+ def report_vulns(host_object, &block)
98
+ return unless host_object.kind_of? Msf::DBManager::Host
99
+ return unless @report_data[:vulns]
100
+ return if @report_data[:vulns].empty?
101
+ @report_data[:vulns].each do |vuln|
102
+ next unless vuln[:refs]
103
+ if vuln[:refs].empty?
104
+ next
105
+ end
106
+ if block
107
+ db.emit(:vuln, ["Missing #{vuln[:name]}",1], &block) if block
108
+ end
109
+ db_report(:vuln, vuln.merge(:host => host_object))
110
+ end
111
+ end
112
+
113
+ def collect_title
114
+ return unless in_tag("SecScan")
115
+ return unless in_tag("Check")
116
+ collect_bulletin_title
117
+ @text = nil
118
+ end
119
+
120
+ def collect_bulletin_title
121
+ return unless @state[:check_state]["ID"] == 500.to_s
122
+ return unless in_tag("UpdateData")
123
+ return unless @state[:update]
124
+ return if @text.to_s.strip.empty?
125
+ @state[:update]["Title"] = @text.to_s.strip
126
+ end
127
+
128
+ def collect_updatedata
129
+ return unless in_tag("SecScan")
130
+ return unless in_tag("Check")
131
+ return unless in_tag("Detail")
132
+ collect_missing_update
133
+ @state[:updates] = {}
134
+ end
135
+
136
+ def collect_missing_update
137
+ return unless @state[:check_state]["ID"] == 500.to_s
138
+ return if @state[:update]["IsInstalled"] == "true"
139
+ @report_data[:missing_updates] ||= []
140
+ this_update = {}
141
+ this_update[:name] = @state[:update]["Title"].to_s.strip
142
+ this_update[:refs] = []
143
+ if @state[:update]["BulletinID"].empty?
144
+ this_update[:refs] << "URL-#{@state[:update][:url]}"
145
+ else
146
+ this_update[:refs] << "MSB-#{@state[:update]["BulletinID"]}"
147
+ end
148
+ @report_data[:missing_updates] << this_update
149
+ end
150
+
151
+ # So far, just care about Host OS
152
+ # There is assuredly more interesting things going on in here.
153
+ def collect_advice_data
154
+ return unless in_tag("SecScan")
155
+ return unless in_tag("Check")
156
+ collect_os_name
157
+ @text = nil
158
+ end
159
+
160
+ def collect_os_name
161
+ return unless @state[:check_state]["ID"] == 10101.to_s
162
+ return unless @text
163
+ return if @text.strip.empty?
164
+ os_match = @text.match(/Computer is running (.*)/)
165
+ return unless os_match
166
+ os_info = os_match[1]
167
+ os_vendor = os_info[/Microsoft/]
168
+ os_family = os_info[/Windows/]
169
+ os_version = os_info[/(XP|2000 Advanced Server|2000|2003|2008|SBS|Vista|7 .* Edition|7)/]
170
+ if os_info
171
+ @report_data[:os_fingerprint] = {}
172
+ @report_data[:os_fingerprint][:type] = "host.os.mbsa_fingerprint"
173
+ @report_data[:os_fingerprint][:data] = {
174
+ :os_vendor => os_vendor,
175
+ :os_family => os_family,
176
+ :os_version => os_version,
177
+ :os_accuracy => 100,
178
+ :os_match => os_info.gsub(/\x2e$/,"")
179
+ }
180
+ end
181
+ end
182
+
183
+ def collect_detail_data
184
+ return unless in_tag("SecScan")
185
+ return unless in_tag("Check")
186
+ if @report_data[:missing_updates]
187
+ @report_data[:vulns] = @report_data[:missing_updates]
188
+ end
189
+ end
190
+
191
+ def collect_check_data
192
+ return unless in_tag("SecScan")
193
+ @state[:check_state] = {}
194
+ end
195
+
196
+ def collect_host_data
197
+ return unless @state[:address]
198
+ return if @state[:address].strip.empty?
199
+ @report_data[:host] = @state[:address].strip
200
+ if @state[:hostname] && !@state[:hostname].empty?
201
+ @report_data[:name] = @state[:hostname]
202
+ end
203
+ @report_data[:state] = Msf::HostState::Alive
204
+ end
205
+
206
+ def report_host(&block)
207
+ if host_is_okay
208
+ db.emit(:address,@report_data[:host],&block) if block
209
+ host_info = @report_data.merge(:workspace => @args[:wspace])
210
+ db_report(:host, host_info)
211
+ end
212
+ end
213
+
214
+ def record_updatedata(attrs)
215
+ return unless in_tag("SecScan")
216
+ return unless in_tag("Check")
217
+ return unless in_tag("Detail")
218
+ update_attrs = attr_hash(attrs)
219
+ @state[:update] = attr_hash(attrs)
220
+ end
221
+
222
+ def record_host(attrs)
223
+ host_attrs = attr_hash(attrs)
224
+ @state[:address] = host_attrs["IP"]
225
+ @state[:hostname] = host_attrs["Machine"]
226
+ end
227
+
228
+ def record_check(attrs)
229
+ return unless in_tag("SecScan")
230
+ @state[:check_state] = attr_hash(attrs)
231
+ end
232
+
233
+ def record_detail(attrs)
234
+ return unless in_tag("SecScan")
235
+ return unless in_tag("Check")
236
+ @state[:detail_state] = attr_hash(attrs)
237
+ end
238
+
239
+ # We need to override the usual host_is_okay because MBSA apparently
240
+ # doesn't report on open ports at all.
241
+ def host_is_okay
242
+ return false unless @report_data[:host]
243
+ return false unless valid_ip(@report_data[:host])
244
+ return false unless @report_data[:state] == Msf::HostState::Alive
245
+ if @args[:blacklist]
246
+ return false if @args[:blacklist].include?(@report_data[:host])
247
+ end
248
+ return true
249
+ end
250
+
251
+ end
252
+
253
+ end
254
+ end
255
+
@@ -43,13 +43,6 @@ module Rex
43
43
  end
44
44
  end
45
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
46
  # When we exit a tag, this is triggered.
54
47
  def end_element(name=nil)
55
48
  block = @block
@@ -109,7 +102,8 @@ module Rex
109
102
  return unless @report_data[:vuln][:matches].kind_of? Array
110
103
  refs = normalize_references(@report_data[:vuln][:refs])
111
104
  refs << "NEXPOSE-#{report_data[:vuln]["id"]}"
112
- db.emit(:vuln, [refs.last,@report_data[:vuln][:matches].size], &block)
105
+ vuln_instances = @report_data[:vuln][:matches].size
106
+ db.emit(:vuln, [refs.last,vuln_instances], &block) if block
113
107
  data = {
114
108
  :workspace => @args[:wspace],
115
109
  :name => refs.last,
@@ -122,7 +116,7 @@ module Rex
122
116
  host_data[:host] = match[:host]
123
117
  host_data[:port] = match[:port] if match[:port]
124
118
  host_data[:proto] = match[:protocol] if match[:protocol]
125
- db.report_vuln(host_data)
119
+ db_report(:vuln, host_data)
126
120
  if match[:key]
127
121
  hosts_keys[host_data[:host]] ||= []
128
122
  hosts_keys[host_data[:host]] << match[:key]
@@ -147,7 +141,7 @@ module Rex
147
141
  next if key_note[:data][data[:name]].include? key_value
148
142
  key_note[:data][data[:name]] << key_value
149
143
  end
150
- db.report_note(key_note)
144
+ db_report(:note, key_note)
151
145
  end
152
146
  end
153
147
 
@@ -240,7 +234,7 @@ module Rex
240
234
  note[:data][:product] = @report_data[:os]["os_product"] if @report_data[:os]["os_prduct"]
241
235
  note[:data][:version] = @report_data[:os]["os_version"] if @report_data[:os]["os_version"]
242
236
  note[:data][:arch] = @report_data[:os]["os_arch"] if @report_data[:os]["os_arch"]
243
- db.report_note(note)
237
+ db_report(:note, note)
244
238
  end
245
239
 
246
240
  def report_services(host_object)
@@ -249,7 +243,7 @@ module Rex
249
243
  return if @report_data[:ports].empty?
250
244
  reported = []
251
245
  @report_data[:ports].each do |svc|
252
- reported << db.report_service(svc.merge(:host => host_object))
246
+ reported << db_report(:service, svc.merge(:host => host_object))
253
247
  end
254
248
  reported
255
249
  end
@@ -277,7 +271,12 @@ module Rex
277
271
  end
278
272
  end
279
273
  if @state[:service]
280
- port_hash[:name] = @state[:service]["name"] if @state[:service]["name"] != "<unknown>"
274
+ if state[:service]["name"] == "<unknown>"
275
+ sname = nil
276
+ else
277
+ sname = db.nmap_msf_service_map(@state[:service]["name"])
278
+ end
279
+ port_hash[:name] = sname
281
280
  end
282
281
  if @state[:service_fingerprint]
283
282
  info = []
@@ -347,7 +346,7 @@ module Rex
347
346
  def report_host(&block)
348
347
  if host_is_okay
349
348
  db.emit(:address,@report_data[:host],&block) if block
350
- host_object = db.report_host( @report_data.merge(
349
+ host_object = db_report(:host, @report_data.merge(
351
350
  :workspace => @args[:wspace] ) )
352
351
  if host_object
353
352
  db.report_import_note(host_object.workspace, host_object)
@@ -41,13 +41,6 @@ module Rex
41
41
  end
42
42
  end
43
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
44
  # When we exit a tag, this is triggered.
52
45
  def end_element(name=nil)
53
46
  block = @block
@@ -108,7 +101,7 @@ module Rex
108
101
  data[:port] = vuln[:port]
109
102
  data[:proto] = vuln[:proto]
110
103
  end
111
- db.report_vuln(data)
104
+ db_report(:vuln,data)
112
105
  end
113
106
 
114
107
  end
@@ -240,7 +233,7 @@ module Rex
240
233
  def report_host(&block)
241
234
  if host_is_okay
242
235
  db.emit(:address,@report_data[:host],&block) if block
243
- host_object = db.report_host( @report_data.merge(
236
+ host_object = db_report(:host, @report_data.merge(
244
237
  :workspace => @args[:wspace] ) )
245
238
  if host_object
246
239
  db.report_import_note(host_object.workspace, host_object)
@@ -267,7 +260,7 @@ module Rex
267
260
  :version => @report_data[:host_fingerprint]["version"],
268
261
  :arch => @report_data[:host_fingerprint]["architecture"]
269
262
  }
270
- db.report_note(note.merge(:data => data))
263
+ db_report(:note, note.merge(:data => data))
271
264
  end
272
265
 
273
266
  def record_service(attrs)
@@ -292,8 +285,12 @@ module Rex
292
285
  when "port"
293
286
  port_hash[:port] = v
294
287
  when "name"
295
- port_hash[:name] = v.to_s.downcase.split("(")[0].strip
296
- port_hash.delete(:name) if port_hash[:name] == "<unknown>"
288
+ sname = v.to_s.downcase.split("(")[0].strip
289
+ if sname == "<unknown>"
290
+ port_hash[:name] = nil
291
+ else
292
+ port_hash[:name] = db.nmap_msf_service_map(sname)
293
+ end
297
294
  end
298
295
  end
299
296
  if @state[:service_fingerprint]
@@ -317,7 +314,7 @@ module Rex
317
314
  return if @report_data[:ports].empty?
318
315
  reported = []
319
316
  @report_data[:ports].each do |svc|
320
- reported << db.report_service(svc.merge(:host => host_object))
317
+ reported << db_report(:service, svc.merge(:host => host_object))
321
318
  end
322
319
  reported
323
320
  end
@@ -303,43 +303,43 @@ module Rex
303
303
  def report_traceroute(host_object)
304
304
  return unless host_object.kind_of? ::Msf::DBManager::Host
305
305
  return unless @report_data[:traceroute]
306
- db.report_note(
306
+ tr_note = {
307
307
  :workspace => host_object.workspace,
308
308
  :host => host_object,
309
309
  :type => "host.nmap.traceroute",
310
- :data => {
311
- 'port' => @report_data[:traceroute]["port"].to_i,
310
+ :data => { 'port' => @report_data[:traceroute]["port"].to_i,
312
311
  'proto' => @report_data[:traceroute]["proto"].to_s,
313
- 'hops' => @report_data[:traceroute][:hops]
314
- }
315
- )
312
+ 'hops' => @report_data[:traceroute][:hops] }
313
+ }
314
+ db_report(:note, tr_note)
316
315
  end
317
316
 
318
317
  def report_uptime(host_object)
319
318
  return unless host_object.kind_of? ::Msf::DBManager::Host
320
319
  return unless @report_data[:last_boot]
321
- db.report_note(
320
+ up_note = {
322
321
  :workspace => host_object.workspace,
323
322
  :host => host_object,
324
323
  :type => "host.last_boot",
325
- :data => { :time => @report_data[:last_boot] }
326
- )
324
+ :data => { :time => @report_data[:last_boot] }
325
+ }
326
+ db_report(:note, up_note)
327
327
  end
328
328
 
329
329
  def report_fingerprint(host_object)
330
330
  return unless host_object.kind_of? ::Msf::DBManager::Host
331
331
  return unless @report_data[:os_fingerprint]
332
- db.report_note(
333
- @report_data[:os_fingerprint].merge(
334
- :workspace => host_object.workspace,
335
- :host => host_object
336
- )
337
- )
332
+ fp_note = @report_data[:os_fingerprint].merge(
333
+ {
334
+ :workspace => host_object.workspace,
335
+ :host => host_object
336
+ })
337
+ db_report(:note, fp_note)
338
338
  end
339
339
 
340
340
  def report_host(&block)
341
341
  if host_is_okay
342
- host_object = db.report_host( @report_data.merge(
342
+ host_object = db_report(:host, @report_data.merge(
343
343
  :workspace => @args[:wspace] ) )
344
344
  db.emit(:address,@report_data[:host],&block) if block
345
345
  host_object
@@ -352,7 +352,7 @@ module Rex
352
352
  return if @report_data[:ports].empty?
353
353
  reported = []
354
354
  @report_data[:ports].each do |svc|
355
- reported << db.report_service(svc.merge(:host => host_object))
355
+ reported << db_report(:service, svc.merge(:host => host_object))
356
356
  end
357
357
  reported
358
358
  end
@@ -114,11 +114,48 @@ module Parser
114
114
  return true
115
115
  end
116
116
 
117
- # XXX: Define this
117
+ # XXX: Document classes ought to define this
118
118
  def determine_port_state(v)
119
119
  return v
120
120
  end
121
121
 
122
+ # Circumvent the unknown attribute logging by the various reporters. They
123
+ # seem to be there just for debugging anyway.
124
+ def db_report(table, data)
125
+ raise "Data should be a hash" unless data.kind_of? Hash
126
+ nonempty_data = data.reject {|k,v| v.nil?}
127
+ valid_attrs = db_valid_attributes(table)
128
+ raise "Unknown table `#{table}'" if valid_attrs.empty?
129
+ if table == :note # Notes don't whine about extra stuff
130
+ just_the_facts = nonempty_data
131
+ else
132
+ just_the_facts = nonempty_data.select {|k,v| valid_attrs.include? k.to_s.to_sym}
133
+ end
134
+ just_the_facts.empty? ? return : db.send("report_#{table}", just_the_facts)
135
+ end
136
+
137
+ # XXX: It would be better to either have a single registry of acceptable
138
+ # keys if we're going to alert on bad ones, or to be more forgiving if
139
+ # the caller is this thing. There is basically no way to tell if
140
+ # report_host()'s tastes are going to change with this scheme.
141
+ def db_valid_attributes(table)
142
+ case table.to_s.to_sym
143
+ when :host
144
+ Msf::DBManager::Host.new.attribute_names.map {|x| x.to_sym} |
145
+ [:host, :workspace]
146
+ when :service
147
+ Msf::DBManager::Service.new.attribute_names.map {|x| x.to_sym} |
148
+ [:host, :host_name, :mac, :workspace]
149
+ when :vuln
150
+ Msf::DBManager::Vuln.new.attribute_names.map {|x| x.to_sym} |
151
+ [:host, :refs, :workspace, :port, :proto]
152
+ when :note
153
+ [:anything]
154
+ else
155
+ []
156
+ end
157
+ end
158
+
122
159
  # Nokogiri 1.4.4 (and presumably beyond) generates attrs as pairs,
123
160
  # like [["value1","foo"],["value2","bar"]] (but not hashes for some
124
161
  # reason). 1.4.3.1 (and presumably 1.4.3.x and prior) generates attrs
@@ -139,13 +176,30 @@ module Parser
139
176
  return attr_pairs
140
177
  end
141
178
 
179
+ # This breaks xml-encoded characters, so need to append.
180
+ # It's on the end_element tag name to turn the appending
181
+ # off and clear out the data.
182
+ def characters(text)
183
+ return unless @state[:has_text]
184
+ @text ||= ""
185
+ @text << text
186
+ end
187
+
188
+ # Effectively the same as characters()
189
+ def cdata_block(text)
190
+ return unless @state[:has_text]
191
+ @text ||= ""
192
+ @text << text
193
+ end
194
+
142
195
  def end_document
143
196
  block = @block
197
+ return unless @report_type_ok
144
198
  unless @state[:current_tag].empty?
145
199
  missing_ends = @state[:current_tag].keys.map {|x| "'#{x}'"}.join(", ")
146
200
  msg = "Warning, the provided file is incomplete, and there may be missing\n"
147
201
  msg << "data. The following tags were not closed: #{missing_ends}."
148
- db.emit(:warning,msg,&block)
202
+ db.emit(:warning,msg,&block) if block
149
203
  end
150
204
  end
151
205
 
@@ -29,8 +29,18 @@ module Scanner
29
29
  msg = hit[1].is_a?(Array) ? hit[1].join(" ") : hit[1]
30
30
  $stdout.puts pe.ptr_s(vma) + " " + msg
31
31
  if(param['disasm'])
32
+ #puts [msg].pack('H*').inspect
32
33
  insns = []
33
- d2 = Metasm::Shellcode.decode(msg, Metasm::Ia32.new).disassembler
34
+ msg.gsub!("; ", "\n")
35
+ if msg.include?("retn")
36
+ msg.gsub!("retn", "ret")
37
+ end
38
+ #puts msg
39
+ begin
40
+ d2 = Metasm::Shellcode.assemble(Metasm::Ia32.new, msg).disassemble
41
+ rescue Metasm::ParseError
42
+ d2 = Metasm::Shellcode.disassemble(Metasm::Ia32.new, [msg].pack('H*')[0])
43
+ end
34
44
  addr = 0
35
45
  while ((di = d2.disassemble_instruction(addr)))
36
46
  insns << di.instruction
@@ -39,6 +49,9 @@ module Scanner
39
49
  $stdout.puts disasm
40
50
  addr = di.next_addr
41
51
  end
52
+ # ::Rex::Assembly::Nasm.disassemble([msg].pack("H*")).split("\n").each do |line|
53
+ # $stdout.puts "\tnasm: #{line.strip}"
54
+ #end
42
55
  end
43
56
  end
44
57
  end
@@ -3,40 +3,44 @@ 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
38
  insns = []
39
- d2 = Metasm::Shellcode.decode(buf, Metasm::Ia32.new).disassembler
39
+ buf.gsub!("; ", "\n")
40
+ if buf.include?("retn")
41
+ buf.gsub!("retn", "ret")
42
+ end
43
+ d2 = Metasm::Shellcode.disassemble(Metasm::Ia32.new, buf)
40
44
  addr = 0
41
45
  while ((di = d2.disassemble_instruction(addr)))
42
46
  insns << di.instruction
@@ -46,7 +50,8 @@ module Search
46
50
  addr = di.next_addr
47
51
  end
48
52
  end
49
- end
53
+
54
+ end
50
55
  end
51
56
 
52
57
  class DumpOffset < DumpRVA
@@ -56,7 +61,7 @@ module Search
56
61
  rescue Rex::PeParsey::BoundsError
57
62
  end
58
63
  end
59
- end
64
+ end
60
65
  end
61
66
  end
62
67
  end
@@ -224,7 +224,8 @@ class Tlv
224
224
  # Converts the TLV to raw.
225
225
  #
226
226
  def to_r
227
- raw = value.to_s;
227
+ # Forcibly convert to ASCII-8BIT encoding
228
+ raw = value.to_s.unpack("C*").pack("C*")
228
229
 
229
230
  if (self.type & TLV_META_TYPE_STRING == TLV_META_TYPE_STRING)
230
231
  raw += "\x00"
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: librex
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.34
5
+ version: 0.0.35
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-27 00:00:00 -05:00
14
+ date: 2011-06-01 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 12756
18
+ description: Rex provides a variety of classes useful for security testing and exploit development. Based on SVN Revision 12804
19
19
  email:
20
20
  - hdm@metasploit.com
21
21
  - jacob.hammack@hammackj.com
@@ -153,10 +153,12 @@ files:
153
153
  - lib/rex/parser/apple_backup_manifestdb.rb
154
154
  - lib/rex/parser/arguments.rb
155
155
  - lib/rex/parser/arguments.rb.ut.rb
156
+ - lib/rex/parser/foundstone_nokogiri.rb
156
157
  - lib/rex/parser/ini.rb
157
158
  - lib/rex/parser/ini.rb.ut.rb
158
159
  - lib/rex/parser/ip360_aspl_xml.rb
159
160
  - lib/rex/parser/ip360_xml.rb
161
+ - lib/rex/parser/mbsa_nokogiri.rb
160
162
  - lib/rex/parser/nessus_xml.rb
161
163
  - lib/rex/parser/netsparker_xml.rb
162
164
  - lib/rex/parser/nexpose_raw_nokogiri.rb