# -*- coding: binary -*- module Msf module EvasiveTCP attr_accessor :_send_size, :_send_delay, :evasive def denagle begin setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) rescue ::Exception end end def write(buf, opts={}) return super(buf, opts) if not @evasive ret = 0 idx = 0 len = @_send_size || buf.length while(idx < buf.length) if(@_send_delay and idx > 0) ::IO.select(nil, nil, nil, @_send_delay) end pkt = buf[idx, len] res = super(pkt, opts) flush() idx += len ret += res if res end ret end end ### # # This module provides methods for establish a connection to a remote host and # communicating with it. # ### module Exploit::Remote::Tcp # # Initializes an instance of an exploit module that exploits a # vulnerability in a TCP server. # def initialize(info = {}) super register_options( [ Opt::RHOST, Opt::RPORT ], Msf::Exploit::Remote::Tcp ) register_advanced_options( [ OptBool.new('SSL', [ false, 'Negotiate SSL for outgoing connections', false]), OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'SSL3', ['SSL2', 'SSL3', 'TLS1']]), Opt::Proxies, Opt::CPORT, Opt::CHOST, OptInt.new('ConnectTimeout', [ true, 'Maximum number of seconds to establish a TCP connection', 10]) ], Msf::Exploit::Remote::Tcp ) register_evasion_options( [ OptInt.new('TCP::max_send_size', [false, 'Maxiumum tcp segment size. (0 = disable)', 0]), OptInt.new('TCP::send_delay', [false, 'Delays inserted before every send. (0 = disable)', 0]) ], Msf::Exploit::Remote::Tcp ) end # # Establishes a TCP connection to the specified RHOST/RPORT # def connect(global = true, opts={}) dossl = false if(opts.has_key?('SSL')) dossl = opts['SSL'] else dossl = ssl if (datastore.default?('SSL') and rport.to_i == 443) dossl = true end end nsock = Rex::Socket::Tcp.create( 'PeerHost' => opts['RHOST'] || rhost, 'PeerPort' => (opts['RPORT'] || rport).to_i, 'LocalHost' => opts['CHOST'] || chost || "0.0.0.0", 'LocalPort' => (opts['CPORT'] || cport || 0).to_i, 'SSL' => dossl, 'SSLVersion'=> opts['SSLVersion'] || ssl_version, 'Proxies' => proxies, 'Timeout' => (opts['ConnectTimeout'] || connect_timeout || 10).to_i, 'Context' => { 'Msf' => framework, 'MsfExploit' => self, }) # enable evasions on this socket set_tcp_evasions(nsock) # Set this socket to the global socket as necessary self.sock = nsock if (global) # Add this socket to the list of sockets created by this exploit add_socket(nsock) return nsock end # Enable evasions on a given client def set_tcp_evasions(socket) if( datastore['TCP::max_send_size'].to_i == 0 and datastore['TCP::send_delay'].to_i == 0) return end return if socket.respond_to?('evasive') socket.extend(EvasiveTCP) if ( datastore['TCP::max_send_size'].to_i > 0) socket._send_size = datastore['TCP::max_send_size'] socket.denagle socket.evasive = true end if ( datastore['TCP::send_delay'].to_i > 0) socket._send_delay = datastore['TCP::send_delay'] socket.evasive = true end end def handler(nsock = self.sock) # If the handler claims the socket, then we don't want it to get closed # during cleanup if ((rv = super) == Handler::Claimed) if (nsock == self.sock) self.sock = nil end # Remove this socket from the list of sockets so that it will not be # aborted. remove_socket(nsock) end return rv end # # Closes the TCP connection # def disconnect(nsock = self.sock) begin if (nsock) nsock.shutdown nsock.close end rescue IOError end if (nsock == sock) self.sock = nil end # Remove this socket from the list of sockets created by this exploit remove_socket(nsock) end # # Performs cleanup, disconnects the socket if necessary # def cleanup super disconnect end ## # # Wrappers for getters # ## # # Returns the target host # def rhost datastore['RHOST'] end # # Returns the remote port # def rport datastore['RPORT'] end # # Returns the local host # def lhost datastore['LHOST'] end # # Returns the local port # def lport datastore['LPORT'] end # # Returns the local host for outgoing connections # def chost datastore['CHOST'] end # # Returns the local port for outgoing connections # def cport datastore['CPORT'] end # # Returns the boolean indicating SSL # def ssl datastore['SSL'] end # # Returns the string indicating SSLVersion # def ssl_version datastore['SSLVersion'] end # # Returns the proxy configuration # def proxies datastore['Proxies'] end # # Returns the TCP connection timeout # def connect_timeout datastore['ConnectTimeout'] end protected attr_accessor :sock end ### # # This mixin provides a generic interface for running a TCP server of some # sort that is designed to exploit clients. Exploits that include this mixin # automatically take a passive stance. # ### module Exploit::Remote::TcpServer def initialize(info = {}) super(update_info(info, 'Stance' => Msf::Exploit::Stance::Passive)) register_options( [ OptBool.new('SSL', [ false, 'Negotiate SSL for incoming connections', false]), OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'SSL3', ['SSL2', 'SSL3', 'TLS1']]), OptPath.new('SSLCert', [ false, 'Path to a custom SSL certificate (default is randomly generated)']), OptAddress.new('SRVHOST', [ true, "The local host to listen on. This must be an address on the local machine or 0.0.0.0", '0.0.0.0' ]), OptPort.new('SRVPORT', [ true, "The local port to listen on.", 8080 ]), ], Msf::Exploit::Remote::TcpServer) register_advanced_options( [ OptString.new('ListenerComm', [ false, 'The specific communication channel to use for this service']), ], Msf::Exploit::Remote::TcpServer) register_evasion_options( [ OptInt.new('TCP::max_send_size', [false, 'Maximum tcp segment size. (0 = disable)', 0]), OptInt.new('TCP::send_delay', [false, 'Delays inserted before every send. (0 = disable)', 0]) ], Msf::Exploit::Remote::Tcp ) end # # This mixin overrides the exploit method so that it can initiate the # service that corresponds with what the client has requested. # def exploit start_service() print_status("Server started.") # Call the exploit primer primer # Wait on the service to stop self.service.wait end # # Primer method to call after starting service but before handling connections # def primer end # # Stops the service, if one was created. # def cleanup super if(service) stop_service() print_status("Server stopped.") end end # # Called when a client connects. # def on_client_connect(client) end # # Called when a client has data available for reading. # def on_client_data(client) end # # Called when a client has disconnected. # def on_client_close(client) end # # Starts the service. # def start_service(*args) begin comm = datastore['ListenerComm'] if comm == "local" comm = ::Rex::Socket::Comm::Local else comm = nil end self.service = Rex::Socket::TcpServer.create( 'LocalHost' => srvhost, 'LocalPort' => srvport, 'SSL' => ssl, 'SSLCert' => ssl_cert, 'Comm' => comm, 'Context' => { 'Msf' => framework, 'MsfExploit' => self, }) self.service.on_client_connect_proc = Proc.new { |client| on_client_connect(client) } self.service.on_client_data_proc = Proc.new { |client| on_client_data(client) } self.service.on_client_close_proc = Proc.new { |client| on_client_close(client) } # Start the listening service self.service.start rescue ::Errno::EACCES => e if (srvport.to_i < 1024) print_line(" ") print_error("Could not start the TCP server: #{e}.") print_error( "This module is configured to use a privileged TCP port (#{srvport}). " + "On Unix systems, only the root user account is allowed to bind to privileged ports." + "Please run the framework as root to use this module." ) print_error( "On Microsoft Windows systems, this error is returned when a process attempts to "+ "listen on a host/port combination that is already in use. For example, Windows XP "+ "will return this error if a process attempts to bind() over the system SMB/NetBIOS services." ) print_line(" ") end raise e end end # # Stops the service. # def stop_service if (service) begin self.service.deref if self.service.kind_of?(Rex::Service) if self.service.kind_of?(Rex::Socket) self.service.close self.service.stop end self.service = nil rescue ::Exception end end end # # Returns the local host that is being listened on. # def srvhost datastore['SRVHOST'] end # # Returns the local port that is being listened on. # def srvport datastore['SRVPORT'] end # # Returns the SSL option # def ssl datastore['SSL'] end # # Returns the SSLCert option # def ssl_cert datastore['SSLCert'] end # # Re-generates the payload, substituting the current RHOST and RPORT with # the supplied client host and port from the socket. # def regenerate_payload(cli, arch = nil, platform = nil, target = nil) ohost = datastore['RHOST'] oport = datastore['RPORT'] p = nil begin # Update the datastore with the supplied client peerhost/peerport datastore['RHOST'] = cli.peerhost datastore['RPORT'] = cli.peerport if ((p = super(arch, platform, target)) == nil) print_error("Failed to generate payload") return nil end # Allow the payload to start a new handler add_handler({ 'RHOST' => datastore['RHOST'], 'RPORT' => datastore['RPORT'] }) ensure datastore['RHOST'] = ohost datastore['RPORT'] = oport end p end protected attr_accessor :service # :nodoc: end end