¿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

Deja un comentario