22
33require 'uri'
44require 'digest'
5+
56module Msf
67
78###
@@ -11,6 +12,7 @@ module Msf
1112#
1213###
1314module Exploit ::Remote ::HttpClient
15+
1416 include Msf ::Auxiliary ::Report
1517
1618 #
@@ -48,6 +50,7 @@ def initialize(info = {})
4850 OptString . new ( 'DOMAIN' , [ true , 'The domain to use for Windows authentication' , 'WORKSTATION' ] ) ,
4951 OptFloat . new ( 'HttpClientTimeout' , [ false , 'HTTP connection and receive timeout' ] ) ,
5052 OptBool . new ( 'HttpPartialResponses' , [ false , 'Return partial HTTP responses despite timeouts' , false ] ) ,
53+ OptBool . new ( 'HttpKeepCookies' , [ false , 'Keep cookies from HTTP responses for reuse in HTTP requests' , false ] ) ,
5154 OptBool . new ( 'HttpTrace' , [ false , 'Show the raw HTTP requests and responses' , false ] ) ,
5255 OptBool . new ( 'HttpTraceHeadersOnly' , [ false , 'Show HTTP headers only in HttpTrace' , false ] ) ,
5356 OptString . new ( 'HttpTraceColors' , [ false , 'HTTP request and response colors for HttpTrace (unset to disable)' , 'red/blu' ] )
@@ -88,6 +91,9 @@ def initialize(info = {})
8891 )
8992 register_autofilter_ports ( [ 80 , 8080 , 443 , 8000 , 8888 , 8880 , 8008 , 3000 , 8443 ] )
9093 register_autofilter_services ( %W{ http https } )
94+
95+ # Initialize an empty cookie jar to keep cookies
96+ self . cookie_jar = Set . new
9197 end
9298
9399 def deregister_http_client_options
@@ -314,69 +320,81 @@ def cleanup
314320 #
315321 # Passes +opts+ through directly to Rex::Proto::Http::Client#request_raw.
316322 #
317- def send_request_raw ( opts = { } , timeout = 20 , disconnect = false )
323+ def send_request_raw ( opts = { } , timeout = 20 , disconnect = false )
318324 if datastore [ 'HttpClientTimeout' ] && datastore [ 'HttpClientTimeout' ] > 0
319325 actual_timeout = datastore [ 'HttpClientTimeout' ]
320326 else
321- actual_timeout = opts [ :timeout ] || timeout
327+ actual_timeout = opts [ :timeout ] || timeout
322328 end
323329
324- begin
325- c = connect ( opts )
326- r = opts [ :cgi ] ? c . request_cgi ( opts ) : c . request_raw ( opts )
330+ c = connect ( opts )
331+ r = opts [ :cgi ] ? c . request_cgi ( opts ) : c . request_raw ( opts )
327332
328- if datastore [ 'HttpTrace' ]
329- request_color , response_color =
330- ( datastore [ 'HttpTraceColors' ] || '' ) . split ( '/' ) . map { |color | "%bld%#{ color } " }
333+ if datastore [ 'HttpTrace' ]
334+ request_color , response_color =
335+ ( datastore [ 'HttpTraceColors' ] || '' ) . split ( '/' ) . map { |color | "%bld%#{ color } " }
331336
332- request = r . to_s ( headers_only : datastore [ 'HttpTraceHeaders' ] )
337+ request = r . to_s ( headers_only : datastore [ 'HttpTraceHeaders' ] )
333338
334- print_line ( '#' * 20 )
335- print_line ( '# Request:' )
336- print_line ( '#' * 20 )
337- print_line ( "%clr#{ request_color } #{ request } %clr" )
338- end
339+ print_line ( '#' * 20 )
340+ print_line ( '# Request:' )
341+ print_line ( '#' * 20 )
342+ print_line ( "%clr#{ request_color } #{ request } %clr" )
343+ end
339344
340- res = c . send_recv ( r , actual_timeout )
345+ res = c . send_recv ( r , actual_timeout )
341346
342- if datastore [ 'HttpTrace' ]
343- print_line ( '#' * 20 )
344- print_line ( '# Response:' )
345- print_line ( '#' * 20 )
347+ if datastore [ 'HttpTrace' ]
348+ print_line ( '#' * 20 )
349+ print_line ( '# Response:' )
350+ print_line ( '#' * 20 )
346351
347- if res
348- response = res . to_terminal_output ( headers_only : datastore [ 'HttpTraceHeadersOnly' ] )
352+ if res
353+ response = res . to_terminal_output ( headers_only : datastore [ 'HttpTraceHeadersOnly' ] )
349354
350- print_line ( "%clr#{ response_color } #{ response } %clr" )
351- else
352- print_line ( 'No response received' )
353- end
355+ print_line ( "%clr#{ response_color } #{ response } %clr" )
356+ else
357+ print_line ( 'No response received' )
354358 end
359+ end
355360
356- disconnect ( c ) if disconnect
361+ disconnect ( c ) if disconnect
357362
358- res
359- rescue ::Errno ::EPIPE , ::Timeout ::Error => e
360- print_line ( e . message ) if datastore [ 'HttpTrace' ]
361- nil
362- rescue Rex ::ConnectionError => e
363- vprint_error ( e . to_s )
364- nil
365- rescue ::Exception => e
366- print_line ( e . message ) if datastore [ 'HttpTrace' ]
367- raise e
368- end
363+ res
364+ rescue ::Errno ::EPIPE , ::Timeout ::Error => e
365+ print_line ( e . message ) if datastore [ 'HttpTrace' ]
366+ nil
367+ rescue Rex ::ConnectionError => e
368+ vprint_error ( e . to_s )
369+ nil
370+ rescue ::Exception => e
371+ print_line ( e . message ) if datastore [ 'HttpTrace' ]
372+ raise e
369373 end
370374
371-
372375 # Connects to the server, creates a request, sends the request,
373376 # reads the response
374377 #
375378 # Passes `opts` through directly to {Rex::Proto::Http::Client#request_cgi}.
379+ # Set `opts['keep_cookies']` to keep cookies from responses for reuse in requests.
376380 #
377381 # @return (see Rex::Proto::Http::Client#send_recv))
378- def send_request_cgi ( opts = { } , timeout = 20 , disconnect = true )
379- send_request_raw ( opts . merge ( cgi : true ) , timeout , disconnect )
382+ def send_request_cgi ( opts = { } , timeout = 20 , disconnect = true )
383+ if cookie_jar . any?
384+ opts = { 'cookie' => cookie_jar . to_a . join ( ' ' ) } . merge ( opts )
385+ end
386+
387+ res = send_request_raw ( opts . merge ( cgi : true ) , timeout , disconnect )
388+
389+ return unless res
390+ return res unless res . headers [ 'Set-Cookie' ] . present?
391+
392+ if opts [ 'keep_cookies' ] || datastore [ 'HttpKeepCookies' ]
393+ # XXX: CGI::Cookie (get_cookies_parsed) is hella broken
394+ cookie_jar . merge ( res . get_cookies . split ( ' ' ) )
395+ end
396+
397+ res
380398 end
381399
382400 # Connects to the server, creates a request, sends the request, reads the
@@ -871,10 +889,10 @@ def service_details
871889 }
872890 end
873891
874- protected
892+ protected
875893
876894 attr_accessor :client
895+ attr_accessor :cookie_jar
877896
878897end
879-
880898end
0 commit comments