1
1
# -*- coding: binary -*-
2
2
3
3
require 'erb'
4
+ require 'cgi'
5
+ require 'date'
4
6
require 'rex/exploitation/js'
5
7
6
8
###
@@ -16,6 +18,9 @@ module Exploit::Remote::BrowserExploitServer
16
18
include Msf ::Exploit ::Remote ::HttpServer ::HTML
17
19
include Msf ::Exploit ::RopDb
18
20
21
+ # this must be static between runs, otherwise the older cookies will be ignored
22
+ DEFAULT_COOKIE_NAME = '__ua'
23
+
19
24
PROXY_REQUEST_HEADER_SET = Set . new (
20
25
%w{
21
26
CLIENT_IP
@@ -72,10 +77,15 @@ def initialize(info={})
72
77
[
73
78
OptBool . new ( 'Retries' , [ false , "Allow the browser to retry the module" , true ] )
74
79
] , Exploit ::Remote ::BrowserExploitServer )
80
+
81
+ register_advanced_options ( [
82
+ OptString . new ( 'CookieName' , [ false , "The name of the tracking cookie" , DEFAULT_COOKIE_NAME ] ) ,
83
+ OptString . new ( 'CookieExpiration' , [ false , "Cookie expiration in years (blank=expire on exit)" ] )
84
+ ] , Exploit ::Remote ::BrowserExploitServer )
75
85
end
76
86
77
87
#
78
- # Syncs a block of code
88
+ # Allows a block of code to access BES resources in a thread-safe fashion
79
89
#
80
90
# @param block [Proc] Block of code to sync
81
91
#
@@ -89,7 +99,7 @@ def sync(&block)
89
99
# @return [String] URI to the exploit page
90
100
#
91
101
def get_module_resource
92
- "#{ get_resource . chomp ( "/" ) } /#{ @exploit_receiver_page } "
102
+ "#{ get_resource . chomp ( "/" ) } /#{ @exploit_receiver_page } / "
93
103
end
94
104
95
105
#
@@ -191,7 +201,7 @@ def get_bad_requirements(profile)
191
201
192
202
#
193
203
# Returns the target profile based on the tag. Each profile has the following structure:
194
- # 'cookie ' =>
204
+ # 'cookie_name ' =>
195
205
# {
196
206
# :os_name => 'Windows',
197
207
# :os_flavor => 'something'
@@ -238,13 +248,13 @@ def update_profile(target_profile, key, value)
238
248
end
239
249
240
250
#
241
- # Initializes a profile
251
+ # Initializes a profile, if it did not previously exist
242
252
#
243
253
# @param tag [String] A unique string as a way to ID the profile
244
254
#
245
255
def init_profile ( tag )
246
256
sync do
247
- @target_profiles [ tag ] = { }
257
+ @target_profiles [ tag ] || = { }
248
258
end
249
259
end
250
260
@@ -256,14 +266,18 @@ def init_profile(tag)
256
266
#
257
267
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
258
268
#
259
- def retrieve_tag ( request )
260
- tag = request . headers [ 'Cookie' ] . to_s
269
+ def retrieve_tag ( cli , request )
270
+ cookie = CGI ::Cookie . parse ( request . headers [ 'Cookie' ] . to_s )
271
+ tag = cookie . has_key? ( cookie_name ) && cookie [ cookie_name ] . first
261
272
262
273
if tag . blank?
263
274
# Browser probably doesn't allow cookies, plan B :-/
275
+ vprint_status ( "No cookie received, resorting to headers hash." )
264
276
ip = cli . peerhost
265
277
os = request . headers [ 'User-Agent' ]
266
278
tag = Rex ::Text . md5 ( "#{ ip } #{ os } " )
279
+ else
280
+ vprint_status ( "Received cookie '#{ tag } '." )
267
281
end
268
282
269
283
tag
@@ -277,31 +291,25 @@ def retrieve_tag(request)
277
291
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
278
292
#
279
293
def process_browser_info ( source , cli , request )
280
- tag = retrieve_tag ( request )
281
-
282
- # Browser doesn't allow cookies. Can't track that, use a different way to track.
283
- init_profile ( tag ) if request . headers [ 'Cookie' ] . blank?
294
+ tag = retrieve_tag ( cli , request )
295
+ init_profile ( tag )
284
296
target_info = get_profile ( tag )
297
+ update_profile ( target_info , :source , source . to_s )
285
298
286
299
# Gathering target info from the detection stage
287
300
case source
288
301
when :script
289
302
# Gathers target data from a POST request
290
- update_profile ( target_info , :source , 'script' )
291
- raw = Rex ::Text . uri_decode ( Rex ::Text . decode_base64 ( request . body ) )
292
- raw . split ( '&' ) . each do |item |
293
- k , v = item . scan ( /(\w +)=(.*)/ ) . flatten
294
- update_profile ( target_info , k . to_sym , v )
295
- end
296
-
303
+ parsed_body = CGI ::parse ( Rex ::Text . decode_base64 ( request . body ) || '' )
304
+ vprint_debug ( "Received sniffed browser data over POST: \n #{ parsed_body } ." )
305
+ parsed_body . each { |k , v | update_profile ( target_info , k . to_sym , v . first ) }
297
306
when :headers
298
307
# Gathers target data from headers
299
308
# This may be less accurate, and most likely less info.
300
309
fp = fingerprint_user_agent ( request . headers [ 'User-Agent' ] )
301
310
# Module has all the info it needs, ua_string is kind of pointless.
302
311
# Kill this to save space.
303
312
fp . delete ( :ua_string )
304
- update_profile ( target_info , :source , 'headers' )
305
313
fp . each do |k , v |
306
314
update_profile ( target_info , k . to_sym , v )
307
315
end
@@ -356,6 +364,7 @@ def get_detection_html(user_agent)
356
364
return Base64.encode(q.join('&'));
357
365
}
358
366
367
+
359
368
window.onload = function() {
360
369
var osInfo = window.os_detect.getVersion();
361
370
var d = {
@@ -380,8 +389,9 @@ def get_detection_html(user_agent)
380
389
<% end %>
381
390
382
391
var query = objToQuery(d);
383
- postInfo("<%=get_resource.chomp("/")%>/<%=@info_receiver_page%>/", query);
384
- window.location = "<%=get_resource.chomp("/")%>/<%=@exploit_receiver_page%>/";
392
+ postInfo("<%=get_resource.chomp("/")%>/<%=@info_receiver_page%>/", query, function(){
393
+ window.location="<%= get_module_resource %>";
394
+ });
385
395
}
386
396
| ) . result ( binding ( ) )
387
397
@@ -394,11 +404,26 @@ def get_detection_html(user_agent)
394
404
</script>
395
405
<noscript>
396
406
<img style="visibility:hidden" src="#{ get_resource . chomp ( "/" ) } /#{ @noscript_receiver_page } /">
397
- <meta http-equiv="refresh" content="1; url=#{ get_resource . chomp ( "/" ) } / #{ @exploit_receiver_page } / ">
407
+ <meta http-equiv="refresh" content="1; url=#{ get_module_resource } ">
398
408
</noscript>
399
409
|
400
410
end
401
411
412
+ # @return [String] name of the tracking cookie
413
+ def cookie_name
414
+ datastore [ 'CookieName' ] || DEFAULT_COOKIE_NAME
415
+ end
416
+
417
+ def cookie_header ( tag )
418
+ cookie = "#{ cookie_name } =#{ tag } ;"
419
+ if datastore [ 'CookieExpiration' ] . present?
420
+ expires_date = ( DateTime . now + 365 *datastore [ 'CookieExpiration' ] . to_i )
421
+ expires_str = expires_date . to_time . strftime ( "%a, %d %b %Y 12:00:00 GMT" )
422
+ cookie << " Expires=#{ expires } ;"
423
+ end
424
+ cookie
425
+ end
426
+
402
427
#
403
428
# Handles exploit stages.
404
429
#
@@ -411,45 +436,51 @@ def on_request_uri(cli, request)
411
436
#
412
437
# This is the information gathering stage
413
438
#
414
- if get_profile ( retrieve_tag ( request ) )
415
- send_redirect ( cli , " #{ get_resource . chomp ( "/" ) } / #{ @exploit_receiver_page } " )
439
+ if get_profile ( retrieve_tag ( cli , request ) )
440
+ send_redirect ( cli , get_module_resource )
416
441
return
417
442
end
418
443
419
444
print_status ( "Gathering target information." )
420
445
tag = Rex ::Text . rand_text_alpha ( rand ( 20 ) + 5 )
421
- ua = request . headers [ 'User-Agent' ]
446
+ ua = request . headers [ 'User-Agent' ] || ''
422
447
init_profile ( tag )
423
- html = get_detection_html ( ua )
424
- send_response ( cli , html , { 'Set-Cookie' => tag } )
448
+ print_status ( "Sending response HTML." )
449
+ send_response ( cli , get_detection_html ( ua ) , { 'Set-Cookie' => cookie_header ( tag ) } )
425
450
426
451
when /#{ @info_receiver_page } /
427
452
#
428
453
# The detection code will hit this if Javascript is enabled
429
454
#
430
- process_browser_info ( source = :script , cli , request )
431
- send_response ( cli , '' )
455
+ vprint_status "Info receiver page called."
456
+ process_browser_info ( :script , cli , request )
457
+ send_response ( cli , '' , { 'Set-Cookie' => cookie_header ( tag ) } )
432
458
433
459
when /#{ @noscript_receiver_page } /
434
460
#
435
461
# The detection code will hit this instead of Javascript is disabled
436
462
# Should only be triggered by the img src in <noscript>
437
463
#
438
- process_browser_info ( source = :headers , cli , request )
464
+ process_browser_info ( :headers , cli , request )
439
465
send_not_found ( cli )
440
466
441
467
when /#{ @exploit_receiver_page } /
442
468
#
443
469
# This sends the actual exploit. A module should define its own
444
470
# on_request_exploit() to get the target information
445
471
#
446
- tag = retrieve_tag ( request )
472
+ tag = retrieve_tag ( cli , request )
473
+ vprint_status ( "Serving exploit to user with tag #{ tag } " )
447
474
profile = get_profile ( tag )
448
- if profile [ :tried ] and datastore [ 'Retries' ] == false
475
+ if profile . nil?
476
+ print_status ( "Browsing directly to the exploit URL is forbidden." )
477
+ send_not_found ( cli )
478
+ elsif profile [ :tried ] and datastore [ 'Retries' ] == false
449
479
print_status ( "Target with tag \" #{ tag } \" wants to retry the module, not allowed." )
450
480
send_not_found ( cli )
451
481
else
452
482
update_profile ( profile , :tried , true )
483
+ vprint_status ( "Setting target \" #{ tag } \" to :tried." )
453
484
try_set_target ( profile )
454
485
bad_reqs = get_bad_requirements ( profile )
455
486
if bad_reqs . empty?
0 commit comments