Opciones de compilación
A la hora de recompilar un nuevo núcleo de Linux hemos de tener en cuenta algunas opciones dentro del grupo
`Networking Options' que pueden afectar a la seguridad de nuestra máquina (algunos de estos aspectos, para núcleos 2.0, pueden encontrarse en [Wre98]). Sin embargo, antes de entrar en detalles con opciones concretas, es
muy conveniente que introduzcamos soporte para sistemas de ficheros
proc en
`Filesystems' (CONFIG/SMALL>_PROC/SMALL>_FS) y activemos el interfaz
sysctl en
`General Setup' (CONFIG/SMALL>_SYSCTL); con estos pasos habilitamos la capacidad de Linux para modificar ciertos parámetros del núcleo (en
/proc/sys/) sin necesidad de reiniciar el sistema o recompilar el
kernel.
Pasando ya a comentar algunas opciones que nos ofrece Linux, es bastante interesante para la seguridad configurar nuestro sistema como un cortafuegos a la hora de compilar el núcleo (CONFIG/SMALL>_IP/SMALL>_FIREWALL). Linux ofrece en su
kernel facilidades para definir un
firewall de paquetes en el sistema, que además permitirá el
IP-Masquerading. Para que el subsistema de filtrado funcione es necesario que el
IP-Forwarding esté activado de la forma que más tarde veremos.
Otra opción que nos puede ayudar a incrementar la seguridad de nuestro equipo es la defragmentación de paquetes (CONFIG/SMALL>_IP/SMALL>_ALWAYS/SMALL>_DEFRAG) que llegan a través de la red. Cuando un equipo situado entre el origen y el destino de los datos decide que los paquetes a enviar son demasiado grandes, los divide en fragmentos de longitud menor; sin embargo, los números de puerto sólamente viajan en el primer fragmento, lo que implica que un atacante puede insertar información en el resto de tramas que en teoría no debe viajar en ellas. Activando esta opción, en nuestra máquina estos fragmentos se reagruparán de nuevo incluso si van a ser reenviados a otro
host.
Siguiendo con las diferentes opciones del subsistema de red, podemos habilitar el soporte para
`SYN Cookies' (CONFIG/SMALL>_SYN/SMALL>_COOKIES) en el núcleo que estamos configurando. Una red TCP/IP habitual no puede soportar un ataque de negación de servicio conocido como
`SYN Flooding', consistente básicamente en enviar una gran cantidad de tramas con el bit SYN activado para así saturar los recursos de una máquina determinada hasta que los usuarios no pueden ni siquiera conectar a ella. Las
`SYN Cookies' proporcionan cierta protección contra este tipo de ataques, ya que la pila TCP/IP utiliza un protocolo criptográfico para permitir que un usuario legítimo pueda seguir accediendo al sistema incluso si este está siendo atacado. Aunque configuremos y ejecutemos un núcleo con esta opción soportada, hemos de activar las
`SYN Cookies' cada vez que el sistema arranca (como veremos luego), ya que por defecto están deshabilitadas.
En ciertas situaciones es interesante analizar en espacio de usuario - es decir, sin sobrecargar al núcleo más de lo estrictamente necesario - un paquete o parte de él (típicamente, los 128 primeros bytes) que llega a través de la red hasta nuestra máquina; de esta forma, un analizador simple puede tomar ciertas decisiones en función del contenido del paquete recibido, como enviar un correo al administrador en caso de sospecha o grabar un mensaje mediante
syslog. Justamente esto es lo que conseguimos si habilitamos la opción
Firewall Packet Netlink Device (CONFIG/SMALL>_IP/SMALL>_FIREWALL/SMALL>_NETLINK).
Hasta ahora hemos hablado de la posibilidad que tiene Linux para modificar parámetros del núcleo sin necesidad de recompilarlo o de reiniciar el equipo, mediante el interfaz
sysctl; esto implica por ejemplo que podemos modificar el comportamiento del subsistema de red simplemente modificando determinados ficheros de
/proc/sys/ (recordemos que el sistema de ficheros
/proc/ de algunos Unix es una interfaz entre estructuras de datos del núcleo y el espacio de usuario). Veremos en el punto 10.5 algunos de estos parámetros configurables que tienen mucho que ver con la seguridad Linux, en especial con el subsistema de red.
Dispositivos
Linux (no así otros Unices) proporciona dos dispositivos virtuales denominados
/dev/random y
/dev/urandom que pueden utilizarse para generar números pseudoaleatorios, necesarios para aplicaciones criptográficas. El primero de estos ficheros,
/dev/random, utiliza lo que su autor denomina
`ruido ambiental' (por ejemplo, temporizadores de IRQs, accesos a disco o tiempos entre pulsaciones de teclas) para crear una fuente de entropía aceptable y - muy importante - que apenas introduce sobrecarga en el sistema. El segundo archivo,
/dev/urandom, crea un resumen de la entropía de
/dev/random utilizando la función
hash SHA (
Secure Hash Algorithm), diseñada por el NIST y la NSA para su
Digital Signature Standard ([oST84]). Por tanto, tenemos una fuente de entropía aceptable,
/dev/urandom, y otra incluso mejor, pero de capacidad limitada,
/dev/random. Para detalles concretos sobre su funcionamiento se puede consultar el fichero que las implementa dentro del núcleo de Linux,
drivers/char/random.c.
Como en el propio código se explica, cuando un sistema operativo arranca ejecuta una serie de acciones que pueden ser predecidas con mucha facilidad por un potencial atacante (especialmente si en el arranque no interactua ninguna persona, como es el caso habitual en Unix). Para mantener el nivel de entropía en el sistema se puede almacenar el desorden que existía en la parada de la máquina para restaurarlo en el arranque; esto se consigue modificando los
scripts de inicialización del sistema. En el fichero apropiado que se ejecute al arrancar (por ejemplo,
/etc/rc.d/rc.M) debemos añadir las siguientes líneas:
echo "Initializing random number generator..."
random_seed=/var/run/random-seed
# Carry a random seed from start-up to start-up
# Load and then save 512 bytes, which is the size of the entropy pool
if [ -f $random_seed ]; then
cat $random_seed >/dev/urandom
fi
dd if=/dev/urandom of=$random_seed count=1
chmod 600 $random_seed
Mientras que en un fichero que se ejecute al parar el sistema añadiremos lo siguiente:
# Carry a random seed from shut-down to start-up
# Save 512 bytes, which is the size of the entropy pool
echo "Saving random seed..."
random_seed=/var/run/random-seed
dd if=/dev/urandom of=$random_seed count=1
chmod 600 $random_seed
Con estas pequeñas modificaciones de los archivos de arranque y parada del sistema conseguimos mantener un nivel de entropía aceptable durante todo el tiempo que el sistema permanezca encendido. Si de todas formas no consideramos suficiente la entropía proporcionada por estos dispositivos de Linux, podemos conseguir otra excelente fuente de desorden en el mismo sistema operativo a partir de una simple tarjeta de sonido y unas modificaciones en el núcleo ([Men98]), o utilizar alguno de los generadores - algo más complejos - citados en [Sch94].
Algunas mejoras de la seguridad
En esta sección vamos a comentar algunos aspectos de modificaciones del núcleo que se distribuyen libremente en forma de parches, y que contribuyen a aumentar la seguridad de un sistema Linux; para obtener referencias actualizadas de estos códigos - y otros no comentados aquí - es recomendable consultar [Sei99]; para información de estructuras de datos, ficheros o límites del núcleo de Linux se puede consultar [BBD+96] o [CDM97].
Límites del núcleo
En
include/asm/resource.h tenemos la inicialización de algunas estructuras de datos del núcleo relacionadas con límites a la cantidad de recursos consumida por un determinado proceso; por ejemplo, el máximo número de procesos por usuario (RLIMIT/SMALL>_NPROC) se inicializa a
MAX/SMALL>_TASKS/SMALL>_PER/SMALL>_USER, valor que en
include/linux/tasks.h podemos comprobar que se corresponde con la mitad de NR/SMALL>_TASKS (número máximo de procesos en el sistema); en arquitecturas
i86 el valor del límite de procesos por usuario se fija a 256. De la misma forma, el número máximo de ficheros abiertos por un proceso (RLIMIT/SMALL>_NOFILE) se inicializa al valor NR/SMALL>_OPEN, que en el archivo
include/asm/limits.h se define como 1024.
Estos límites se pueden consultar desde espacio de usuario con la llamada
getrlimit(); esta función utiliza una estructura de datos
rlimit, definida en
include/linux/resource.h, que contiene dos datos enteros para representar lo que se conoce como límite
soft o blando y límite
hard o duro. El límite blando de un recurso puede ser modificado por cualquier proceso sin privilegios que llame a
setrlimit(), ya sea para aumentar o para disminuir su valor; por el contrario, el límite
hard define un valor máximo para la utilización de un recurso, y sólo puede ser sobrepasado por procesos que se ejecuten con privilegios de administrador.
En el fichero
include/linux/nfs.h podemos definir el puerto máximo que los clientes NFS pueden utilizar (NFS/SMALL>_PORT); si le asignamos un valor inferior a 1024 (puertos privilegiados), sólo el administrador de otro sistema Unix podrá utilizar nuestros servicios NFS, de forma similar a la variable
nfs_portmon de algunos Unices.
Para cambiar los límites de los parámetros vistos aquí la solución más rápida pasa por modificar los ficheros de cabecera del
kernel, recompilarlo y arrancar la máquina con el nuevo núcleo; sin embargo, a continuación vamos a hablar brevemente de
Fork Bomb Defuser, un módulo que permite al administrador modificar algunos de estos parámetros sin reiniciar el sistema. Más adelante hablaremos también de los límites a recursos ofrecidos por PAM (
Pluggable Authentication Modules), un sistema de autenticación incorporado en la actualidad a la mayoría de Linux, así como en otros Unices.
Fork Bomb Defuser
El
kernel de Linux no permite por defecto limitar el número máximo de usuarios y el número máximo de procesos por usuario que se pueden ejecutar en el sistema sin tener que modificar el código del núcleo; si no queremos modificarlo, casi no hay más remedio que utilizar un poco de programación (unos simples
shellscripts suelen ser suficientes) y las herramientas de planificación de tareas para evitar que un usuario lance demasiados procesos o que conecte cuando el sistema ya ha sobrepasado un cierto umbral de usuarios conectados a él.
Mediante el módulo
Fork Bomb Defuser se permite al administrador controlar todos estos parámetros del sistema operativo, incrementando de forma flexible la seguridad de la máquina. El código está disponible en
http://rexgrep.tripod.com/rexfbdmain.htm##∞.
Secure Linux
Por Secure Linux se conoce a una colección de parches para el núcleo de Linux programados por Solar Designer, uno de los hackers más reconocidos a nivel mundial en la actualidad (entendiendo hacker en el buen - y único - sentido de la palabra). Este software, disponible libremente desde http://www.false.com/security/linux/##11.2∞, incrementa la seguridad que el núcleo proporciona por defecto, ofreciendo cuatro importantes diferencias con respecto a un
kernel normal:
- Área de pila no ejecutable
En un sistema con el área de la pila no ejecutable los ataques de buffer overflow son más difíciles de realizar que en los sistemas habituales, ya que muchos de estos ataques se basan en sobreescribir la dirección de retorno de una función en la pila para que apunte a código malicioso, también depositado en la pila. Aunque Secure Linux no es una solución completa, sí que añade un nivel extra de seguridad en este sentido, haciendo que un atacante que pretenda utilizar un buffer overflow contra nuestro sistema tenga que utilizar código más complicado para hacerlo.
- Enlaces restringidos en /tmp
Con esta característica, Secure Linux intenta que los usuarios sin privilegios puedan crear enlaces en /tmp/ sobre ficheros que no les pertenecen, eliminando así ciertos problemas de seguridad que afectan a algunos sistemas Linux, relacionados principalmente con condiciones de carrera en el acceso a ficheros.
- Tuberías restringidas en /tmp
Esta opción no permite a los usuarios escribir en tuberías (fifos) que no le pertenezcan a él o al root en directorios con el bit de permanencia activo, como /tmp. De esta forma se evitan ciertos ataques de Data Spoofing.
- /proc restringido
Esta es quizás la característica más útil de este parche, aparte de la más visible para el usuario normal. Permite que los usuarios no tengan un acceso completo al directorio /proc/ (que recordemos permite un acceso a estructuras de datos del núcleo, como la tabla de procesos, desde el espacio de usuario) a no ser que se encuentren en un determinado grupo con el nivel de privilegio suficiente. De esta forma se consigue un aumento espectacular en la privacidad del sistema, ya que por ejemplo los usuarios sólo podrán ver sus procesos al ejecutar un ps aux, y tampoco tendrán acceso al estado de las conexiones de red vía netstat; así, órdenes como ps o top sólo muestran información relativa a los procesos de quién las ejecuta, a no ser que esta persona sea el administrador o un usuario perteneciente al grupo 0.