Escaner de servicios SIP

Continuando con la VoIP, tengo otro script que he programado este verano y que se trata de un escaner de servicio SIP. ¿porqué? pues porque el más que conocido SIPvicious (http://code.google.com/p/sipvicious/) o se ha quedado viejo, o no está especialmente diseñado para Asterisk, y el svmap.py, muchas veces, no muestra los resultados esperados. En algunos escaneos hay que usar el parámetro -c y en otros no para obtener resultados válidos, dependiendo de la versión con la que te enfrentes. Con lo cual toca escanear los mismos rangos 2 veces para obtener todos los resultados.

Además, que el fingerprint no da muchas pistas. Es más útil la información que aparece en el user agent.

Bueno, y tras criticar a este fabuloso programa (el resto de herramientas de la suite SIPvicious funcionan bien xDD) pongo mi modesto script, que escanea, usando threads, (va un poco más rápido que SIPvicious) bien un host o bien un rango de IPs. Del mismo modo que puede escanear diferentes puertos a la vez.

También es posible indicarle el método de escaneo que deseamos, permitiendo mandar paquetes INVITE, OPTIONS o REGISTER.

Y sin enrollarme más, aquí va el script:

#!/usr/bin/perl
# -=-=-=-=-=-=
# Sipscan v1.0
# -=-=-=-=-=-=
#
# Pepelux <pepeluxx[at]gmail[dot]com>

use warnings;
use strict;
use IO::Socket;
use NetAddr::IP;
use threads;
use threads::shared;
use Getopt::Long;
use Digest::MD5;

my $maxthreads = 300;
my $time_ping = 2; # wait secs

my $threads : shared = 0;
my $found : shared = 0;
my $count : shared = 0;
my $percent : shared = 0;
my @range;
my @results;

my $host = '';	   # hosts to scan
my $port = '';	   # ports to scan
my $method = '';	# method to use (INVITE, REGISTER, OPTIONS)
my $v = 0;		   # verbose mode

my $user = "100";
my $pass = "aaaaaa";
my $lport = "5061";
my $myip = "anonymous";
my $tmpfile = "sipscan".time().".txt";

open(OUTPUT,">$tmpfile");

OUTPUT->autoflush(1);
STDOUT->autoflush(1);

sub init() {
	my $pini;
	my $pfin;

	if ($^O =~ /Win/) {system("cls");}else{system("clear");}

	# check params
	my $result = GetOptions ("h=s" => \$host,
	                         "m=s" => \$method,
	                         "p=s" => \$port,
	                         "v+" => \$v);

	help() if ($host eq "");

	$port = "5060" if ($port eq "");
	$method = uc($method);
	$method = "OPTIONS" if ($method eq "");

	if ($host =~ /\-/) {
		my $ip = $host;

		$ip =~ /([0-9|\.]*)-([0-9|\.]*)/;
		my $ipini = $1;
		my $ipfin = $2;

		my $ip2 = $ipini;
		$ip2 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/;
	  	my $ip2_1 = int($1);
	  	my $ip2_2 = int($2);
	  	my $ip2_3 = int($3);
	  	my $ip2_4 = int($4);

		my $ip3 = $ipfin;
		$ip3 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/;
	  	my $ip3_1 = int($1);
	  	my $ip3_2 = int($2);
	  	my $ip3_3 = int($3);
	  	my $ip3_4 = int($4);

	  	for (my $i1 = $ip2_1; $i1 <= $ip3_1; $i1++) {
		  	for (my $i2 = $ip2_2; $i2 <= $ip3_2; $i2++) {
			  	for (my $i3 = $ip2_3; $i3 <= $ip3_3; $i3++) {
				  	for (my $i4 = $ip2_4; $i4 <= $ip3_4; $i4++) {
					  	$ip = "$i1.$i2.$i3.$i4";
						push @range, $ip;
					}
				}
			}
		}

	}
	else {
		my $ip = new NetAddr::IP($host);

		if ($ip < $ip->broadcast) {
			$ip++;

			while ($ip < $ip->broadcast) {
				my $ip2 = $ip;
				$ip2 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/;
			  	$ip2 = "$1.$2.$3.$4";
				push @range, $ip2;
				$ip++;
			}
		}
		else {
			push @range, $host;
		}
	}

	if ($port =~ /\-/) {
		$port =~ /([0-9]*)-([0-9]*)/;
		$pini = $1;
		$pfin = $2;
	}
	else {
		$pini = $port;
		$pfin = $port;
	}

	my $nhost = @range;

	for (my $i = 0; $i <= $nhost; $i++) {
		for (my $j = $pini; $j <= $pfin; $j++) {
			while (1) {
				if ($threads < $maxthreads) {
					last unless defined($range[$i]);
					my $thr = threads->new(\&scan, $range[$i], $j);
					$thr->detach();
					$percent = ($count/($nhost*($pfin-$pini+1)))*100;
					$percent = sprintf("%.1f", $percent);
					print "THREADS: $threads || STATUS: $percent% || FOUND: $found     \r";

					last;
				}
				else {
					sleep(1);
				}
			}
		}
	}

	sleep(1);

	close(OUTPUT);

	print "THREADS: 0 || STATUS: 100% || FOUND: $found     \r\n";

	open(OUTPUT, $tmpfile);

	print "\nIP:port\t\t\t  User-Agent\n";
	print "=======\t\t\t  ==========\n";

	my @results = <OUTPUT>;
	close (OUTPUT);

	unlink($tmpfile);

	@results = sort(@results);

	foreach(@results) {
		print $_;
	}

	print "\n";

	exit;
}

sub scan {
	my $ip = shift;
	my $nport = shift;

	if ($method eq "REGISTER") {
		register($ip, $nport);
	}
	if ($method eq "INVITE") {
		invite($ip, $nport);
	}
	if ($method eq "OPTIONS") {
		options($ip, $nport);
	}
}

sub options {
	{lock($count);$count++;}
	{lock($threads);$threads++;}

	my $ip = shift;
	my $nport = shift;

	my $sc = new IO::Socket::INET->new(PeerPort=>$nport, Proto=>'udp', PeerAddr=>$ip);

	$lport = $sc->sockport();

	my $branch = &generate_random_string(71, 0);
	my $callerid = &generate_random_string(32, 1);

	my $msg = "OPTIONS sip:$ip SIP/2.0\n";
	$msg .= "Supported: \n";
	$msg .= "Allow: INVITE, ACK, OPTIONS, CANCEL, BYE\n";
	$msg .= "Contact: $user <sip:".$user."@".$ip.":$lport>\n";
	$msg .= "Via: SIP/2.0/UDP $ip:$lport;branch=$branch\n";
	$msg .= "Call-id: $callerid\n";
	$msg .= "Cseq: 1 OPTIONS\n";
	$msg .= "From: $user <sip:".$user."@".$ip.">;tag=ddb044893807095baf1cf07269f03118\n";
	$msg .= "Max-forwards: 70\n";
	$msg .= "To: $user <sip:".$user."@".$ip.">\n";
	$msg .= "Content-length: 0\n\n";

	print $sc $msg;

	print "\nSending:\n=======\n$msg\n\n" if ($v eq 1);

	my $data = "";
	my $server = "";
	my $useragent = "";

	LOOP: {
		while (<$sc>) {
			my $line = $_;

			if ($line =~ /[Ss]erver/ && $server eq "") {
	   		$line =~ /[Ss]erver\:\s(.+)\r\n/;

		   	if ($1) {
					$server = $1;
				}
			}

			if ($line =~ /[Uu]ser\-[Aa]gent/ && $useragent eq "") {
	   		$line =~ /[Uu]ser\-[Aa]gent\:\s(.+)\r\n/;

		   	if ($1) {
					$useragent = $1;
				}
			}

			$data .= $line;

			if ($line =~ /^\r\n/) {
				last LOOP;
			}
		}
	}

	if ($data ne "") {
		if ($v eq 1) {
			print "\nReceiving:\n=========\n$data\n\n";
		}

		if ($server eq "") {
			$server = $useragent;
		}
		else {
			if ($useragent ne "") {
				$server .= " - $useragent";
			}
		}

		my $dhost = "$ip:$nport";
		$dhost .= "\t" if (length($dhost) < 10);
		$server = "Unknown" if ($server eq "");
		print OUTPUT "$dhost\t| $server\n";
		{lock($found);$found++;}
	}

	{lock($threads);$threads--;}
}

sub invite {
	{lock($count);$count++;}
	{lock($threads);$threads++;}

	my $ip = shift;
	my $nport = shift;

	my $sc = new IO::Socket::INET->new(PeerPort=>$nport, Proto=>'udp', PeerAddr=>$ip, Timeout => 2);

	$lport = $sc->sockport();

	my $branch = &generate_random_string(71, 0);
	my $callerid = &generate_random_string(32, 1);

	my $msg = "INVITE sip:$ip SIP/2.0\n";
	$msg .= "Supported: \n";
	$msg .= "Allow: INVITE, ACK, OPTIONS, CANCEL, BYE\n";
	$msg .= "Contact: $user <sip:".$user."@".$myip.":$lport>\n";
	$msg .= "Via: SIP/2.0/UDP $myip:$lport;branch=$branch\n";
	$msg .= "Call-id: $callerid\n";
	$msg .= "Cseq: 1 INVITE\n";
	$msg .= "From: $user <sip:".$user."@".$myip.">;tag=ddb044893807095baf1cf07269f03118\n";
	$msg .= "Max-forwards: 70\n";
	$msg .= "To: $user <sip:".$user."@".$ip.">\n";
	$msg .= "Content-length: 123\n\n";
	$msg .= "v=0\n";
	$msg .= "o=anonymous 1312841870 1312841870 IN IP4 $ip\n";
	$msg .= "s=session\n";
	$msg .= "c=IN IP4 $ip\n";
	$msg .= "t=0 0\n";
	$msg .= "m=audio 2362 RTP/AVP 0\n\n";

	print $sc $msg;

	print "\nSending:\n=======\n$msg\n\n" if ($v eq 1);

	my $data = "";
	my $server = "";
	my $useragent = "";
	my $line = "";

	LOOP: {
		while (<$sc>) {
			$line = $_;

			if ($line =~ /[Ss]erver/ && $server eq "") {
	   		$line =~ /[Ss]erver\:\s(.+)\r\n/;

		   	if ($1) {
					$server = $1;
				}
			}

			if ($line =~ /[Uu]ser\-[Aa]gent/ && $useragent eq "") {
	   		$line =~ /[Uu]ser\-[Aa]gent\:\s(.+)\r\n/;

		   	if ($1) {
					$useragent = $1;
				}
			}

			$data .= $line;

			if ($line =~ /^\r\n/) {
				last LOOP;
			}
		}
	}

	if ($data ne "") {
		if ($v eq 1) {
			print "\nReceiving:\n=========\n$data\n\n";
		}

		if ($server eq "") {
			$server = $useragent;
		}
		else {
			if ($useragent ne "") {
				$server .= " - $useragent";
			}
		}

		my $dhost = "$ip:$nport";
		$dhost .= "\t" if (length($dhost) < 10);
		$server = "Unknown" if ($server eq "");
		print OUTPUT "$dhost\t| $server\n";
		{lock($found);$found++;}
	}

	{lock($threads);$threads--;}
}

sub register {
	{lock($count);$count++;}
	{lock($threads);$threads++;}

	my $ip = shift;
	my $nport = shift;

	my $sc = new IO::Socket::INET->new(PeerPort=>$nport, Proto=>'udp', PeerAddr=>$ip);

	$lport = $sc->sockport();

	my $branch = &generate_random_string(71, 0);
	my $callerid = &generate_random_string(32, 1);

	my $msg = "REGISTER sip:$ip SIP/2.0\n";
	$msg .= "Via: SIP/2.0/UDP $myip:$lport;branch=$branch\n";
	$msg .= "Call-id: $callerid\n";
	$msg .= "Contact: $user <sip:".$user."@".$myip.":$lport>\n";
	$msg .= "Cseq: 1 REGISTER\n";
	$msg .= "Expires: 900\n";
	$msg .= "From: $user <sip:".$user."@".$myip.">;tag=ddb044893807095baf1cf07269f03118\n";
	$msg .= "Max-forwards: 70\n";
	$msg .= "To: $user <sip:".$user."@".$ip.">\n";
	$msg .= "Content-length: 0\n\n";

	print $sc $msg;

	print "\nSending:\n=======\n$msg\n\n" if ($v eq 1);

	my $nonce = "";
	my $realm = "";
	my $data = "";

	LOOP: {
		while (<$sc>) {
			my $line = $_;

			if ($line =~ /nonce/ && $nonce eq "") {
	   		$line =~ /nonce\=\"(\w+)\"/i;

		   	if ($1) {
					$nonce = $1;
				}
			}

			if ($line =~ /realm/ && $realm eq "") {
	   		$line =~ /realm\=\"(\w+)\"/i;

		   	if ($1) {
					$realm = $1;
				}
			}

			$data .= $line;

			if ($line =~ /^\r\n/) {
				last LOOP;
			}
		}
	}

	if ($data ne "") {
		print "\nReceiving:\n=========\n$data\n\n" if ($v eq 1);

		$branch = &generate_random_string(71, 0);

		my $md5 = Digest::MD5->new;
		$md5->add($user, ':', $realm, ':', $pass);
		my $HXA = $md5->hexdigest;
		my $uri = "sip:$ip";

		$md5 = Digest::MD5->new;
		$md5->add('REGISTER', ':', $uri);
		my $HXB = $md5->hexdigest;

		$md5 = Digest::MD5->new;
		$md5->add($HXA, ':', $nonce, ':', $HXB);
		my $response = $md5->hexdigest;

		$msg = "REGISTER sip:$ip SIP/2.0\n";
		$msg .= "Via: SIP/2.0/UDP $myip:$lport;branch=$branch\n";
		$msg .= "Call-id: $callerid\n";
		$msg .= "Contact: $user <sip:".$user."@".$myip.":$lport>\n";
		$msg .= "Expires: 900\n";
		$msg .= "From: $user <sip:".$user."@".$myip.">;tag=ddb044893807095baf1cf07269f03118\n";
		$msg .= "Max-forwards: 70\n";
		$msg .= "To: $user <sip:".$user."@".$ip.">\n";
		$msg .= "Authorization: Digest username=\"$user\",realm=\"$realm\",nonce=\"$nonce\",uri=\"sip:$ip\",response=\"$response\"\n";
		$msg .= "Cseq: 2 REGISTER\n";
		$msg .= "Content-length: 0\n\n";

		print $sc $msg;

		print "Sending:\n=======\n$msg\n\n" if ($v eq 1);

		$data = "";
		my $server = "";

		LOOP: {
			while (<$sc>) {
				my $line = $_;

				if ($line =~ /[Ss]erver/ && $server eq "") {
		   		$line =~ /[Ss]erver\:\s(.+)\r\n/;

			   	if ($1) {
						$server = $1;
					}
				}

				$data .= $line;

				if ($line =~ /^\r\n/) {
					last LOOP;
				}
			}
		}

		if ($v eq 1) {
			print "\nReceiving:\n=========\n$data\n\n";
		}

		my $dhost = "$ip:$nport";
		$dhost .= "\t" if (length($dhost) < 10);
		$server = "Unknown" if ($server eq "");
		print OUTPUT "$dhost\t| $server\n";
		{lock($found);$found++;}
	}

	{lock($threads);$threads--;}
}

sub generate_random_string {
	my $length_of_randomstring = shift;
	my $only_hex = shift;
	my @chars;

	if ($only_hex == 0) {
		@chars = ('a'..'z','0'..'9');
	}
	else {
		@chars = ('a'..'f','0'..'9');
	}
	my $random_string;
	foreach (1..$length_of_randomstring) {
		$random_string.=$chars[rand @chars];
	}
	return $random_string;
}

sub help {
	print qq{
Usage:  $0 -h <host> [options]

    == Options ==
      -m <string>      = Method: REGISTER/INVITE/OPTIONS/ALL (default: REGISTER)
      -p <integer>     = Remote SIP port (default: 5060)
      -v               = Verbose mode

    == Examples ==
         \$$0 -h 192.168.0.1 -m invite
         \$$0 -h 192.168.0.0/24 -p 5060-5070
         \$$0 -h 192.168.0.1-192.168.0.100 -p 5060-5070 -v

};

	exit 1;
}

init();

Tags: , ,

One Response to “Escaner de servicios SIP”

  1. tuwi dice:

    sir you deserve a medal!! thnx man

Leave a Reply

A %d blogueros les gusta esto: