All pastes #816622 Raw Edit

Unnamed

public text v1 · immutable
·published 2007-12-14 21:47 UTC
rendered paste body
#!/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 <cidr block>\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 <cidr block>\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();
	}
}