Ben Aveling > NET-MitM > NET::MitM

Download:
NET-MitM-0.02.tar.gz

Dependencies

Annotate this POD

View/Report Bugs
Module Version: 0.02   Source  

NAME ^

NET::MitM - Man in the Middle - connects a client and a server, giving visibility of and control over messages passed.

VERSION ^

Version 0.02

SYNOPSIS ^

NET::MitM is designed to be inserted between a client and a server. It proxies all traffic through verbatum, and also copies that same data to a log file and/or a callback function, allowing a data session to be monitored, recorded, even altered on the fly.

MitM acts as a 'man in the middle', sitting between the client and server. To the client, MitM looks like the server. To the server, MitM looks like the client.

MitM cannot be used to covertly operate on unsuspecting client/server sessions - it requires that you control either the client or the server. If you control the client, you can tell it to connect via your MitM. If you control the server, you can move it to a different port, and put a MitM in its place.

When started, MitM opens a socket and listens for connections. When that socket is connected to, MitM opens another connection to the server. Messages from either client or server are passed to the other, and a copy of each message is, potentially, logged. Alternately, callback methods may be used to add business logic, including potentially altering the messages being passed.

MitM can also be used as a proxy to allow two processes on machines that cannot 'see' each other to communicate via an intermediary machine that is visible to both.

There is an (as yet unreleased) sister module NET::Replay that allows a MitM session to be replayed.

Usage

Assume the following script is running on the local machine:

    use NET::MitM;
    my $MitM = NET::MitM->new("cpan.org", 80, 10080);
    $MitM->log_file("MitM.log");
    $MitM->go();

A browser connecting to http://localhost:10080 will now cause MitM to open a connection to cpan.org, and messages sent by either end will be passed to the other end, and logged to MitM.log.

For another example, see samples/mitm.pl in the MitM distribution.

Modifying messages on the fly.

However you deploy MitM, it will be virtually identical to having the client and server talk directly. The difference will be that either the client and/or server will be at an address other than the one its counterpart believes it to be at. Most programs ignore this, but sometimes it matters.

For example, HTTP browsers pass a number of parameters, one of which is "Host", the host to which the browser believes it is connecting. Often, this parameter is unused. But sometimes, a single HTTP server will be serving content for more than one website. Such a server generally relies on the Host parameter to know what it is to return. If the MitM is not on the same host as the HTTP server, the host parameter that the browser passes will cause the HTTP server to fail to serve the desired pages.

Further, HTTP servers typically return URLs containing the host address. If the browser navigates to a returned URL, it will from that point onwards connect directly to the server in the URL instead of communicating via MitM.

Both of these problems can be worked around by modifying the messages being passed.

For example, assume the following script is running on the local machine:

    use NET::MitM;
    sub send_($) {$_[0] =~ s/Host: .*:\d+/Host: cpan.org/;}
    sub receive($) {$_[0] =~ s/cpan.org:\d+/localhost:10080/g;}
    my $MitM = NET::MitM->new("cpan.org", 80, 10080);
    $MitM->client_to_server_callback(\&send);
    $MitM->server_to_client_callback(\&receive);
    $MitM->log_file("http_MitM.log");
    $MitM->go();

The send callback tells the server that it is to serve cpan.org pages, instead of some other set of pages, while the receive callback tells the browser to access cpan.org URLs via the MitM process, not directly. The HTTP server will now respond properly, even though the browser sent the wrong hostname, and the browser will now behave as desired and direct future requests via the MitM.

For another example, see samples/http_mitm.pl in the MitM distribution.

A more difficult problem is security aware processes, such as those that use HTTPS based protocols. They are actively hostname aware. Precisely to defend against a man-in-the-middle attack, they check their counterpart's reported hostname (but not normally the port) against the actual hostname. Unless client, server and MitM are all on the same host, either the client or the server will notice that the remote hostname is not what it should be, and will abort the connection. There is no good workaround for this, unless you can run an instance of MitM on the server, and another on the client - but even if you do, you still have to deal with the communication being encrypted.

SUBROUTINES/METHODS ^

new( remote_ip_address, local_port_num, remote_port_num )

Creates a new MitM

Parameters

Usage

To keep a record of all messages sent:

    use NET::MitM;
    my $MitM = NET::MitM->new("www.cpan.org", 80, 10080);
    $MitM->log_file("MitM.log");
    $MitM->go();

go( )

Listen on local_port, accept incoming connections, and forwards messages back and forth.

Parameters

Usage

When a connection on local_port is received a connect to remote_ip_address:remote_port is created and messages from the client are passed to the server and vice-versa.

If parallel() was set, which is not the default, there will be a new process created for each such session.

If any callback functions have been set, they will be called before each message is passed. If logging is on, messages will be logged.

go() does not return. You may want to fork before calling it. There is no way to stop it from outside except using a signal to interrupt it. This will probably change in a future release of MitM.

If new_server() was used instead of new(), messages from client are instead passed to the server callback function.

name( [name] )

Names the object - will be reported back in logging/debug

Parameters

Usage

For a minimal MitM:

    use NET::MitM;
    my $MitM = NET::MitM->new("www.cpan.org", 80, 10080);
    $MitM->go();

verbose( [level] )

Turns on/off reporting to stdout.

Parameters

Usage

Setting verbose changes the amount of information printed to stdout.

client_to_server_callback( callback )

Set a callback function to monitor/modify each message sent to server

Parameters

Usage

If client_to_server_callback is set, it will be called with a copy of each message to the server before it is sent. Whatever the callback returns will be sent.

For example, to modify messages:

    use NET::MitM;
    sub send_($) {$_[0] =~ s/Host: .*:\d+/Host: cpan.org/;}
    sub receive($) {$_[0] =~ s/www.cpan.org(:\d+)?/localhost:10080/g;}
    my $MitM = NET::MitM->new("www.cpan.org", 80, 10080);
    $MitM->client_to_server_callback(\&send);
    $MitM->server_to_client_callback(\&receive);
    $MitM->go();

If the callback is readonly, it should either return a copy of the original message, or undef. Be careful not to accidentally return something else - remember that perl methods implicitly returns the value of the last command executed.

For example, to write messages to a log:

    sub peek($) {my $msg = shift; print LOG; return $msg;}
    my $MitM = NET::MitM->new("www.cpan.org", 80, 10080);
    $MitM->client_to_server_callback(\&peek);
    $MitM->server_to_client_callback(\&peek);
    $MitM->go();

This would also work: sub peek($) {my $msg = shift; print LOG; return undef;} ...

But this is unlikely to do what you would want: sub peek($) {my $msg = shift; print LOG} ...

server_to_client_callback( [callback] )

Set a callback function to monitor/modify each message received from server

Parameters

Usage

If server_to_client_callback is set, it will be called with a copy of each message received from the server before it is sent to the client. Whatever the callback returns will be sent.

If the callback is readonly, it should either return a copy of the original message, or undef. Be careful not to accidentally return something else - remember that perl methods implicitly returns the value of the last command executed.

timer_callback( [interval, callback] )

Set a callback function to be called at regular intervals

Parameters

Usage

If the callback is set, it will be called every interval seconds. Interval must be > 0 seconds. It may be fractional. If interval is passed as 0 it will be reset to 1 second. This is to prevent accidental spin-wait. If you really want to spin-wait, pass an extremely small but non-zero interval.

If the callback returns false, mainloop will exit and return control to the caller.

The time spent in callbacks is not additional to the specified interval - the timer callback will be called every interval seconds, or as close as possible to every interval seconds.

Please remember that if you have called fork before calling go() that the timer_callback method will be executed in a different process to the parent - the two processes will need to use some form of IPC to communicate.

parallel( [level] )

Turns on/off running in parallel.

Parameters

Usage

If running in parallel, MitM starts a new process for each new connection using fork.

Running in serial still allows multiple clients to run concurrently, as so long as none of them have long-running callbacks. If they do, they will block other clients from sending/recieving.

serial( [level] )

Turns on/off running in serial

Parameters

Usage

Calling this function with level=$l is exactly equivalent to calling parallel with level=!$l.

If running in parallel, MitM starts a new process for each new connection using fork.

Running in serial, which is the default, still allows multiple clients to run concurrently, as so long as none of them have long-running callbacks. If they do, they will block other clients from sending/recieving.

log_file( [log_file_name] ] )

log_file() sets, or clears, a log file.

Parameters

Usage

The log file contains a record of connects and disconnects and messages as sent back and forwards. Log entries are timestamped. If the log file already exists, it is appended to.

The default timestamp is "hh:mm:ss", eg 19:49:43 - see mydate() and hhmmss().

defrag_delay( [delay] )

Use a small delay to defragment messages

Parameters

Usage

Under TCPIP, there is always a risk that large messages will be fragmented in transit, and that messages sent close together may be concatenated.

Client/Server programs have to know how to turn a stream of bytes into the messages they care about, either by repeatedly reading until they see an end-of-message (defragmenting), or by splitting the bytes read into multiple messages (deconcatenating).

For our purposes, fragmentation and concatenation can make our logs harder to read.

Without knowning the protocol, it's not possible to tell for sure if a message has been fragmented or concatenated.

A small delay can be used as a way of defragmenting messages, although it increases the risk that separate messages may be concatenated.

Eg: $MitM->defrag_delay( 0.1 );

SUPPORTING SUBROUTINES/METHODS ^

The remaining functions are supplimentary. new_server() and new_client() provide a simple client and a simple server that may be useful in some circumstances. The other methods are only likely to be useful if you choose to bypass go() in order to, for example, have more control over messages being received and sent.

new_server( local_port_num, callback_function )

Returns a very simple server, adequate for simple tasks.

Parameters

Usage

  sub do_something($){
    my $in = shift;
    my $out = ...
    return $out;
  }

  my $server = NET::MitM::new_server(8080,\&do_something) || die;
  $server->go();

The server returned by new_server has a method, go(), which tells it to start receiving messages (arbitrary strings). Each string is passed to the callback_function, which is expected to return a single string, being the response to be returned to caller. If the callback returns undef, the original message will be echoed back to the client.

go() does not return. You may want to fork before calling it.

See, for another example, samples/echo_server.pl in the MitM distribution.

new_client( remote_host, local_port_num )

new client returns a very simple client, adequate for simple tasks

The server returned has a method, send_and_receive(), which sends a message and receives a response.

Alternately, send_to_server() may be used to send a message, and read_from_server() may be used to receive a message.

Explicitly calling connect_to_server() is optional, but may be useful if you want to be sure the server is reachable. If you don't call it explicitly, it will be called the first time a message is sent.

Parameters

Usage

  my $client = NET::MitM::new_client("localhost", 8080) || die("failed to start test client: $!");
  $client->connect_to_server();
  my $resp = $client->send_and_receive("hello");
  ...

See, for example, samples/client.pl or samples/clients.pl in the MitM distribution.

log( string )

log is a convenience function that prefixes output with a timestamp and PID information then writes to log_file.

Parameters

Usage

log is a convenience function that prefixes output with a timestamp and PID information then writes to log_file.

log() does nothing unless log_file is set, which by default, it is not.

echo( string(s) )

Prints to stdout and/or the logfile

Parameters

Usage

echo() is a convenience function that prefixes output with a timestamp and PID information and prints it to standard out if verbose is set and, if log_file() has been set, logs it to the log file.

send_to_server( string(s) )

send_to_server() sends a message to the server

Parameters

Usage

If a callback is set, it will be called before the message is sent.

send_to_server() may 'die' if it detects a failure to send.

send_to_client( string(s) )

Sends a message to the client

Parameters

Usage

If a callback is set, it will be called before the message is sent.

read_from_server( )

Reads a message from the server

Parameters

Usage

Blocks until a message is received.

send_and_receive( )

Sends a message to the server and receives a response

Parameters

Usage

Blocks until a message is received. If the server does not always return exactly one message for each message it receives, send_and_receive() will either concatenate messages or block forever.

connect_to_server( )

Connects to the server

Parameters

Usage

This method is automatically called when needed. It only needs to be called directly if you want to be sure that the connection to server succeeds before proceeding.

disconnect_from_server( )

Disconnects from the server

Parameters

Usage

Disconnection is normally triggered by the other party disconnecting, not by us. disconnect_from_server() is only useful with new_client(), and not otherwise supported.

hhmmss( )

The default timestamp function - returns localtime in hh:mm:ss format

Parameters

Usage

This function is, by default, called when a message is written to the log file.

It may be overridden by calling mydate().

mydate( )

Override the standard hh:mm:ss datestamp

Parameters

Usage

For example:

  sub yymmddhhmmss {
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
    return sprintf "%02d/%02d/%02d %02d:%02d:%02d", 
      $year+1900,$mon+1,$mday,$hour,$min,$sec;
  }
  mydate(\&yymmddhhmmss);

listen( )

Listen on local_port and prepare to accept incoming connections

Parameters

Usage

This method is called by go(). It only needs to be called directly if go() is being bypassed for some reason.

Exports ^

MitM does not export any functions or variables. If parallel() is turned on, which by default it is not, MitM sets SIGCHD to IGNORE, and as advertised, it calls fork() once for each new connection.

AUTHOR ^

Ben AVELING, <ben dot aveling at optusnet dot com dot au>

BUGS ^

Please report any bugs or feature requests to bug-NET-MitM at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=NET-MitM. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT ^

You can find documentation for this module with the perldoc command.

    perldoc NET::MitM

You can also look for information at:

ACKNOWLEDGEMENTS ^

I'd like to acknowledge W. Richard Steven's and his fantastic introduction to TCPIP: "TCP/IP Illustrated, Volume 1: The Protocols", Addison-Wesley, 1994. (http://www.kohala.com/start/tcpipiv1.html). It got me started. Recommend. RIP. The Blue Camel Book is also pretty useful, and Langworth & chromatic's "Perl Testing, A Developer's Notebook" is also worth a hat tip.

ALTERNATIVES ^

If what you want is a pure proxy, especially if you want an ssh proxy or support for firewalls, you might want to evaluate Philippe "BooK" Bruhat's Net::Proxy.

And if you want a full "portable multitasking and networking framework for any event loop", you may be looking for POE.

LICENSE AND COPYRIGHT ^

Copyleft 2013 Ben AVELING.

This program is free software; you can redistribute it and/or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at:

http://www.perlfoundation.org/artistic_license_2_0

Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license.

If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license.

This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder.

This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, have, hold and cherish, use, offer to use, sell, offer to sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed.

Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SO THERE.

syntax highlighting: