TCP Proxy for MITM Attacks in Metasploit

Some time ago I wrote my first metasploit module and therefore had to play around with ruby. The metasploit module I wrote implements a man-in-the-middle attack on an application layer protocol. So my module is both TCP Server and Client and therefore I like to call it TCP Proxy.

Coming from a python background ruby was kind of strange at the beginning. I still don't know what to think of ruby. Some things are pretty cool, while other just seem weird. Anyway that's not the topic today.

Implementing such a TCP Proxy with the mixins, which the metasploit framework provides, is quite easy... If you know your way around ruby, which I don't. Anyway here's the code so that you can see for yourself how I managed to get this to run. I used the Tcp and the TcpServer server mixins. The TcpServer mixin opens a listen port. When a client connects, I let the Tcp mixin connect to the real server and receive data from the client, send it to the server and then back. In the middle I can inspect or modify the data.

# Copyright (c) 2012, Michael Rodler (contact@f0rki.at)
#
#   Redistribution and use in source and binary forms, with or without
#   modification, are permitted provided that the following conditions are met:
#
#   1. Redistributions of source code must retain the above copyright notice, this
#      list of conditions and the following disclaimer.
#
#   2. Redistributions in binary form must reproduce the above copyright notice,
#      this list of conditions and the following disclaimer in the documentation
#      and/or other materials provided with the distribution.
#
#       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
#       AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#       IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#       ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
#       LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
#       CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
#       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
#       INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
#       CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
#       ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#       POSSIBILITY OF SUCH DAMAGE.


require 'msf/core'

class Metasploit3 < Msf::Auxiliary

        # mixin Tcp
        include Msf::Exploit::Remote::Tcp
        # create alias methods
        alias_method :cleanup_tcp, :cleanup
        alias_method :run_tcp, :run
        # mixin TcpServer
        include Msf::Exploit::Remote::TcpServer
        # create alias methods
        alias_method :cleanup_tcpserver, :cleanup
        alias_method :run_tcpserver, :run
        alias_method :exploit_tcpserver, :exploit


        def initialize(info = {})
                super(update_info(info,))

                # in my case I didn't need this SSL stuff
                deregister_options('SSL', 'SSLCert', 'SSLVersion', 'RPORT')

                register_options(
                        [
                                OptPort.new('SRVPORT', [ true, "", 0 ]),
                                OptString.new('SRVHOST', [ true, "Local listen address", "0.0.0.0" ]),
                                OptString.new('RHOST', [ true, "", "0.0.0.0" ]),
                        ], self.class)

                #
                datastore["RPORT"] = datastore["SRVPORT"]
        end


        # run tcp server, i.e. start listening port
        def run
                exploit_tcpserver
        end
        alias_method :exploit, :run

        # cleanup method, which calls both Tcp and TcpServer cleanup
        def cleanup
                cleanup_tcp()
                cleanup_tcpserver()
        end

        # client connected, so we let the Tcp mixin connect
        def on_client_connect(client)
                print_status("client connected " + client.peerinfo())
                connect()
        end

        # client disconnected, so we let the Tcp mixin disconnect
        def on_client_close(client)
                print_status("client disconnected " + client.peerinfo())
                disconnect()
        end

        def on_client_data(client)
                begin
                        # receive from client
                        data = client.get_once()
                        return if data.nil? or data.length == 0

                        ### do something evil with the tcp data here

                        # send data to server
                        sock.send(data, 0)
                        # receive data from server
                        respdata = sock.get_once()
                        return if respdata.nil? or respdata.length == 0

                        ### do something evil with the tcp data here

                        # send data back to client
                        client.put(respdata)
                rescue ::EOFError, ::Errno::EACCES, ::Errno::ECONNABORTED, ::Errno::ECONNRESET
                rescue ::Exception
                        print_status("Error: #{$!.class} #{$!} #{$!.backtrace}")
                end
        end


end

src

But assuming that we do a mitm attack (like arpspoofing in my case) we get a packet which is not directed at one of our own IP addresses. We can circumvent this by either configuring the IP address on our system or use the firewall to redirect the packet to our own machine:

$ iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 80

If you know how to improve this PLEASE contect me via mail/twitter/whatever. If you are the metasploit/ruby guru and still can't think of a better way, also let me know ;).