#!/usr/bin/perl
# SSL Man-In-The-Middle v0.1. Copyright (C) Vlatko Kosturjak, Kost
# Distributed under GPL v2+.
use strict;
use POSIX;
use IO::Socket::SSL qw(debug3);
use Getopt::Long;
use Time::HiRes qw(time);
my $debug = 0;
my $laddr = "127.0.0.1";
my $lport = 8080;
my $raddr = "127.0.0.1";
my $rport = 80;
my $logdir;
my $help;
my $daemon;
my $buffersize = 2048;
my $logtype;
my $daemon;
my $serverkey;
my $servercert;
my $serverdh;
$| = 1;
my $goresult = GetOptions(
"lport=i" => \$lport,
"laddr=s" => \$laddr,
"rport=i" => \$rport,
"raddr=s" => \$raddr,
"logtype=i" => \$logtype,
"logdir=s" => \$logdir,
"daemon" => \$daemon,
"serverkey=s" => \$serverkey,
"servercert=s" => \$servercert,
"serverdh=s" => \$serverdh,
"help" => \$help,
'debug!' => \$debug,
);
if ($help) {
print <<"END";
SSL Man-In-The-Middle v0.1. Copyright (C) Vlatko Kosturjak, Kost
Distributed under GPL v2+.
Usage: $0 [OPTIONS]
--lport <port> Listening port (default 80)
--laddr <address> Listening address (default localhost)
--rport <port> Remote port to connect to (default 8080)
--raddr <address> Remote address to connect to (default localhost)
--serverkey <file> Certificate key file for local SSL server
--servercert <file> Certificate file for local SSL server
--serverdh <file> Diffie-Helman file for key exchange
--log <type> Type of log where 0 is no log (default 0)
--logdir Directory to log to (default .)
--daemon Daemonize (work in background)
--help Display this help message
END
exit;
}
$Net::SSLeay::trace = 4 if $debug;
$logdir ||= "$laddr:$lport-$raddr:$rport";
$serverkey ||= "$logdir/ssl.key";
$servercert ||= "$logdir/ssl.cert";
mkdir $logdir;
system "openssl req -new -x509 -days 365 -nodes -out $servercert -keyout $serverkey"
if ! -e $serverkey && ! -e $servercert;
if ($daemon) {
my $pid = fork;
exit if $pid;
die "$!" unless defined($pid);
POSIX::setsid() or die "$!";
}
my $ah = IO::Socket::SSL->new(
'LocalPort' => $lport,
'LocalAddr' => $laddr,
'Reuse' => 1,
'Proto' => 'tcp',
'SSL_verify_mode' => '0',
'SSLdhfile' => $serverdh,
'SSL_cert_file' => $servercert,
'SSL_key_file' => $serverkey,
'Listen' => 10,
# 'SSL_version' => 'SSLv3', # SSLv3, SSLv2, TLSv1
# 'SSL_cipher_list' => 'RC4-MD5',
) || die "$!";
$SIG{'CHLD'} = 'IGNORE';
my $num = 0;
while (1) {
my $ch = $ah->accept();
if ( !$ch ) {
print STDERR "cannot accept: $! ", IO::Socket::SSL::errstr(),
"\n";
next;
}
if ( !$ch ) { print STDERR "cannot accept: $!\n"; next; }
++$num;
my $pid = fork();
if ( !defined($pid) ) { print STDERR "cannot fork while(1) $!\n"; }
elsif ( $pid == 0 ) {
$ah->close( SSL_no_shutdown => 1 );
Run( $ch, $num );
} else {
$ch->close( SSL_no_shutdown => 1 );
}
}
sub hexdump {
my $bytes = shift;
my $hex = unpack('H*', $bytes);
$hex =~ s/(.{8})/$1 /g;
return $hex;
}
sub Run {
my ( $ch, $num ) = @_;
my $th = IO::Socket::SSL->new(
'PeerAddr' => $raddr,
'PeerPort' => $rport,
# 'SSL_use_cert' => '0',
# 'SSL_verify_mode' => '0',
'SSL_version' => 'SSLv3', # SSLv3, SSLv2, TLSv1
'SSL_cipher_list' => 'RC4-MD5',
'Proto' => 'tcp'
);
if ( !$th ) { print "cannot connect $raddr:$rport th: $!"; exit 0; }
else { print "connected to $raddr:$rport\n"; }
my $fh;
if ( -d $logdir ) {
$fh = Symbol::gensym();
my $path = sprintf("%s/%15.5f", $logdir, Time::HiRes::time() );
open( $fh, '>', $path ) or die "$!";
}
$ch->autoflush();
$th->autoflush();
my $httpheader = "";
my $httpbuf = "";
while ( $ch || $th ) {
my $rin = "";
vec( $rin, fileno($ch), 1 ) = 1 if $ch;
vec( $rin, fileno($th), 1 ) = 1 if $th;
my ( $rout, $eout );
select( $rout = $rin, undef, $eout = $rin, 120 );
if ( !$rout && !$eout ) { }
my $cbuffer = "";
my $tbuffer = "";
if ($ch
&& ( vec( $eout, fileno($ch), 1 )
|| vec( $rout, fileno($ch), 1 ) )
)
{
my $result = sysread( $ch, $tbuffer, $buffersize );
if ( !defined($result) ) {
print STDERR "$!\n";
exit 0;
}
if ( $result == 0 ) { exit 0; }
}
if ($th
&& ( vec( $eout, fileno($th), 1 )
|| vec( $rout, fileno($th), 1 ) )
)
{
my $result = sysread( $th, $cbuffer, $buffersize );
if ( !defined($result) ) { print STDERR "$!\n"; exit 0; }
if ( $result == 0 ) { exit 0; }
}
if ( $fh && $tbuffer ) {
print $fh "\n# <<< client\n$tbuffer";
warn "C>S ", hexdump($tbuffer), "\n";
}
while ( my $len = length($tbuffer) ) {
my $res = syswrite( $th, $tbuffer, $len );
if ( $res > 0 ) { $tbuffer = substr( $tbuffer, $res ); }
else { print STDERR "$!\n"; }
}
if ( $fh && $cbuffer ) {
print $fh "\n# >>> server\n$cbuffer";
warn "S>C ", hexdump($cbuffer), "\n";
}
while ( my $len = length($cbuffer) ) {
my $res = syswrite( $ch, $cbuffer, $len );
if ( $res > 0 ) { $cbuffer = substr( $cbuffer, $res ); }
else { print STDERR "$!\n"; }
}
}
}