#!/usr/local/bin/perl =pod upgrade perl.. also some of these modules can be replaced by only a few lines of code.. also memory leaks are killing us.. =cut use warnings; # only enable warnings for this program and not for those buggy modules (perl -w) use strict; use threads; use threads::shared; use IO::Socket; use IO::Select; use Net::SOCKS; # probably replaceable use Thread::Semaphore; # maybe use NetAddr::IP; # probably easy use Carp; # prototypes # sub scan($@); sub testsocks($$); sub beginscan; sub syn_sent; sub monitor; sub address_server(@); sub getaddress; sub testaddress(@); sub shuffle(@); # test smtp server address # we try to connect to this server through any proxies we find # this server should have an smtp server listening on port 25 or we'll get false negatives # my $SMTPADDR = '66.249.132.126'; # number of worker threads # increasing this will increase the number of threads that we will spawn # each thread requires a considerable amount of memory # my $NUMTHREADS = 40; # concurrent connection attempts per thread # my $SIMULSCAN = 800; # tcp connect timeout in seconds # having this too low will increase the chances of getting false negatives # having this set high slows our scan # my $TCP_TIMEOUT = 3; # timeout for socks connection attempt # this should be set high or else we risk timing out before detecting a proxy (false negative) # we don't spend very much time testing socks anyway so it shouldn't be a problem # my $SOCKS_TIMEOUT = 10; # maximum number of SYN_SENT connections # my $MAXOPENPORTS = 2000; # maximum number of open sockets # my $MAXOPENSOCK = 4000; # tcp port number for our address server # my $ADDRSVRPORT = 10101; # number of addresses to ping simultaneously # my $ADDRBLOCKSIZE = 128; my @addr; # addresses to scan my $n; # NetAddr::IP object #my $spawnlock = new Thread::Semaphore; # lock newly spawned threads my $addrsvrlock = new Thread::Semaphore; # get exclusive access to the address server my $scanned : shared = 0; # scan count my $opensyn : shared = 0; # open syn_sent connections my $opensock : shared = 0; # open socket count my $tbs : shared = 0; # threads currently building sockets my $tbc : shared = 0; # threads currently building connections my $wso : shared = 0; my $wsy : shared = 0; my $op_c : shared = 0; # open ports count my $prx_c : shared = 0; # proxy count my $count : shared; # total number of addresses my @ports = shuffle(80, 1025..65535); # ports to scan select STDOUT; $|++; # unbuffer output select STDERR; $|++; # check arguments # unless(defined($ARGV[0])) { print STDERR "usage: $0 \n"; exit -1; } # build NetAddr::IP object and check for valid cidr block # eval { $n = NetAddr::IP->new($ARGV[0]); }; if($@) { print STDERR "$@"; print STDERR "invalid cidr block\n"; print STDERR "usage: $0 \n"; exit -1; } my @thread; # this list holds our thread objects my $thread_num; # this is the current thread's number (integer starting at 0) =pod $SIG{'ABRT'} = sub { print "caught signal " . 'ABRT' . "\n"; }; $SIG{'TRAP'} = sub { print "caught signal " . 'TRAP' . "\n"; }; $SIG{'HUP'} = sub { print "caught signal " . 'HUP' . "\n"; }; $SIG{'FPE'} = sub { print "caught signal " . 'FPE' . "\n"; }; $SIG{'EMT'} = sub { print "caught signal " . 'EMT' . "\n"; }; $SIG{'XCPU'} = sub { print "caught signal " . 'XCPU' . "\n"; }; $SIG{'TTOU'} = sub { print "caught signal " . 'TTOU' . "\n"; }; $SIG{'IOT'} = sub { print "caught signal " . 'IOT' . "\n"; }; $SIG{'IO'} = sub { print "caught signal " . 'IO' . "\n"; }; $SIG{'URG'} = sub { print "caught signal " . 'URG' . "\n"; }; $SIG{'STOP'} = sub { print "caught signal " . 'STOP' . "\n"; }; $SIG{'TTIN'} = sub { print "caught signal " . 'TTIN' . "\n"; }; $SIG{'PROF'} = sub { print "caught signal " . 'PROF' . "\n"; }; $SIG{'TSTP'} = sub { print "caught signal " . 'TSTP' . "\n"; }; $SIG{'XFSZ'} = sub { print "caught signal " . 'XFSZ' . "\n"; }; $SIG{'INT'} = sub { print "caught signal " . 'INT' . "\n"; }; $SIG{'SEGV'} = sub { print "$thread_num - caught signal " . 'SEGV' . "\n"; croak; }; $SIG{'QUIT'} = sub { print "caught signal " . 'QUIT' . "\n"; }; $SIG{'PIPE'} = sub { print "caught signal " . 'PIPE' . "\n"; }; $SIG{'USR2'} = sub { print "caught signal " . 'USR2' . "\n"; }; $SIG{'INFO'} = sub { print "caught signal " . 'INFO' . "\n"; }; $SIG{'SYS'} = sub { print "caught signal " . 'SYS' . "\n"; }; $SIG{'VTALRM'} = sub { print "caught signal " . 'VTALRM' . "\n"; }; $SIG{'CONT'} = sub { print "caught signal " . 'CONT' . "\n"; }; $SIG{'USR1'} = sub { print "caught signal " . 'USR1' . "\n"; }; $SIG{'WINCH'} = sub { print "caught signal " . 'WINCH' . "\n"; }; $SIG{'ILL'} = sub { print "caught signal " . 'ILL' . "\n"; }; #$SIG{'TERM'} = sub { print "caught signal " . 'TERM' . "\n"; exit; }; $SIG{'BUS'} = sub { print "caught signal " . 'BUS' . "\n"; }; #$SIG{'KILL'} = sub { print "caught signal " . 'KILL' . "\n"; }; #$SIG{'CHLD'} = sub { print "caught signal " . 'CHLD' . "\n"; }; =cut $SIG{'ALRM'} = sub { print "$thread_num - caught signal " . 'ALRM' . "\n"; }; $addrsvrlock->down; # wait for address server to become available # spawn our threads # for($thread_num = 0; $thread_num < $NUMTHREADS; $thread_num++) { $thread[$thread_num] = threads->create('beginscan', $thread_num); defined($thread[$thread_num]) or die "could not create thread: $!"; $thread[$thread_num]->detach; } sleep(1); print STDERR "shuffling addresses\n"; @addr = shuffle($n->hostenum()); $count = scalar(@addr); $count *= scalar(@ports); # spawn address server thread # my $address_thread = threads->create('address_server', @addr); defined($address_thread) or die "could not create thread: $!"; $address_thread->detach; $addrsvrlock->up; monitor; # address server thread # sub address_server(@) { my $server; my $client; print STDERR "spawned address server thread\n"; $server = IO::Socket::INET->new( Proto => 'tcp', LocalAddr => '127.0.0.1', LocalPort => $ADDRSVRPORT, Listen => 1, Type => SOCK_STREAM, ReuseAddr => 1, Blockin => 1 ) or die "socket: $!"; while(scalar(@_)) { my @block = splice(@_, 0, $ADDRBLOCKSIZE); $client = $server->accept(); $client->autoflush(1); for(@block) { print $client "$_\n"; } close($client); } } # monitor # sub monitor { my $starttime = time; my $numports = scalar(@ports); sleep(1); for(;;) { $opensyn = syn_sent(); # update opensyn regularly # my $c_scanned = sprintf("%07d", ($scanned / 1024)); # my $c_count = $count / 1024; # # $c_scanned =~ s/(\d+)(\d{3})(\d{3})$/$1,$2,$3/; # $c_count =~ s/(\d+)(\d{3})(\d{3})$/$1,$2,$3/; my $c_scanned = $scanned; my $c_count = $count; print STDERR "\n" . "so=" . sprintf("%04d", $opensock) . " sy=" . sprintf("%04d", $opensyn) . " tbs=" . sprintf("%02d", $tbs) . " tbc=" . sprintf("%02d", $tbc) . " wso=" . sprintf("%02d", $wso) . " wsy=" . sprintf("%02d", $wsy) . " op=" . sprintf("%02d", $op_c) . " prx=" . sprintf("%02d", $prx_c) . " - " . $c_scanned . "k of " . $c_count . "k - " . sprintf("%2.2f", (($scanned / (time - $starttime)) / $numports)) . "h/s - " . sprintf("%2.2f", ($scanned / $count) * 100) . "%\n\n"; sleep(5); } } # connect to address server and retrieve an address # sub getaddress { my $remote; $addrsvrlock->down; # one thread at a time { $remote = IO::Socket::INET->new( Proto => 'tcp', PeerAddr => '127.0.0.1', PeerPort => $ADDRSVRPORT) or do { print STDERR "$thread_num - socket: $!\n"; sleep(5); redo; }; } my $line; my @block; while(defined($line = <$remote>)) { chomp($line); $line =~ s/\/32*//; push(@block, $line); } close($remote); $addrsvrlock->up; return(@block); } # borrowed from List::Util # sub shuffle(@) { my @a=\(@_); my $n; my $i=@_; map { $n = rand($i--); (${$a[$n]}, $a[$n] = $a[$i])[0]; } @_; } # test reachability of list of addresses # sub testaddress(@) { my $testcmd = '/usr/local/sbin/fping -r1 -t2000 -i10 '; for(@_) { $testcmd .= "$_ "; } $testcmd .= '2>&1 | /usr/bin/grep "is alive$" | sed "s/ is alive//"'; my @block = readpipe($testcmd); for(@block) { chomp($_); } return(@block); } # begin the scanning process # sub beginscan { my @ports_tmp; my $total_ports = scalar(@ports); my @ports_block; my @open; my @block; print STDERR "spawned thread " . sprintf("%02d", $thread_num) . "\n"; ADDRESS: { @block = testaddress(getaddress()); my $addrinblock = scalar(@block); print STDERR sprintf("%02d", $thread_num) . "\t\t\t" . localtime(time) . "\tblock contains " . $addrinblock . " reachable " . (($addrinblock == 1) ? "host" : "hosts") . "\n"; $scanned += (($ADDRBLOCKSIZE - $addrinblock) * $total_ports); # count skipped hosts redo ADDRESS unless($addrinblock); threads->yield(); for my $address (@block) { print STDERR sprintf("%02d", $thread_num) . "\t\t\t" . localtime(time) . "\tscanning $address\n"; my $scan_count = 0; # ports scanned for this target my $starttime = time; @ports_tmp = @ports; PORTS: { threads->yield(); # grab a "small" block to simul-scan # @ports_block = splice(@ports_tmp, 0, $SIMULSCAN); my $numports = scalar(@ports_block); my $portsleft = scalar(@ports_tmp); if($numports) { $scan_count += $numports; # simul-scan our block # @open = scan($address, @ports_block); # exit if there was a fatal error # defined($open[0]) and ($open[0] == -1) and return; # test socks on all open ports # for(@open) { testsocks($address, $_); } $scanned += $numports; redo PORTS; # continue until we've tested all ports } } print STDERR sprintf("%02d", $thread_num) . " finished scanning $address in " . (time - $starttime) . " seconds\t" . ($scan_count / (time - $starttime)) . "p/s\n"; } redo ADDRESS; } } # return number tcp sockets in SYN_SENT according to netstat # sub syn_sent { my $op = `/usr/bin/netstat -n | /usr/bin/grep SYN_SENT | /usr/bin/wc -l`; $op =~ s/\s//g; return $op; } # perform a seriously brutal port scan # sub scan($@) { my ($victim, @ports) = @_; my $sel = new IO::Select(); # select object my @sock; # list of sockets my @open; # list of open ports my $starttime = time; my $numports = scalar(@ports); # # this is crude.. # # wait for open socket count to drop # if(($opensock + (($numports * ($tbs + 1)) / 2)) > $MAXOPENSOCK) { $wso++; while(($opensock + (($numports * ($tbs + 1)) / 2)) > $MAXOPENSOCK) { threads->yield(); sleep(1); } $wso--; } $tbs++; # threads building sockets counter for my $port (@ports) { threads->yield(); # create some non-blocking sockets # eval { $sock[$port] = IO::Socket::INET->new( Proto => 'tcp', Blocking => 0 ) or die "$!"; }; if($@) { print STDERR "$thread_num - $victim:$port - socket: $@"; return(-1); # just end the thread if we can't create a socket } $opensock++; } $tbs--; # wait for SYN_SENT count to drop # if(($opensyn + (($numports * ($tbc + 1)) / 2)) > $MAXOPENPORTS) { $wsy++; while(($opensyn + (($numports * ($tbc + 1)) / 2)) > $MAXOPENPORTS) { threads->yield(); sleep(1); } $wsy--; } $tbc++; # threads building connections counter for my $port (@ports) { threads->yield(); my $iaddr = inet_aton($victim); my $paddr = sockaddr_in($port, $iaddr); eval { # connect to all of them connect($sock[$port], $paddr); # or die "$!"; }; if($@) { unless($!{EINPROGRESS}) { # non-blocking print STDERR "$thread_num - $victim:$port - connect: $@"; } } $sel->add($sock[$port]); # select them } for($sel->can_write($TCP_TIMEOUT)) { # look for any writable sockets my $peerport = $_->peerport(); if($peerport) { # that are actually connected $op_c++; push(@open, $peerport); my @nmap = `/usr/local/bin/nmap -P0 -A --version-all -p T:$peerport $victim`; print STDERR "\n**** $victim:$peerport is open\n"; for(@nmap) { print STDERR if(/(tcp open|Service Info)/); } print "\n\n"; } } $tbc--; for my $port (@ports) { threads->yield(); if(defined($sock[$port])) { close($sock[$port]); # close sockets $sel->remove($sock[$port]); # and stop selecting them } $opensock--; } return @open } # test socks connectivity for a host and port # sub testsocks($$) { my ($a, $port) = @_; my ($sock, $c); my $p_res; for my $proto (4, 5) { # test SOCKS4/5 threads->yield(); $sock = new Net::SOCKS( socks_addr => $a, socks_port => $port, protocol_version => $proto); eval { local($SIG{'ALRM'}) = sub { die "alarm" }; # set timeout alarm($SOCKS_TIMEOUT); $c = $sock->connect( peer_addr => $SMTPADDR, peer_port => 25); alarm(0); }; if($@) { print STDERR "\n$thread_num - " . localtime(time) . " - $a:$port - socks timeout\n\n"; $sock->close(); return; } if($c) { # if connected # print STDERR "\n$thread_num - " . localtime(time) . # " - $a:$port - connected\n\n"; my $line = <$c>; # look for Sendmail banner (($line) and ($line =~ /Sendmail/)) and do { print STDERR "$thread_num - \"$line\"\n"; print "\n\n\t\t**** $a:$port is a SOCKS$proto proxy\n\n\n"; $prx_c++; }; } $sock->close(); } }