# proxy.pl # honeyclient proxy engine # Copyright (c) 2004-2005 Kathy Wang # proxy.pl opens two sockets, one of which is listening for IE requests, # and the other is receiving traffic from the remote webserver. proxy.pl # does not do anything beyond parse out URLs from the remote server and # save those URLs as external and internal URLs in hash tables. # proxy.pl and driver.pl communicate with each other through files, namely # domainfile. #!/usr/bin/perl use strict; use Socket; use IO::Select; use IO::Socket; use Net::hostent; use HTML::LinkExtor; use Carp; my $intfile = "intfile"; my $extfile = "extfile"; my $domainfile = "domainfile"; my $donefile = "donefile"; my $proxydonefile = "proxydone"; my $timeout = 10; my @intlinks = (); my @extlinks = (); # my $remote_addr = shift || 'localhost'; my $remote_port = shift || 6768; my $local_port = 6768; my $proto = getprotobyname('tcp'); my $local_iaddr = INADDR_ANY; my $local_paddr = sockaddr_in($local_port, $local_iaddr); my $remote_paddr; my $baseurl; my $done = 0; # print "Heading into loooooooooop\n"; while(!$done) { # If domainfile is populated with URL, then open listening and remote sockets. # Listening socket listens for traffic from IE, and remote socket listens for # traffic from remote webserver. if (-f $domainfile) { open DOMAIN, "$domainfile" or die "Could not open $domainfile: $!\n"; my $remote = ; chomp $remote; close(DOMAIN); unlink $domainfile; my $remote_iaddr = inet_aton($remote); if($remote_iaddr) { $remote_paddr = sockaddr_in($remote_port, $remote_iaddr); my $server = IO::Socket::INET->new( Proto => 'tcp', LocalPort => $local_port, Listen => SOMAXCONN, Reuse => 1); $baseurl = $remote; my $local_socket = $server->accept(); proxyrun($local_socket); close($local_socket); } else { print "Proxy did not detect address for $remote\n"; open INTFILE, ">$intfile" or die "Cannot open $intfile: $!\n"; print INTFILE "\n"; close INTFILE; open EXTFILE, ">$extfile" or die "Cannot open $extfile: $!\n"; print EXTFILE "\n"; close EXTFILE; open PROXYDONE, ">$proxydonefile" or die "Cannot open $proxydonefile: $!\n"; print PROXYDONE "Done!\n"; close PROXYDONE; } } if(-f $donefile) { $done = 1; } # Process sleeps for one second before checking whether domainfile is # populated. If not, then sleep for another second, then check again, etc sleep(1); } # This subroutine writes all internal URLs to intfile, and all external # links to extfile when proxy times out, which is currently set to 5 secs. sub proxycomplete { my $self = shift; # print "Completing!\n"; open INTFILE, ">$intfile" or die "Cannot open $intfile: $!\n"; foreach my $value (@intlinks) { print INTFILE "$value\n"; # print "INTFILE $value\n"; } print INTFILE "\n"; close INTFILE; open EXTFILE, ">$extfile" or die "Cannot open $extfile: $!\n"; foreach my $value (@extlinks) { print EXTFILE "$value\n"; # print "EXTFILE $value\n"; } print EXTFILE "\n"; close EXTFILE; open PROXYDONE, ">$proxydonefile" or die "Cannot open $proxydonefile: $!\n"; print PROXYDONE "Done!\n"; close PROXYDONE; return; } # This subroutine extracts URL information and puts the key (tag) and # value (tag values) into a hash table. Also, the URL is parsed to determine # whether it is an internal or external link. For example, if the URL starts # with 'http://' vs if the URL is internal (e.g. '/project/foo') sub linkextract { my($tag, %attr) = @_; return if $tag ne 'a'; # we only look closer at tags # print "LINKEXTRACT CALLED!\n"; # print "LINKS: $tag type\n"; # print values %attr; foreach my $attribute (values %attr) { if ($attribute =~/$baseurl/i) { push(@intlinks, $attribute); } elsif ($attribute =~ /http:\/\//i) { push @extlinks, $attribute; } elsif ($attribute =~/^\//) { push(@intlinks, $attribute); } elsif ($attribute =~/^[a-zA-Z0-9]/) { my $newatt = "/" . $attribute; push(@intlinks, $newatt); } } } sub proxyrun { my $local_socket = shift; my $done = 0; my $readmask = ""; my $readready = ""; my @ready_handles = []; my $localdone = 0; my $remotedone = 0; my $readbuf; my $readpos = 0; my $blksize = 4096; my $lasttraffic = time(); vec($readmask, fileno(Local_Sock), 1) = 1; socket(Remote_Sock, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; connect(Remote_Sock, $remote_paddr) or die "connect: $!"; my $select = IO::Select->new(); my $linkextor = HTML::LinkExtor->new(\&linkextract); # $linkextor->end = \&proxycomplete; $select->add(\*Remote_Sock); $select->add($local_socket); while(!$done) { @ready_handles = $select->can_read(1); foreach my $handle(@ready_handles) { if ($handle == \*Remote_Sock) { $lasttraffic = time(); my $len = sysread(Remote_Sock, $readbuf, $blksize); if($len == 0) { $remotedone = 1; } else { # print "Back: ". $readbuf; $linkextor->parse($readbuf); syswrite($local_socket, $readbuf, $blksize); if($readbuf =~ /<\/html>/i) { # print "Got the end\n"; $done = 1; } } } elsif ($handle == $local_socket) { $lasttraffic = time(); my $len = sysread($local_socket, $readbuf, $blksize); if($len == 0) { $localdone = 1; } else { # print "Out: " . $readbuf; syswrite(Remote_Sock, $readbuf, $blksize); } } else { die "Unknown handle returned by select\n"; } } if($localdone && $remotedone) { # print "Local and Remote Done"; $done = 1; } my $currenttime = time(); my $differential = $currenttime - $lasttraffic; if($differential > $timeout) { # print "Timed out: $lasttraffic $currenttime $differential\n"; $done = 1; } else { } } $linkextor->eof(); proxycomplete(); }