Rasguñando Netfilter (parte 4)

En la parte anterior, mostré cómo imponer límites razonables a los clientes, y cómo registrar trazas. En esta parte pretendo mostrar cómo minimizar el riesgo de falseo de direcciones de origen, detectar posibles escaneos de puertos, y otros temas afines.

Tal como dejamos nuestras reglas en la parte anterior, un usuario malicioso dentro de nuestra red local podría intentar falser su dirección IP de origen para hacerse pasar por otro equipo, de modo que conviene protegerse en este sentido.

Mitigando el falseo de direcciones

El primer paso será evitar (y registrar) que otro equipo pueda intente colocarse como dirección IP de origen la dirección de cualesquiera de las interfaces de nuestro equipo:

¿Por qué recomiendo usar la tabla raw en lugar de la tabla filter? Para empezar, porque es la primera tabla con la cual se evalúan los paquetes; además es rápida, porque en esta tabla aún no se activa el rastreo de conexiones. De modo que puede aprovecharse para mitigar intentos de ataque. En este caso, evitar que un paquete recibido por cualquier interfaz diferente a la local, utilice una dirección IP de origen perteneciente a nuestro equipo.

Además, si nuestro equipo tiene su interfaz LAN con una dirección en el bloque 192.168.0.0/24. Por lógica, ningún paquete proveniente de esa subred debería tener una ip de origen que no pertenecza al rango, de modo que agreguémoslo:

Ahora bien, existe una excepción válida. Si nuestro equipo tiene además habilitado un servicio DHCP, con la regla anterior ningún otro equipo de nuestra LAN podría utilizar dicho servicio para recibir la asignación apropiadamente. Una forma de solucionar este problema es mediante cadenas personalizadas, pero primero conviene comprender el funcionamiento del servicio DHCP.

Cuando se conecta un nuevo equipo que nunca ha recibido una asignación de dirección IP, envía un paquete mediante el protocolo UDP desde el puerto 68 con la ip de origen 0.0.0.0, destinado al puerto 67 de la ip 255.255.255.255, es decir, a la dirección de difusión (BROADCAST) global.

Aunque esto sea solo parte del proceso de asignación, comprenderlo nos sirve para declarar algo como esto:

El bloque anterior tiene dos partes, la primera crea en la tabla raw una nueva cadena de nombre DHCP, a la cual se agregan entonces reglas que registren todos los paquetes, acepten los que conicidan con una petición DHCP, descarten todo los demás, y finalmente regresen a la cadena desde donde se hizo el salto.

En la segunda parte del bloque, utilizamos la cadena que acabamos de crear como destino de salto para todos aquellos paquetes entrantes por la interfaz de la red local (eth0) cuya IP de origen no pertenezca al bloque de direcciones de nuestra subred local, y posteriormente permitimos la entrada a los que cumplan el criterio de una solicitud de asignación correcta. Pudiera parecer que hay redundancia, pero sin la penúltima regla del bloque, aunque la solicitud de asignación se acepte en la tabla raw, no llegaría al servicio DHCP a causa de las políticas predeterminadas de la cadena INPUT, que son restrictivas, de modo que hay que permitir el acceso explícitamente.

Finalmente, se añade una regla para las renovaciones, que son un caso diferente porque el equipo invitado ya obtuvo una dirección IP de la red local, de modo que solicitará la renovación directamente al servidor que le otorgó la asignación. Sólo en caso de no encontrar el servidor, el equipo solicita una asignación nueva, que quedará similarmente cubierta por el bloque de reglas que acabamos de declarar.

Las respuestas del servicio quedarín cubiertas por las reglas que permitan paquetes de conexiones establecidas y relacionadas, que en el artículo pasado movimos hacia el final de nuestro script.

Ahora bien, hasta aquí hemos prevenido una parte de los intentos de suplantación, pero queda otro escenario: que alguien se coloque la dirección IP de un equipo privilegiado. Para minimizar esta vulnerabilidad, puede utilizarse la dirección física de las interfaces.

Direcciones físicas

Cada interfaz de red, además de utilizar una dirección IP tiene una dirección física que consiste en seis cifras hexadecimales, de las cuales las primeras tres suelen utilizarse para identificar el fabricante del equipamiento y las últimas tres funcionan como identificador seriado del dispositivo. Estas direcciones también pueden utilizarse para asegurar un poco más nuestro cortafuegos.

Por ejemplo, supongamos que el modem-router ADSL que nos proporciona enlace a Internet tiene la dirección IP 192.168.2.1 y la dirección MAC A1:B2:C3:D4:E5:F6, y que en nuestra red local tenemos un servidor de correo con la dirección IP 192.168.0.25 y la dirección MAC 00:00:00:A1:B2:C3. Ambos equipos son sensibles y quisiéramos evitar en lo posible que otros equipos de la red intenten asignarse estas direcciones IP; podríamos lograrlo con reglas como estas:

Nótese que este tipo de reglas reglas no son infalibles, son sólo una medida más de protección básica. Existen herramientas que permiten modificar una dirección física, pero no por ello este tipo de comprobación deja de tener su utilidad.

Mitigando el escaneo de puertos

Uno de los primeros pasos que un posible atacante suele tomar antes de intentar vulnerar una red, consiste en escanear los puertos abiertos. De modo que dificultar el escaneo de puertos puede ser un medida efectiva de seguridad adicional.

Pues bien, existe un módulo para netfilter llamado psd cuyo propósito es analizar los paquetes TCP y UDP provenientes de un mismo cliente con diferentes puertos de destino para tratarlos como una secuencia de escaneo de puertos. Sucede que este módulo no se instala por defecto, sino que se ecuentra dentro de un grupo de extensiones que es necesario compilar desde el código fuente. Afortunadamente, existe un asistente que puede facilitarnos la tarea. Para instalar el módulo entonces, usualmente basta con ejecutar los siguientes comandos:

Con esto, el paquete modules-assistant compilará e instalará automáticamente el código fuente de las extensiones contenidas en xtables-addons. ¿Cómo usar entonces el módulo psd que acabamos de instalar? Esta podría ser una forma:

En este caso propongo usar la tabla mangle porque es una tabla que se evalúa antes que filter, tiene cadena PREROUTING y control de estado. En otras palabras, los paquetes puede verificarse antes de enrutarse, independientemente de que estén destinados al propio equipo u otros, y puede comprobarse su estado. Esto evita duplicar las mismas reglas en las cadenas INPUT y FORWARD de la tabla filter.

En la cadena integrada PREROUTING declaramos entonces una regla que evalúe con el criterio de parámetros del módulo psd los paquetes nuevos que no provengan de la interfaz local, y en caso de coincidencia, los hacemos saltar a la cadena personalizada PORTSCAN. De esta manera, evitamos declarar la regla múltiples veces.

En la cadena personalizada PORTSCAN, ante todo, el intento quedará registrado en las trazas. Luego, en caso de que provenga de la interfaz interna (LAN), se rechazará con un mensaje de prohibición por parte del administrador, y en caso de que provenga desde cualquier otra interfaz, se descartará silenciosamente.

El mecanismo del módulo psd es el siguiente: se establece un límite de peso, que en este caso es 21. Cada puerto que un cliente “toque” se pesará según el valor de --psd-lo-ports-weight para los puertos entre 0 y 1023, y según el valor de --psd-hi-ports-weight para los puertos entre 1024 y 65535. Si en el lapso de tiempo definido por --psd-delay-threshold (en centécimas de segundo) la sumatoria de pesos alcanza el valor establecido, la coincidencia será verdadera.

Un par de detalles a tomar en consideración: En el caso de Proxmox, las extensiones deben instalarse en el host si se pretende usarlas desde contenedores. En caso de tenerse habilitado SELinux, es conveniente deshabilitarlo antes de instalar las extensiones.

Servicios de administración

El módulo psd podría ser insuficiente si el cliente toma la precaución de probar sólo unos 5 puertos cada 5 segundos. No obstante, es muy probable que entre los intentos de acceso se encuentren servicios de administración como Telnet, SSH, VNC o RDP que suelen estar reservados sólo para ciertos equipos en la red. Esto podemos aprovecharlo, como explicaré a continuación.

Actividad reciente

Netfilter tiene un módulo denominado recent que podríamos utlizar para descartar temporalmente cualquier paquete que provenga de un equipo no autorizado que haya tocado recientemente alguno de los puertos correspondientes a servicios de administración. ¿Cómo lograrlo? De la siguiente manera, por ejemplo:

En este caso, creamos en la tabla raw la cadena personalizada VERIFICAR, en la cual registramos y descartamos cualquier paquete cuya IP de origen coincida con la de un equipo autorizado, pero no así la dirección MAC correspondiente. Posteriormente agregamos en la cadena PREROUTING una regla que haga saltar los paquetes con IP de origen en el rango de direcciones de administración, para evaluarlos con la cadena personalizada. Esto sirve como una primera etapa de filtrado. Nótese la introducción de los comentarios.

Entonces, en la tabla mangle creamos otra cadena personalizada VERIFICAR, en la cual primero registramos todo intento de conexión, y luego descartamos aquellos paquetes cuya dirección IP de origen no se encuentre entre las autorizadas a utilizar los servicios de administración, no sin antes colocar la dirección IP de origen en una lista negra (esta es la novedad).

Posteriormente, en la cadena PREROUTING descartamos durante 5 minutos todo paquete proveniente de una dirección IP en lista negra, y finalmente hacemos que aquellos paquetes de una nueva conexión que intenten acceder a puertos de administración salten a evaluarse con la cadena personalizada VERIFICAR de la tabla mangle.

La penúltima regla se coloca antes de la que manda a verificar el paquete, porque si la dirección IP de un cliente ya fue puesta en lista negra, pueden descartarse los paquetes posteriores provenientes de ese cliente directamente sin que sea necesario evaluarlos cada vez. Adicionalmente, el parámetro --update hace que se extienda el tiempo de prohibición de acceso por otros 5 minutos. Es decir que el cliente en lista negra que insista en acceder a nuestro equipo, solo conseguirá prolongar el tiempo que permanece en la lista negra. Sólo después de un período de inactividad superior a 5 minutos, podrá volver a realizar operaciones normales.

Colocar estas reglas en la tabla mangle sin declarar una dirección IP de destino, ofrece la ventaja de que funcionarán lo mismo para un paquete entrante que para un paquete que se intente reenviar a otro equipo usando el nuestro como pasarela. En otras palabras, a los efectos prácticos es equivalente a declarar las mismas reglas duplicadas en las cadenas INPUT y FORWARD de la tabla filter.

Paquetes inválidos

En la primera parte mencionamos el estado INVALID (inválido), que se asigna a cualquier paquete con parámetros inesperados o que alegue ser parte de una conexión, pero el rastreo de conexiones no haya visto tráfico asociado a ninguna conexión conocida.

Además de este, existen para el protocolo TCP las así llamadas flags (banderas) o parámetros del protocolo: SYN, ACK, FIN, RST, URG, PSH, ALL, NONE. No nos extenderemos aquí a detallar el funcionamiento del protocolo TCP/IP, pero existen combinaciones de estos parámetros que a veces se utilizan para fines maliciosos y en realidad no deberían coexistir naturalmente en un mismo paquete.

La documentación de netfilter no es muy clara en cuanto a qué considera como inválido, pero si examinamos el código fuente (netfilter/nf_conntrack_proto_tcp.c), veremos que esto depende del valor del kernel net.netfilter.nf_conntrack_tcp_be_liberal. Si este valor se ha establecido en 0, el estado INVALID detectará una buena parte de las combinaciones de flags incorrectas, por lo que podrá ser suficiente con algo como esto:

Sin embargo, si el valor de net.netfilter.nf_conntrack_tcp_be_liberal es diferente de cero, solo se detectarán como inválidos los paquetes con el flag RST activo pero fuera de la ventana esperada, por lo que podrá ser necesario excluir manualmente las combinaciones incorrectas, por ejemplo:

Ahora bien, la comprobación de flags del protocolo TCP no invalida la comprobación del estado INVALID, pues este puede cubrir también el protocolo UDP, de modo que ambas soluciones podrían combinarse por si acaso.

Uniéndolo todo

A estas alturas, nuestro script de cortafuegos ha adquirido cierta complejidad, de modo que conviene mostrar aproximadamente cómo podría quedar, para ver el orden de declaración de las reglas:

Con esto, incluso si las reglas de nuestro cortafuegos no garantizan una efectividad absoluta, nuestro equipo estará considerablemente más protegido que al principio.

(Continuará)

En la próxima parte de este tema, hablaré de las redirecciones y la tabla de traducción de direcciones, entre otros temas.

Notas

Revisando el artículo encontré errores en la declaración de algunas reglas, que acabo de arreglar. Por favor, notificar cualquier posible error en un comentario.

(Visited 110 times, 1 visits today)
Hugo Florentino
Sobre Hugo Florentino 7 Artículos
Administrador de redes y sistemas. Usuario regular de GNU/Linux desde Octubre de 2008. Miembro fundador del Grupo de Usuarios de Tecnologías Libres (GUTL).

Sé el primero en comentar

Dejar una contestacion

Tu dirección de correo electrónico no será publicada.


*