librex 0.0.35 → 0.0.36

Sign up to get free protection for your applications and to get access to all the features.
@@ -148,7 +148,7 @@ protected
148
148
  closed = true
149
149
  wlog("monitor_rsock: closed remote socket due to nil read")
150
150
  end
151
- rescue ::Exception
151
+ rescue ::Exception => e
152
152
  closed = true
153
153
  wlog("monitor_rsock: exception during read: #{e.class} #{e}")
154
154
  end
@@ -169,10 +169,11 @@ protected
169
169
  # This way we naturally perform a resend if a failure occured.
170
170
  # Catches an edge case with meterpreter TCP channels where remote send
171
171
  # failes gracefully and a resend is required.
172
- if (sent.nil? or sent <= 0)
172
+ if (sent.nil?)
173
+ closed = true
173
174
  wlog("monitor_rsock: failed writing, socket must be dead")
174
175
  break
175
- else
176
+ elsif (sent > 0)
176
177
  total_sent += sent
177
178
  end
178
179
  rescue ::IOError, ::EOFError => e
@@ -0,0 +1,394 @@
1
+ require File.join(File.expand_path(File.dirname(__FILE__)),"nokogiri_doc_mixin")
2
+ require 'rex'
3
+ require 'uri'
4
+
5
+ module Rex
6
+ module Parser
7
+
8
+ # If Nokogiri is available, define the Acunetix document class.
9
+ load_nokogiri && class AcunetixDocument < Nokogiri::XML::SAX::Document
10
+
11
+ include NokogiriDocMixin
12
+
13
+ # The resolver prefers your local /etc/hosts (or windows equiv), but will
14
+ # fall back to regular DNS. It retains a cache for the import to avoid
15
+ # spamming your network with DNS requests.
16
+ attr_reader :resolv_cache
17
+
18
+ # If name resolution of the host fails out completely, you will not be
19
+ # able to import that Scan task. Other scan tasks in the same report
20
+ # should be unaffected.
21
+ attr_reader :parse_warnings
22
+
23
+ def start_document
24
+ @parse_warnings = []
25
+ @resolv_cache = {}
26
+ end
27
+
28
+ def start_element(name=nil,attrs=[])
29
+ attrs = normalize_attrs(attrs)
30
+ block = @block
31
+ @state[:current_tag][name] = true
32
+ case name
33
+ when "Scan" # Start of the thing.
34
+ when "Name", "StartURL", "Banner", "Os"
35
+ @state[:has_text] = true
36
+ when "LoginSequence" # Skipping for now
37
+ when "Crawler"
38
+ record_crawler(attrs)
39
+ when "FullURL"
40
+ @state[:has_text] = true
41
+ when "Variable"
42
+ record_variable(attrs)
43
+ when "Request", "Response"
44
+ @state[:has_text] = true
45
+ end
46
+ end
47
+
48
+ def end_element(name=nil)
49
+ block = @block
50
+ case name
51
+ when "Scan"
52
+ # Clears most of the @state out, we're done with this web site.
53
+ @state.delete_if {|k| k != :current_tag}
54
+ when "Name"
55
+ @state[:has_text] = false
56
+ collect_scan_name
57
+ collect_report_item_name
58
+ @text = nil
59
+ when "StartURL" # Populates @state[:starturl_uri], we use this a lot
60
+ @state[:has_text] = false
61
+ collect_host
62
+ collect_service
63
+ @text = nil
64
+ handle_parse_warnings &block
65
+ host_object = report_host &block
66
+ if host_object
67
+ report_starturl_service(host_object,&block)
68
+ db.report_import_note(@args[:wspace],host_object)
69
+ end
70
+ when "Banner"
71
+ @state[:has_text] = false
72
+ collect_and_report_banner
73
+ when "Os"
74
+ @state[:has_text] = false
75
+ report_os_fingerprint
76
+ when "LoginSequence" # This comes up later in the report anyway
77
+ when "Crawler"
78
+ report_starturl_web_site(&block)
79
+ when "FullURL"
80
+ @state[:has_text] = false
81
+ report_web_site(@text,&block)
82
+ @text = nil
83
+ when "Inputs"
84
+ report_web_form(&block)
85
+ when "Request"
86
+ @state[:has_text] = false
87
+ collect_page_request
88
+ @text = nil
89
+ when "Response"
90
+ @state[:has_text] = false
91
+ collect_page_response
92
+ @text = nil
93
+ report_web_page(&block)
94
+ end
95
+ @state[:current_tag].delete name
96
+ end
97
+
98
+ def collect_page_response
99
+ return unless in_tag("TechnicalDetails")
100
+ return unless in_tag("ReportItem")
101
+ return unless @text
102
+ return if @text.to_s.empty?
103
+ @state[:page_response] = @text
104
+ end
105
+
106
+ def collect_page_request
107
+ return unless in_tag("TechnicalDetails")
108
+ return unless in_tag("ReportItem")
109
+ return unless @text
110
+ return if @text.to_s.empty?
111
+ @state[:page_request] = @text
112
+ end
113
+
114
+ def collect_scan_name
115
+ return unless in_tag("Scan")
116
+ return if in_tag("ReportItems")
117
+ return if in_tag("Crawler")
118
+ return unless @text
119
+ return if @text.strip.empty?
120
+ @state[:scan_name] = @text.strip
121
+ end
122
+
123
+ def collect_host
124
+ return unless in_tag("Scan")
125
+ return unless @text
126
+ return if @text.strip.empty?
127
+ uri = URI.parse(@text) rescue nil
128
+ return unless uri
129
+ address = resolve_scan_starturl_address(uri)
130
+ @report_data[:host] = address
131
+ @report_data[:state] = Msf::HostState::Alive
132
+ end
133
+
134
+ def collect_service
135
+ return unless @report_data[:host]
136
+ return unless in_tag("Scan")
137
+ return unless @text
138
+ return if @text.strip.empty?
139
+ uri = URI.parse(@text) rescue nil
140
+ return unless uri
141
+ @state[:starturl_uri] = uri
142
+ @report_data[:ports] ||= []
143
+ @report_data[:ports] << @state[:starturl_port]
144
+ end
145
+
146
+ def collect_and_report_banner
147
+ return unless (svc = @state[:starturl_service_object]) # Yes i want assignment
148
+ return unless @text
149
+ return if @text.strip.empty?
150
+ return unless in_tag("Scan")
151
+ svc_info = {
152
+ :host => svc.host,
153
+ :port => svc.port,
154
+ :proto => svc.proto,
155
+ :info => @text.strip
156
+ }
157
+ db_report(:service, svc_info)
158
+ @text = nil
159
+ end
160
+
161
+ def collect_report_item_name
162
+ return unless in_tag("ReportItem")
163
+ return unless @text
164
+ return if @text.strip.empty?
165
+ @state[:report_item] = @text
166
+ end
167
+
168
+ # @state[:fullurl] is set by report_web_site
169
+ def record_variable(attrs)
170
+ return unless in_tag("Inputs")
171
+ return unless @state[:fullurl].kind_of? URI
172
+ method = attr_hash(attrs)["Type"]
173
+ return unless method
174
+ return if method.strip.empty?
175
+ @state[:form_variables] ||= []
176
+ @state[:form_variables] << [attr_hash(attrs)["Name"],method]
177
+ end
178
+
179
+ def record_crawler(attrs)
180
+ return unless in_tag("Scan")
181
+ return unless @state[:starturl_service_object]
182
+ starturl = attr_hash(attrs)["StartUrl"]
183
+ return unless starturl
184
+ @state[:crawler_starturl] = starturl
185
+ end
186
+
187
+ def report_web_form(&block)
188
+ return unless in_tag("SiteFiles")
189
+ return unless @state[:web_site]
190
+ return unless @state[:fullurl].kind_of? URI
191
+ return unless @state[:form_variables].kind_of? Array
192
+ return if @state[:form_variables].empty?
193
+ method = @state[:form_variables].first[1]
194
+ vars = @state[:form_variables].map {|x| x[0]}
195
+ form_info = {}
196
+ form_info[:web_site] = @state[:web_site]
197
+ form_info[:path] = @state[:fullurl].path
198
+ form_info[:query] = @state[:fullurl].query
199
+ form_info[:method] = method
200
+ form_info[:params] = vars
201
+ url = @state[:fullurl].to_s
202
+ db.emit(:web_form,url,&block) if block
203
+ db_report(:web_form,form_info)
204
+ @state[:fullurl] = nil
205
+ @state[:form_variables] = nil
206
+ end
207
+
208
+ def report_web_page(&block)
209
+ return if should_skip_this_page
210
+ return unless @state[:web_site]
211
+ return unless @state[:page_request]
212
+ return if @state[:page_request].strip.empty?
213
+ return unless @state[:page_response]
214
+ return if @state[:page_response].strip.empty?
215
+ path,query_string = parse_request(@state[:page_request])
216
+ return unless path
217
+ parsed_response = parse_response(@state[:page_response])
218
+ return unless parsed_response
219
+ web_page_info = {}
220
+ web_page_info[:web_site] = @state[:web_site]
221
+ web_page_info[:path] = path
222
+ web_page_info[:code] = parsed_response[:code].to_i
223
+ web_page_info[:headers] = parsed_response[:headers]
224
+ web_page_info[:body] = parsed_response[:body]
225
+ web_page_info[:query] = query_string || ""
226
+ url = ""
227
+ url << @state[:web_site].service.name.to_s << "://"
228
+ url << @state[:web_site].vhost.to_s << ":"
229
+ url << path
230
+ uri = URI.parse(url) rescue nil
231
+ return unless uri # Sanity checker
232
+ db.emit(:web_page, url, &block) if block
233
+ web_page_object = db_report(:web_page,web_page_info)
234
+ @state[:page_request] = @state[:page_response] = nil
235
+ @state[:web_page] = web_page_object
236
+ end
237
+
238
+ # Reasons why we shouldn't collect a particular web page.
239
+ def should_skip_this_page
240
+ if @state[:report_item] =~ /Unrestricted File Upload/
241
+ # This means that the page being collected is something the
242
+ # auditor put there, so it's not useful to report on.
243
+ return true
244
+ end
245
+ return false
246
+ end
247
+
248
+ # XXX Rex::Proto::Http::Packet seems broken for
249
+ # actually parsing requests and responses, but all I
250
+ # need are the headers anyway
251
+ def parse_request(request)
252
+ headers = Rex::Proto::Http::Packet::Header.new
253
+ headers.from_s request.dup # It's destructive.
254
+ return unless headers.cmd_string
255
+ verb,req = headers.cmd_string.split(/\s+/)
256
+ return unless verb
257
+ return unless req
258
+ path,query_string = req.split(/\?/)[0,2]
259
+ end
260
+
261
+ def parse_response(response)
262
+ headers = Rex::Proto::Http::Packet::Header.new
263
+ headers.from_s response.dup # It's destructive.
264
+ return unless headers.cmd_string
265
+ http,code,msg = headers.cmd_string.split(/\s+/)
266
+ return unless code
267
+ return unless code.to_i.to_s == code
268
+ parsed = {}
269
+ parsed[:code] = code
270
+ parsed[:headers] = {}
271
+ headers.each do |k,v|
272
+ parsed[:headers][k.to_s.downcase] = []
273
+ parsed[:headers][k.to_s.downcase] << v
274
+ end
275
+ parsed[:body] = "" # We never seem to get this from Acunetix
276
+ parsed
277
+ end
278
+
279
+ def report_host(&block)
280
+ return unless @report_data[:host]
281
+ return unless in_tag("Scan")
282
+ if host_is_okay
283
+ db.emit(:address,@report_data[:host],&block) if block
284
+ host_info = @report_data.merge(:workspace => @args[:wspace])
285
+ db_report(:host,host_info)
286
+ end
287
+ end
288
+
289
+ # The service is super important, so we hang on to it for the
290
+ # rest of the scan.
291
+ def report_starturl_service(host_object,&block)
292
+ return unless host_object
293
+ return unless @state[:starturl_uri]
294
+ name = @state[:starturl_uri].scheme
295
+ port = @state[:starturl_uri].port
296
+ addr = host_object.address
297
+ svc = {
298
+ :host => host_object,
299
+ :port => port,
300
+ :name => name.dup,
301
+ :proto => "tcp"
302
+ }
303
+ if name and port
304
+ db.emit(:service,[addr,port].join(":"),&block) if block
305
+ @state[:starturl_service_object] = db_report(:service,svc)
306
+ end
307
+ end
308
+
309
+ def report_web_site(url,&block)
310
+ return unless in_tag("Crawler")
311
+ return unless url
312
+ return if url.strip.empty?
313
+ uri = URI.parse(url) rescue nil
314
+ return unless uri
315
+ host = uri.host
316
+ port = uri.port
317
+ scheme = uri.scheme
318
+ return unless scheme[/^https?/]
319
+ return unless (host && port && scheme)
320
+ address = resolve_address(host)
321
+ return unless address
322
+ service_info = [ @args[:wspace], address, "tcp", port ]
323
+ service_object = db.get_service(*service_info)
324
+ service_object = db_report(:service,service_info) unless service_object
325
+ web_site_info = {
326
+ :workspace => @args[:wspace],
327
+ :service => service_object,
328
+ :vhost => host,
329
+ :ssl => (scheme == "https")
330
+ }
331
+ @state[:web_site] = db_report(:web_site,web_site_info)
332
+ @state[:fullurl] = uri
333
+ end
334
+
335
+ def report_starturl_web_site(&block)
336
+ return unless @state[:crawler_starturl]
337
+ starturl = @state[:crawler_starturl].dup
338
+ report_web_site(starturl,&block)
339
+ end
340
+
341
+ def report_os_fingerprint
342
+ return unless @state[:starturl_service_object]
343
+ return unless @text
344
+ return if @text.strip.empty?
345
+ return unless in_tag("Scan")
346
+ host = @state[:starturl_service_object].host
347
+ fp_note = {
348
+ :workspace => host.workspace,
349
+ :host => host,
350
+ :type => 'host.os.acunetix_fingerprint',
351
+ :data => {:os => @text}
352
+ }
353
+ db_report(:note, fp_note)
354
+ @text = nil
355
+ end
356
+
357
+ def resolve_port(uri)
358
+ @state[:port] = uri.port
359
+ unless @state[:port]
360
+ @parse_warnings << "Could not determine a port for '#{@state[:scan_name]}'"
361
+ end
362
+ @state[:port] = uri.port
363
+ end
364
+
365
+ def resolve_address(host)
366
+ return @resolv_cache[host] if @resolv_cache[host]
367
+ address = Rex::Socket.resolv_to_dotted(host) rescue nil
368
+ @resolv_cache[host] = address
369
+ return address
370
+ end
371
+
372
+ def resolve_scan_starturl_address(uri)
373
+ if uri.host
374
+ address = resolve_address(uri.host)
375
+ unless address
376
+ @parse_warnings << "Could not resolve address for '#{uri.host}', skipping '#{@state[:scan_name]}'"
377
+ end
378
+ else
379
+ @parse_warnings << "Could not determine a host for '#{@state[:scan_name]}'"
380
+ end
381
+ address
382
+ end
383
+
384
+ def handle_parse_warnings(&block)
385
+ return if @parse_warnings.empty?
386
+ @parse_warnings.each do |pwarn|
387
+ db.emit(:warning, pwarn, &block) if block
388
+ end
389
+ end
390
+
391
+ end
392
+ end
393
+ end
394
+
@@ -0,0 +1,366 @@
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 AppScan document class.
7
+ load_nokogiri && class AppscanDocument < Nokogiri::XML::SAX::Document
8
+
9
+ include NokogiriDocMixin
10
+
11
+ # The resolver prefers your local /etc/hosts (or windows equiv), but will
12
+ # fall back to regular DNS. It retains a cache for the import to avoid
13
+ # spamming your network with DNS requests.
14
+ attr_reader :resolv_cache
15
+
16
+ # If name resolution of the host fails out completely, you will not be
17
+ # able to import that Scan task. Other scan tasks in the same report
18
+ # should be unaffected.
19
+ attr_reader :parse_warning
20
+
21
+ def start_document
22
+ @parse_warnings = []
23
+ @resolv_cache = {}
24
+ end
25
+
26
+ def start_element(name=nil,attrs=[])
27
+ attrs = normalize_attrs(attrs)
28
+ block = @block
29
+ @state[:current_tag][name] = true
30
+ case name
31
+ when "Issue" # Start of the stuff we want
32
+ collect_issue(attrs)
33
+ when "Entity" # Start of the stuff we want
34
+ collect_entity(attrs)
35
+ when "Severity", "Url", "OriginalHttpTraffic"
36
+ @state[:has_text] = true
37
+ end
38
+ end
39
+
40
+ def end_element(name=nil)
41
+ block = @block
42
+ case name
43
+ when "Issue" # Wrap it up
44
+ record_issue
45
+ # Reset the state once we close an issue
46
+ @state = @state.select do
47
+ |k| [:current_tag, :web_sites].include? k
48
+ end
49
+ when "Url" # Populates @state[:web_site]
50
+ @state[:has_text] = false
51
+ record_url
52
+ @text = nil
53
+ report_web_site(&block)
54
+ handle_parse_warnings(&block)
55
+ when "Severity"
56
+ @state[:has_text] = false
57
+ record_risk
58
+ @text = nil
59
+ when "OriginalHttpTraffic" # Request and response
60
+ @state[:has_text] = false
61
+ record_request_and_response
62
+ report_service_info
63
+ page_info = report_web_page(&block)
64
+ if page_info
65
+ form_info = report_web_form(page_info,&block)
66
+ if form_info
67
+ report_web_vuln(form_info,&block)
68
+ end
69
+ end
70
+ @text = nil
71
+ end
72
+ @state[:current_tag].delete name
73
+ end
74
+
75
+ def report_web_vuln(form_info,&block)
76
+ return unless(in_issue && has_text)
77
+ return unless form_info.kind_of? Hash
78
+ return unless @state[:issue]
79
+ return unless @state[:issue]["Noise"]
80
+ return unless @state[:issue]["Noise"].to_s.downcase == "false"
81
+ return unless @state[:issue][:vuln_param]
82
+ web_vuln_info = {}
83
+ web_vuln_info[:web_site] = form_info[:web_site]
84
+ web_vuln_info[:path] = form_info[:path]
85
+ web_vuln_info[:query] = form_info[:query]
86
+ web_vuln_info[:method] = form_info[:method]
87
+ web_vuln_info[:params] = form_info[:params]
88
+ web_vuln_info[:pname] = @state[:issue][:vuln_param]
89
+ web_vuln_info[:proof] = "" # TODO: pick this up from <Difference> maybe?
90
+ web_vuln_info[:risk] = @state[:issue][:risk]
91
+ web_vuln_info[:name] = @state[:issue]["IssueTypeID"]
92
+ web_vuln_info[:category] = "imported"
93
+ web_vuln_info[:confidence] = 100 # Seems pretty binary, noise or not
94
+ db.emit(:web_vuln, web_vuln_info[:name], &block) if block
95
+ web_vuln = db_report(:web_vuln, web_vuln_info)
96
+ end
97
+
98
+ def collect_entity(attrs)
99
+ return unless in_issue
100
+ return unless @state[:issue].kind_of? Hash
101
+ ent_hash = attr_hash(attrs)
102
+ return unless ent_hash
103
+ return unless ent_hash["Type"].to_s.downcase == "parameter"
104
+ @state[:issue][:vuln_param] = ent_hash["Name"]
105
+ end
106
+
107
+ def report_web_form(page_info,&block)
108
+ return unless(in_issue && has_text)
109
+ return unless page_info.kind_of? Hash
110
+ return unless @state[:request_body]
111
+ return if @state[:request_body].strip.empty?
112
+ web_form_info = {}
113
+ web_form_info[:web_site] = page_info[:web_site]
114
+ web_form_info[:path] = page_info[:path]
115
+ web_form_info[:query] = page_info[:query]
116
+ web_form_info[:method] = @state[:request_headers].cmd_string.split(/\s+/)[0]
117
+ parsed_params = parse_params(@state[:request_body])
118
+ return unless parsed_params
119
+ return if parsed_params.empty?
120
+ web_form_info[:params] = parsed_params
121
+ web_form = db_report(:web_form, web_form_info)
122
+ @state[:web_forms] ||= []
123
+ unless @state[:web_forms].include? web_form
124
+ db.emit(:web_form, @state[:uri].to_s, &block) if block
125
+ @state[:web_forms] << web_form
126
+ end
127
+ web_form_info
128
+ end
129
+
130
+ def parse_params(request_body)
131
+ return unless request_body
132
+ pairs = request_body.split(/&/)
133
+ params = []
134
+ pairs.each do |pair|
135
+ param,value = pair.split("=",2)
136
+ params << [param,""] # Can't tell what's default
137
+ end
138
+ params
139
+ end
140
+
141
+ def report_web_page(&block)
142
+ return unless(in_issue && has_text)
143
+ return unless @state[:web_site]
144
+ return unless @state[:response_headers]
145
+ return unless @state[:uri]
146
+ web_page_info = {}
147
+ web_page_info[:web_site] = @state[:web_site]
148
+ web_page_info[:path] = @state[:uri].path
149
+ web_page_info[:body] = @state[:response_body].to_s
150
+ web_page_info[:query] = @state[:uri].query
151
+ code = @state[:response_headers].cmd_string.split(/\s+/)[1]
152
+ return unless code
153
+ web_page_info[:code] = code
154
+ parsed_headers = {}
155
+ @state[:response_headers].each do |k,v|
156
+ parsed_headers[k.to_s.downcase] ||= []
157
+ parsed_headers[k.to_s.downcase] << v
158
+ end
159
+ return if parsed_headers.empty?
160
+ web_page_info[:headers] = parsed_headers
161
+ web_page = db_report(:web_page, web_page_info)
162
+ @state[:web_pages] ||= []
163
+ unless @state[:web_pages].include? web_page
164
+ db.emit(:web_page, @state[:uri].to_s, &block) if block
165
+ @state[:web_pages] << web_page
166
+ end
167
+ web_page_info
168
+ end
169
+
170
+ def report_service_info
171
+ return unless(in_issue && has_text)
172
+ return unless @state[:web_site]
173
+ return unless @state[:response_headers]
174
+ banner = @state[:response_headers]["server"]
175
+ return unless banner
176
+ service = @state[:web_site].service
177
+ return unless service.info.to_s.empty?
178
+ service_info = {
179
+ :host => service.host,
180
+ :port => service.port,
181
+ :proto => service.proto,
182
+ :info => banner
183
+ }
184
+ db_report(:service, service_info)
185
+ end
186
+
187
+ def record_request_and_response
188
+ return unless(in_issue && has_text)
189
+ return unless @state[:web_site]
190
+ really_original_traffic = unindent_and_crlf(@text)
191
+ split_traffic = really_original_traffic.split(/\r\n\r\n/)
192
+ request_headers_text = split_traffic.first
193
+ content_length = 0
194
+ if request_headers_text =~ /\ncontent-length:\s+([0-9]+)/mni
195
+ content_length = $1.to_i
196
+ end
197
+ if(content_length > 0) and (split_traffic[1].to_s.size >= content_length)
198
+ request_body_text = split_traffic[1].to_s[0,content_length]
199
+ else
200
+ request_body_text = nil
201
+ end
202
+ response_headers_text = split_traffic[1].to_s[content_length,split_traffic[1].to_s.size].lstrip
203
+ request = request_headers_text
204
+ return unless(request && response_headers_text)
205
+ response_body_text = split_traffic[2]
206
+ req_header = Rex::Proto::Http::Packet::Header.new
207
+ res_header = Rex::Proto::Http::Packet::Header.new
208
+ req_header.from_s request_headers_text.dup
209
+ res_header.from_s response_headers_text.dup
210
+ @state[:request_headers] = req_header
211
+ @state[:request_body] = request_body_text
212
+ @state[:response_headers] = res_header
213
+ @state[:response_body] = response_body_text
214
+ end
215
+
216
+ # Appscan tab-indents which makes parsing a little difficult. They
217
+ # also don't record CRLFs, just LFs.
218
+ def unindent_and_crlf(text)
219
+ second_line = text.split(/\r*\n/)[1]
220
+ indent_level = second_line.size - second_line.lstrip.size
221
+ unindented_text_lines = []
222
+ text.split(/\r*\n/).each do |line|
223
+ if line =~ /^\t{#{indent_level}}/
224
+ unindented_line = line[indent_level,line.size]
225
+ unindented_text_lines << unindented_line
226
+ else
227
+ unindented_text_lines << line
228
+ end
229
+ end
230
+ unindented_text_lines.join("\r\n")
231
+ end
232
+
233
+ def record_risk
234
+ return unless(in_issue && has_text)
235
+ @state[:issue] ||= {}
236
+ @state[:issue][:risk] = map_severity_to_risk
237
+ end
238
+
239
+ def map_severity_to_risk
240
+ case @text.to_s.downcase
241
+ when "high" ; 5
242
+ when "medium" ; 3
243
+ when "low" ; 1
244
+ else ; 0
245
+ end
246
+ end
247
+
248
+ # TODO
249
+ def record_issue
250
+ return unless in_issue
251
+ return unless @report_data[:issue].kind_of? Hash
252
+ return unless @state[:web_site]
253
+ return if @state[:issue]["Noise"].to_s.downcase == "true"
254
+ end
255
+
256
+ def collect_issue(attrs)
257
+ return unless in_issue
258
+ @state[:issue] = {}
259
+ @state[:issue].merge! attr_hash(attrs)
260
+ end
261
+
262
+ def report_web_site(&block)
263
+ return unless @state[:uri]
264
+ uri = @state[:uri]
265
+ hostname = uri.host # Assume the first one is the real hostname
266
+ address = resolve_issue_url_address(uri)
267
+ return unless address
268
+ unless @resolv_cache.values.include? address
269
+ db.emit(:address, address, &block) if block
270
+ end
271
+ port = resolve_port(uri)
272
+ return unless port
273
+ scheme = uri.scheme
274
+ return unless scheme
275
+ web_site_info = {:workspace => @args[:wspace]}
276
+ web_site_info[:vhost] = hostname
277
+ service_obj = check_for_existing_service(address,port)
278
+ if service_obj
279
+ web_site_info[:service] = service_obj
280
+ else
281
+ web_site_info[:host] = address
282
+ web_site_info[:port] = port
283
+ web_site_info[:ssl] = scheme == "https"
284
+ end
285
+ web_site_obj = db_report(:web_site, web_site_info)
286
+ @state[:web_sites] ||= []
287
+ unless @state[:web_sites].include? web_site_obj
288
+ url = "#{uri.scheme}://#{uri.host}:#{uri.port}"
289
+ db.emit(:web_site, url, &block) if block
290
+ db.report_import_note(@args[:wspace], web_site_obj.service.host)
291
+ @state[:web_sites] << web_site_obj
292
+ end
293
+ @state[:service] = service_obj || web_site_obj.service
294
+ @state[:host] = (service_obj || web_site_obj.service).host
295
+ @state[:web_site] = web_site_obj
296
+ end
297
+
298
+ def check_for_existing_service(address,port)
299
+ db.get_service(@args[:wspace],address,"tcp",port)
300
+ end
301
+
302
+ def resolve_port(uri)
303
+ @state[:port] = uri.port
304
+ unless @state[:port]
305
+ @parse_warnings << "Could not determine a port for '#{@state[:scan_name]}'"
306
+ end
307
+ return @state[:port]
308
+ end
309
+
310
+ def resolve_address(host)
311
+ return @resolv_cache[host] if @resolv_cache[host]
312
+ address = Rex::Socket.resolv_to_dotted(host) rescue nil
313
+ @resolv_cache[host] = address
314
+ if address
315
+ block = @block
316
+ db.emit(:address, address, &block) if block
317
+ end
318
+ return address
319
+ end
320
+
321
+ # Alias this
322
+ def resolve_issue_url_address(uri)
323
+ if uri.host
324
+ address = resolve_address(uri.host)
325
+ unless address
326
+ @parse_warnings << "Could not resolve address for '#{uri.host}', skipping."
327
+ end
328
+ else
329
+ @parse_warnings << "Could not determine a host for this import."
330
+ end
331
+ address
332
+ end
333
+
334
+ def handle_parse_warnings(&block)
335
+ return if @parse_warnings.empty?
336
+ @parse_warnings.each do |pwarn|
337
+ db.emit(:warning, pwarn, &block) if block
338
+ end
339
+ end
340
+
341
+ def record_url
342
+ return unless in_issue
343
+ return unless has_text
344
+ uri = URI.parse(@text) rescue nil
345
+ return unless uri
346
+ @state[:uri] = uri
347
+ end
348
+
349
+ def in_issue
350
+ return false unless in_tag("Issue")
351
+ return false unless in_tag("Issues")
352
+ return false unless in_tag("XmlReport")
353
+ return true
354
+ end
355
+
356
+ def has_text
357
+ return false unless @text
358
+ return false if @text.strip.empty?
359
+ @text = @text.strip
360
+ end
361
+
362
+ end
363
+
364
+ end
365
+ end
366
+