Link Search Menu Expand Document

How to use the Msf::Exploit::Remote::Tcp mixin

In Metasploit Framework, TCP sockets are implemented as Rex::Socket::Tcp, which extends the built-in Ruby Socket base class. You should always use the Rex socket instead of the native Ruby one because if not, your sockets are not manageable by the framework itself, and of course some features will be missing such as pivoting. The Developer’s Guide in Metasploit’s documentation directory explains how this works pretty well.

For module development, normally you wouldn’t be using Rex directly, so instead you’d be using the Msf::Exploit::Remote::Tcp mixin. The mixin already provides some useful features you don’t really have to worry about during development, such as TCP evasions, proxies, SSL, etc. All you have to do is make that connection, send something, receive something, and you’re done.

Sounds pretty easy, right?

Using the mixin

To use the mixin, simply add the following statement within your module’s class Metasploit3 (or class Metasploit4) scope:

include Msf::Exploit::Remote::Tcp

When the mixin is included, notice there will be the following datastore options registered under your module:

  • SSL - Negotiate SSL for outgoing connections.
  • SSLVersion - The SSL version used: SSL2, SSL3, TLS1. Default is TLS1.
  • SSLVerifyMode - Verification mode: CLIENT_ONCE, FAIL_IF_NO_PEER_CERT, NONE, PEER. Default is PEER.
  • Proxies - Allows your module to support proxies.
  • ConnectTimeout - Default is 10 seconds.
  • TCP::max_send_size - Evasive option. Maximum TCP segment size.
  • TCP::send_delay - Evasive option. Delays inserted before every send.

If you wish to learn how to change the default value of a datastore option, please read “Changing the default value for a datastore option

Make a connection

To make a connection, simply do the following:

connect

When you do this, what happens is that the connect method will call Rex::Socket::Tcp.create to create the socket, and register it to framework. It automatically checks with the RHOST/RPORT datastore options (so it knows where to connect to), but you can also manually change this:

# This connects to metasploit.com
connect(true, {'RHOST'=>'208.118.237.137', 'RPORT'=>80})

The connect method will then return the Socket object, which is also accessible globally.

But you see, there’s a little more to it. The connect method can also raise some Rex exceptions that you might want to catch, including:

  • Rex::AddressInUse - Possible when it actually binds to the same IP/port.
  • ::Errno::ETIMEDOUT - When Timeout.timeout() waits to long to connect.
  • Rex::HostUnreachable - Pretty self-explanatory.
  • Rex::ConnectionTimeout - Pretty self-explanatory.
  • Rex::ConnectionRefused - Pretty self-explanatory.

So to sum it up, ideally when you use the connect method, you should rescue these:

rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused

If you are curious where all these exceptions are raised, you can find them in rex/socket/comm/local.rb.

Sending data

There are several ways to send data with the Tcp mixin. To make things easier and safer, we recommend just use the put method:

sock.put "Hello, World!"

The reason the put method is safer is because it does not allow the routine to hang forever. By default, it doesn’t wait, but if you want to make this more flexible, you can do this:

begin
	sock.put("data", {'Timeout'=>5})
rescue ::Timeout::Error
	# You can decide what to do if the writing times out
end

Receiving data

Now, let’s talk about how to receive data. Mainly there are three methods you can use: get_once, get, and timed_read. The difference is that get_once will only try to poll the stream to see if there’s any read data available one time, but the get method will keep reading until there is no more. As for timed_read, it’s basically the read method wrapped around with a Timeout.

The following demonstrates how get_once is used:

begin
	buf = sock.get_once
rescue ::EOFError
end

Note that get_once may also return nil if there is no data read, or it hits a EOFError if it receives nil as data. So please make sure you’re catching nil in your module.

The data reading methods can be found in lib/rex/io/stream.rb.

Disconnecting

To disconnect the connection, simply do:

disconnect

It is VERY important you disconnect in an ensure block, obviously to make sure you always disconnect if something goes wrong. If you don’t do this, you may end up with a module that can only one request to the server (that very first one), and the rest are broken.

Full example

The following example should demonstrate how you would typically want to use the Tcp mixin:

# Sends data to the remote machine
#
# @param data [String] The data to send
# @return [String] The received data
def send_recv_once(data)
  buf = ''
  begin
    connect
    sock.put(data)
    buf = sock.get_once || ''
  rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
    elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
  ensure
    disconnect
  end

  buf
end