require 'rex/service_manager' module Msf ### # # This module provides methods for acting as an HTTP client when # exploiting an HTTP server. # ### module Exploit::Remote::HttpClient # # Initializes an exploit module that exploits a vulnerability in an HTTP # server. # def initialize(info = {}) super register_options( [ Opt::RHOST, Opt::RPORT(80), OptString.new('VHOST', [ false, "HTTP server virtual host" ]) ], Msf::Exploit::Remote::HttpClient) end # # Connects to an HTTP server. # def connect(opts={}) nclient = Rex::Proto::Http::Client.new( datastore['RHOST'], datastore['RPORT'].to_i) # Configure the HTTP client with the supplied parameter nclient.config( { 'vhost' => datastore['VHOST'] }.update(opts)) # If this connection is global, persist it if (opts['global']) if (self.client) disconnect end self.client = nclient end return nclient end # # Passes the client connection down to the handler to see if it's of any # use. # def handler(nsock = nil) # If no socket was provided, try the global one. if ((!nsock) and (self.client)) nsock = self.client.conn end # If the parent claims the socket associated with the HTTP client, then # we rip the socket out from under the HTTP client. if (((rv = super(nsock)) == Handler::Claimed) and (self.client) and (nsock == self.client.conn)) self.client.conn = nil end rv end # # Disconnects the HTTP client # def disconnect(nclient = self.client) if (nclient) nclient.close end if (nclient == self.client) self.client = nil end end # # Performs cleanup as necessary, disconnecting the HTTP client if it's # still established. # def cleanup super disconnect end # # Connects to the server, creates a request, sends the request, reads the response # def request(opts={}) c = connect c.send_request(c.request(opts)) end ## # # Wrappers for getters # ## # # Returns the target host # def rhost datastore['RHOST'] end # # Returns the remote port # def rport datastore['RPORT'] end # # Returns the VHOST of the HTTP server. # def vhost datastore['VHOST'] end protected attr_accessor :client end ### # # This module provides methods for exploiting an HTTP client by acting # as an HTTP server. # ### module Exploit::Remote::HttpServer include Msf::Exploit::Remote::TcpServer protected def initialize(info = {}) super register_options( [ OptString.new('URIPATH', [ false, "The URI to use for this exploit (default is random)"]), ], Exploit::Remote::HttpServer) end # # This mixin starts the HTTP server listener. This routine takes a few # different hash parameters: # # ServerHost => Override the server host to listen on (default to SRVHOST). # ServerPort => Override the server port to listen on (default to SRVPORT). # Uri => The URI to handle and the associated procedure to call. # def start_service(opts = {}) # Default the server host and port to what is required by the mixin. opts = { 'ServerHost' => datastore['SRVHOST'], 'ServerPort' => datastore['SRVPORT'], }.update(opts) # Start the HTTP server service. self.service = Rex::ServiceManager.start(Rex::Proto::Http::Server, opts['ServerPort'].to_i, opts['ServerHost']) # Default the procedure of the URI to on_request_uri if one isn't # provided. uopts = { 'Proc' => Proc.new { |cli, req| on_request_uri(cli, req) }, 'Path' => resource_uri }.update(opts['Uri'] || {}) print_status("Using URL: http://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}") add_resource(uopts) end # # Adds a URI resource using the supplied hash parameters. # # Path => The path to associate the procedure with. # Proc => The procedure to call when the URI is requested. # LongCall => Indicates that the request is a long call. # def add_resource(opts) @service_path = opts['Path'] service.add_resource(opts['Path'], opts) end # # Returns the last-used resource path # def get_resource @service_path end # # Removes a URI resource. # def remove_resource(name) service.remove_resource(name) end # # Closes a client connection. # def close_client(cli) service.close_client(cli) end # # Creates an HTTP response packet. # def create_response(code = 200, message = "OK", proto = Rex::Proto::Http::DefaultProtocol) return Rex::Proto::Http::Response.new(code, message, proto); end # # Transmits an HTML response to the supplied client. # def send_html_response(cli, body, headers = {}) response = create_response response['Content-Type'] = 'text/html' response.body = body headers.each_pair { |k,v| response[k] = v } cli.send_response(response) end # # Transmits an gzip-encoded HTML response to the supplied client. # def send_html_gzip_response(cli, body, headers = {}) response = create_response response['Content-Type'] = 'text/html' response['Content-Encoding'] = 'gzip' response.body = Rex::Text.gzip(body) headers.each_pair { |k,v| response[k] = v } cli.send_response(response) end # # Sends a 302 redirect to the client # def send_redirect(cli, location='/', body='') response = create_response(302, 'Moved') response['Content-Type'] = 'text/html' response['Location'] = location response.body = body cli.send_response(response) end # # Sends a 302 redirect relative to our base path # def send_local_redirect(cli, location) send_redirect(cli, get_resource + location) end # # Returns the configured (or random, if not configured) URI path # def resource_uri path = datastore['URIPATH'] || random_uri path = '/' + path if path !~ /^\// return path end # # Generates a random URI for use with making finger printing more # challenging. # def random_uri "/" + Rex::Text.rand_text_alphanumeric(rand(64) + 10) end # # Re-generates the payload, substituting the current RHOST and RPORT with # the supplied client host and port. # def regenerate_payload(cli) # Update the datastore with the supplied client peerhost/peerport datastore['RHOST'] = cli.peerhost datastore['RPORT'] = cli.peerport # If the payload fails to generate for some reason, send a 403. if ((p = super()) == nil) print_error("Failed to generate payload, sending 403.") cli.send_response( create_response(403, 'Forbidden')) return nil end p end ## # # Override methods # ## # # Called when a request is made to a single URI registered during the # start_service. Subsequent registrations will not result in a call to # on_request_uri. # def on_request_uri(cli, request) end end end