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