Secfilter – Módulo de seguridad para Kamailio

En un sistema de VoIP es necesario disponer de servidores proxy para enrutar el tráfico SIP entre los diferentes actores, como por ejemplo entre operadores, wholesales, carriers, etc. Una PBX, como pueda ser Asterisk, es capaz de reenviar tráfico pero también realiza otras muchas funciones, es decir, su finalidad es gestionar los mensajes, ejecutar planes de llamada, locuciones, etc, y esto conlleva a una gran carga. Sin embargo, la finalidad principal de un proxy es enrutar tráfico, y por tanto, puede gestionar muchísimos mensajes con un bajo coste.

Entre las soluciones Open Source más conocidas nos encontramos con Kamailio y con OpenSIPS, que partiendo de una misma base (OpenSER) han evolucionado de forma paralela. No voy a entrar a valorar cuál es mejor o peor porque cada uno tiene sus cosas, pero yo hace tiempo que me decanté por Kamailio.

Los SIP Proxy han ido evolucionando y nos ofrecen numerosos módulos que nos permiten realizar otras funciones muy importantes, evitando la necesidad de tener terceras aplicaciones. Hablo por ejemplo de módulos para autenticar usuarios, para hacer relay de audio, accounting, etc.

En mi trabajo usamos Kamailio y algo que me preocupaba bastante era la gestión de los ataques. Un servidor proxy suele ser muy versátil y nos permite tener un control total de los mensajes, pudiendo analizar hasta el último detalle de cada paquete que nos llega, de forma que es posible detectar muchos tipos de ataques, simplemente analizando una serie de patrones. Pero esto es algo que va variando con el tiempo. Me refiero a que aparecen nuevas herramientas, nos encontramos cada día con diferentes direcciones IP que necesitamos bloquear, y esto requiere una actualización dinámica de las listas negras. Por lo que configurar manualmente en el fichero de configuración, un filtro de datos es algo muy poco práctico, ya que cada cambio o actualización que hagamos requeriría reiniciar el servicio.

Cuando hablo de ataques me refiero a aquellos que son no dirigidos y, que corresponden con el 99% de los ataques. Bien es cierto que podemos ser un objetivo de alguien y que vaya por nosotros para escuchar nuestras comunicaciones o para robar datos, pero en la mayoría de los casos nos encontramos con gente cuya única finalidad es llamar desde nuestros sistemas, es decir, generar tráfico que se traduce en una pérdida económica para nosotros y grandes ganancias para ellos. Por tanto, nos tenemos que proteger de los escaneos de grandes rangos de red, normalmente usando herramientas conocidas, y cuyo objetivo es localizar sistemas de VoIP para luego, bien intentar crackear alguna cuenta de usuario, o bien intentar emitir llamadas desde nuestro sistema aprovechando fallos de configuración.

Por tanto, el principal objetivo es, por un lado detectar y bloquear esas herramientas, y por otro, poder bloquear de forma dinámica tanto direcciones IP como países enteros.

Como soluciones disponemos de diferentes alternativas, por ejemplo a nivel de iptables podemos analizar el contenido de los paquetes y bloquear según la búsqueda de unos patrones, por ejemplo:

Con estas entradas en nuestro firewall es posible bloquear aquellos mensajes que contengan la palabra friendly-scanner y que vayan destinados al puerto del servicio de VoIP de nuestra centralita. Esta solución está bien para bloquear ataques de determinadas herramientas, pero tiene varios inconvenientes. Por un lado, si queremos bloquear direcciones IP, vamos a tener una lista bastante grande, dado que cada día aparecen nuevas IPs a bloquear. Y por otro lado, si el atacante realiza conexiones cifradas (por TLS) no vamos a poder bloquearlo, dado que el firewall no podrá analizar el contenido de los paquetes.

Otra herramienta más que conocida es fail2ban.

Sinceramente, me parece una buena solución para muchos servicios, pero no para VoIP. Esta herramienta se basa en el análisis de logs para detectar ataques y bloquear direcciones IP, pero supongamos que lo tenemos instalado en una máquina con Asterisk y decidimos actualizar la versión de nuestra PBX. Es posible que en la siguiente versión que instalamos los mensajes de error sean diferentes, por lo que fail2ban no va a funcionar correctamente, hasta que no modifiquemos y adaptemos el fichero de configuración. Por otro lado, quizás para una pequeña centralita haga su papel, pero a nivel de operador donde los mensajes de error los pone el administrador y no son mensajes por defecto de una PBX, esta solución ya no sirve.

Estuve revisando otra herramienta que ha programado mi gran amigo Elio Rojano (@hellc2) y que me parece muy interesante:

Esta herramienta sólo funciona en Asterisk y utiliza el servicio Manager (AMI) para monitorizar el tráfico que circula hacia nuestra PBX, de manera que detecta y bloquea ataques por fuerza bruta para intentar crackear nuestras contraseñas de usuario, así como envíos masivos de mensajes INVITE tratando de generar llamadas desde nuestro sistema.

Si sois usuarios de Asterisk, os recomiendo que le echéis un ojo. La podéis descargar aquí: https://github.com/sinologicnet/sipcheck

A pesar de que la herramienta de Elio es una buena solución, tampoco cubre las necesidades de un operador, donde la infraestructura está basada en diferentes servicios y en el uso de proxies.

Hace algo más de un año, realicé un proyecto al que bauticé como SIP-Firewall y que consistía en un proxy SIP Kamailio con una configuración de seguridad y con un pequeño panel de control para gestionar diferentes reglas o filtros de bloqueo. La estructura es la siguiente:

La finalidad es usar el proxy delante de nuestro sistema para que filtre los posibles ataques y sea capaz de bloquear aquellos paquetes que considera como no legítimos. Para ello dispone de:

  • Lista blanca y negra de direcciones IP
  • Lista blanca y negra de agentes de usuario
  • Lista blanca y negra de usuarios
  • Lista blanca y negra de dominios
  • Lista blanca y negra de países
  • Lista negra de destinos a los que no queremos que se pueda llamar

Todos estos datos son almacenados en una base de datos y gestionados a través de un panel de control, de manera que cada vez que el proxy recibe un mensaje, consulta con la base de datos para ver si debe o no bloquearlo. De esta forma tenemos todo funcionando de forma dinámica y, me parece una buena solución cuando tenemos una centralita detrás, o un sistema no excesivamente grande, pero para operadores que mueven miles de llamadas cada día, me parece que no es la solución más idónea. Y voy a explicar el por qué.

Supongamos que llega una solicitud de llamada (mensaje INVITE) a nuestro proxy, por lo que vamos a realizar un total de 11 consultas a la base de datos (lista blanca y negra de IPs, agentes de usuario, dominios, países y usuarios. Además de lista negra de destinos). En un sistema que gestiona miles de llamadas diarias estará generando muchas consultas innecesarias contra su base de datos. Y lo mismo ocurrirá con mensajes del tipo REGISTER, que corresponden con el registro de dispositivos en el sistema, con la finalidad de poder recibir llamadas. Cada dispositivo lo configuramos para que vuelva a registrar cada X minutos y, además, cada vez que lo hace se le pide autenticación, por lo que genera un segundo mensaje REGISTER. Esto supone 10 consultas a la base de datos por cada REGISTER, 20 en total. Y como digo, un sistema con miles de dispositivos conectados que cada pocos minutos vuelven a registrarse, supone demasiadas consultas innecesarias contra nuestra base de datos.

Así que pensé en programar un módulo para Kamailio que haga esta función, de manera que no requiera acceder a la base de datos para cada consulta, sino que mantenga los datos en memoria. Y así es como surgió Secfilter:

Al igual que SIP-Firewall, Secfilter es capaz de detectar y bloquear mensajes basándose en el análisis de los mismos. Hablo de direcciones IP, agentes de usuario, países, dominios, etc. Además ofrece la posibilidad de detectar y bloquear ataques de SQL injection.

La principal ventaja del módulo Secfilter es que, tras arrancarse con Kamailio, accede a la base de datos y recupera todos los datos de listas blancas y negras y los almacena en memoria, de forma que cada mensaje que analice luego lo hará sin necesidad de acceder de nuevo a la base de datos.

Entre las funciones que ofrece tenemos:

secf_check_ip() comprueba si la dirección IP que ha conectado con nuestro proxy está en nuestra lista negra (o blanca). Y con el resultado actuaremos según nuestras necesidades:

En el ejemplo podemos ver que si el resultado es -2, es decir, se ha encontrado la dirección IP en la lista negra, se bloquea el mensaje. Pero también podríamos hacerlo a la inversa, por ejemplo, bloquear si el resultado es diferente a 2, o lo que es lo mismo, permitir sólo el acceso a las direcciones IP que estén en nuestra lista blanca.

secf_check_ua() comprueba si el agente de usuario que viene en el mensaje está en nuestra lista negra (o blanca). Esto es algo muy útil dado que la mayoría de herramientas ponen ahí su propia huella, algo que nos permite identificarlas y bloquearlas sin mayor problema.

secf_check_country() nos permite bloquear un país entero. No hablamos de llamadas cuyo destino sea ese país, sino a las conexiones de todos los rangos de direcciones IP de ese país. No hace falta explicar mucho la utilidad de esta función para cuando estamos sufriendo un ataque de diferentes direcciones IP de un mismo país. Simplemente metemos el código de ese país en la lista negra y todos los mensajes quedarán bloqueados.

secf_check_from_hdr(), secf_check_to_hdr(), secf_check_contact_hdr() son 3 funciones cuyo objetivo es analizar las cabeceras FROM, TO y CONTACT respectivamente. También es muy frecuente identificar determinadas herramientas buscando patrones en estas cabeceras, por lo que es interesante tenerlas controladas. La ventaja de estas funciones es que comprueban varios parámetros, por ejemplo, con secf_check_from_hdr() se verificarán los campos From User, From Name y From Domain, contrastándolos con distintos campos de nuestras listas negras o blancas, como usuarios o dominios. Y lo mismo para las otras dos funciones.

secf_check_dst() puede sernos útil a la hora de gestionar llamadas (mensajes INVITE), de manera que podemos tener una lista negra de destinos a los que no queremos que nuestros usuarios llamen, por ejemplo números premium internacionales que haya en alguna lista negra conocida, etc.

En definitiva, de lo que se trata es de analizar el mayor número de valores posibles dentro de cada mensaje que llega a nuestro sistema, y todo ello con el mínimo coste en cuanto al uso de nuestros recursos:

Además de las funciones que analizan cabeceras basándose en listas blancas y negras, disponemos de un par de funciones para intentar detectar y bloquear ataques de SQLi:

secf_check_sqli_hdr() admite como parámetro el valor que deseamos analizar, lo que nos permite comprobar cualquier variable o pseudovariable en busca de ciertos caracteres que identifiquen un ataque de SQL injection.

secf_check_sqli_all() comprueba en todas las cabeceras importantes de forma automática.

¿Por qué bloquear ataques de SQL injection? Una PBX dispone de un sistema cerrado donde simplemente podemos configurar ciertos valores de los usuarios así como del comportamiento de los planes de llamada, pero si tenemos el sistema debidamente actualizado, a no ser que aparezca un 0-day, podremos tener problemas de malas configuraciones o de contraseñas débiles, pero no seremos susceptibles a un ataque de SQLi. Sin embargo, en un proxy toda la configuración radica en unos ficheros y la realiza el administrador del sistema, por lo que sí que es fácil meter la pata y que seamos vulnerables a este tipo de ataques.

Aquí vemos un ataque típico de SQLi, bloqueado por Secfilter:

Como se puede apreciar, se está intentado inyectar en el campo user. Y esto no es una casualidad, sino que el atacante está buscando la posibilidad de que nosotros verifiquemos el usuario para permitir cursar una llamada. Es decir, supongamos que tenemos una configuración en la que, en lugar de pedir siempre autenticación de usuarios a la hora de realizar una llamada (mensaje INVITE), algo que debería ser así por motivos de seguridad, lo que hacemos es verificar el usuario que realiza la llamada, de forma que si ese usuario está en nuestra base de datos de usuarios permitidos, le dejaremos llamar. Sería algo así:

Obviamente, si no parseamos el contenido de la variables $fU (from user), nos podrán colar una inyección de SQL y alterar el resultado de la sentencia para que siempre sea verdadera, ya que false OR true = true.

Además de las funciones, Secfilter dispone de una seria de comandos RPC para interactuar con el módulo sin necesidad de reiniciar el servicio:

secfilter.reload permite recargar los datos de nuestra base de datos. Algo muy útil si añadimos nuevos valores en las listas blancas o negras. Gracias a este comando, Secfilter actualizará todos los valores que tiene en memoria.

secfilter.print nos permite imprimir los valores que tenemos en memoria. Podemos añadir como parámetro el campo que deseamos visualizar, como country, ua, etc. Por defecto sacará todos los valores.

secfilter.add_bl, secfilter.add_wl, secfilter.add_dst son diferentes comandos que nos permiten añadir valores a la lista legra, blanca o de destinos permitidos. Estos datos no se almacenarán en la base de datos, sino que únicamente quedarán en memoria, por lo que si reiniciamos Kamailio, o el módulo, los perderemos.

Como he comentado antes, la forma de guardar datos de forma persistente es directamente en la base de datos y luego haciendo un reload, pero este comando tiene una gran utilidad dado que no siempre queremos almacenar todo en nuestra base de datos. Y un ejemplo muy claro lo tenemos en el bloqueo de direcciones IP. Cuando sufrimos un ataque, la persona que está al otro lado no nos está atacando desde su casa sino desde un servidor comprometido, vete a saber en qué país. Seguramente en unos días nos atacará desde una máquina con una IP diferente, por lo que no tiene mucho sentido tener un listado con cientos o miles de direcciones IP a bloquear, dado que ese ataque va a durar unas horas.

secfilter.stats secfilter.stats_reset nos ofrecen la posibilidad de ver unas pequeñas estadísticas de bloqueos. Esto es útil para analizar cómo se está comportando nuestro módulo.

Y el resultado sería algo como esto:

He tenido la gran suerte de que el módulo ha sido aceptado por el equipo de desarrollo de Kamailio y está integrado en el proyecto desde la versión 5.3, que se lanzó hace un mes. Podéis ver toda la documentación del módulo aquí: https://www.kamailio.org/docs/modules/devel/modules/secfilter.html

La gran ventaja de que se forme parte del proyecto Kamailio es que no es necesario descargar los fuentes, añadir el módulo y recompilar todo a mano, sino que lo encontramos ya integrado, en cualquier distribución y sobre cualquier plataforma donde se use Kamailio.

No me queda más que agradecer a Daniel-Constantin Mierla (@miconda) y a todo el equipo de desarrollo de Kamailio por aceptar el módulo, algo bastante complicado de conseguir dado la gran magnitud del proyecto Kamailio, que se traduce en unos estrictos controles y requisitos a nivel de estructura y programación. Y gran parte de culpa la tiene mi gran amigo Victor Seva (@linuxmaniac), que lleva varios años como desarrollador de Kamailio y que me ha ayudado y guiado en la forma de cómo elaborar la estructura del módulo Secfilter.

Espero que pueda ser de utilidad a todos aquellos usuarios de Kamailio y, por supuesto, se aceptan críticas y sugerencias. Todo sea por mejorar la funcionalidad de Secfilter.

Deja un comentario