librex 0.0.34 → 0.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@
3
3
  A non-official re-packaging of the Rex library as a gem for easy of usage of the Metasploit REX framework in a non Metasploit application. I received permission from HDM to create this package.
4
4
 
5
5
  Currently based on:
6
- SVN Revision: 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