Lamentablemente, muchos administradores de equipos Unix no disponen de los conocimientos, del tiempo, o simplemente del interés necesario para conseguir sistemas mínimamente fiables. A raíz de esto, las máquinas Unix se convierten en una puerta abierta a cualquier ataque, poniendo en peligro no sólo la integridad del equipo, sino de toda su subred y a la larga de toda Internet.
Aunque esta situación se da en cualquier tipo de organización, es en las dedicadas a I+D donde se encuentran los casos más extremos; se trata de redes y equipos Unix muy abiertos y con un elevado número de usuarios (incluidos externos al perímetro físico de la organización) que precisan de una gran disponibilidad de los datos, primando este aspecto de la información ante otros como la integridad o la privacidad. Esto convierte a los sistemas Unix de centros de I+D, especialmente de universidades, en un objetivo demasiado fácil incluso para los piratas menos experimentados.
Con el objetivo de subsanar esta situación, aquí se van a intentar marcar unas pautas para conseguir un nivel
mínimo de fiabilidad en los equipos Unix. No se va a entrar en detalles muy técnicos o en desarrollos teóricos sobre seguridad que muy pocos van a leer (para eso está el resto de este proyecto), sino que la idea es únicamente explicar los pasos básicos para que incluso los administradores menos preocupados por la seguridad puedan aplicarlos en sus sistemas. A modo de ilustración, hay pequeños ejemplos que han sido realizados sobre una plataforma Solaris 7 (SunOS 5.7); en otros clones de Unix quizás sea necesario modificar las opciones de algún comando o la localización de ciertos ficheros.
Hay que recalcar que se trata de mecanismos
básicos de seguridad, que pueden evitar la acción de algunos piratas casuales (si nuestra máquina ofrece una mínima protección abandonarán el ataque para dedicarse a equipos menos protegidos) pero no de un atacante con cierta experiencia. Lo ideal sería que las pautas marcadas aquí se complementaran con todas las medidas de seguridad posibles, y que entre los libros habituales de un administrador se encontraran títulos sobre seguridad en Unix; uno especialmente recomendado es
Practical Unix & Internet Security, de Simson Garfinkel y Gene Spafford (Ed. O´Reilly and Associates, 1996). También es muy recomendable que la persona encargada de la seguridad de cada equipo permanezca atenta a los nuevos problemas que cada día surgen; una buena forma de conseguirlo es mediante listas de correo como BUGTRAQ.
Prevención
Los mecanismos de prevención han de ser los más importantes para cualquier administrador, ya que obviamente es mucho mejor evitar un ataque que detectar ese mismo problema o tener que recuperar al sistema tras detectarlo.
- Cierre de servicios ofrecidos por inetd
Cada servicio ofrecido en nuestro sistema se convierte en una potencial puerta de acceso al mismo, por lo que hemos de minimizar su número: se recomienda cerrar cualquier servicio que no se vaya a utilizar, y todos aquellos de los que no conozcamos su utilidad (si más tarde son necesarios, los podemos volver a abrir).
Para cerrar un servicio ofrecido desde inetd, en el fichero /etc/inetd.conf debemos comentar la línea correspondiente a ese servicio, de forma que una entrada como telnet stream tcp nowait root /usr/sbin/in.telnetd
se convierta en una como #telnet stream tcp nowait root /usr/sbin/in.telnetd
Tras efectuar esta operación, debemos reiniciar el demonio inetd para que relea su configuración; esto lo conseguimos, por ejemplo, con la ordenanita:/# pkill -HUP inetd
o, si no disponemos de un comando para enviar señales a procesos a partir de su nombre, con la ordenanita:/# kill -HUP `ps -ef|grep -w inetd|awk '{print $2}'`
- Cierre de servicios ofrecidos en el arranque de máquina
Existen una serie de demonios que ofrecen ciertos servicios, como sendmail, que no se procesan a través de inetd sino que se lanzan como procesos independientes al arrancar la máquina. Para detener este tipo de demonios hemos de comentar las líneas de nuestros ficheros de arranque encargadas de lanzarlos (generalmente en directorios como /etc/rc?.d/ o /etc/rc.d/): de esta forma conseguimos que la próxima vez que el sistema se inicie, los demonios no se ejecuten. Aparte de esto, hemos de detener los demonios en la sesión actual, ya que en estos momentos seguramente están funcionando; para ello les enviamos la señal SIGKILL mediante el comando kill.
Por ejemplo, en el caso de Solaris, sendmail se lanza desde el archivo
/etc/rc2.d/S88sendmail; en este fichero tendremos unas líneas similares a estas:if [ -f /usr/lib/sendmail -a -f /etc/mail/sendmail.cf ]; then
if [ ! -d /var/spool/mqueue ]; then
/usr/bin/mkdir -m 0750 /var/spool/mqueue
/usr/bin/chown root:bin /var/spool/mqueue
fi
/usr/lib/sendmail -bd -q15m &
fi
Podemos renombrar este archivo como disabled.S88sendmail o comentar estas líneas de la forma siguiente:#if [ -f /usr/lib/sendmail -a -f /etc/mail/sendmail.cf ]; then
# if [ ! -d /var/spool/mqueue ]; then
# /usr/bin/mkdir -m 0750 /var/spool/mqueue
# /usr/bin/chown root:bin /var/spool/mqueue
# fi
# /usr/lib/sendmail -bd -q15m &
#fi
Y a continuación eliminaremos el proceso sendmail enviándole la señal SIGKILL:anita:/# ps -ef |grep sendmail
root 215 1 0 01:00:38 ? 0:00 /usr/lib/sendmail -bd -q15m
anita:/# kill -9 215
- Instalación de wrappers
A pesar de haber cerrado muchos servicios siguiendo los puntos anteriores, existen algunos que no podremos dejar de ofrecer, como telnet o ftp, ya que los usuarios van a necesitar conectar al sistema de forma remota o transferir ficheros. En estos casos es muy conveniente instalar wrappers para los demonios que sigan recibiendo conexiones; mediante el uso de estos programas vamos a poder restringir los lugares desde los que nuestro equipo va a aceptar peticiones de servicio. Especialmente recomendable es el programa TCP-Wrapper para controlar las conexiones servidas por inetd (incluso sendmail se puede controlar por inetd, lo cual es muy útil si queremos restringir los lugares desde los que nos pueda llegar correo).
Por ejemplo, si no utilizamos wrappers para controlar el servicio de telnet, cualquier máquina de Internet puede intentar el acceso a nuestro sistema:luisa:~$ telnet anita
Trying 192.168.0.3...
Connected to anita.
Escape character is '^]'.
SunOS 5.7
login:
Sin embargo, configurando TCP-Wrapper para que no admita conexiones desde fuera de la universidad, si alguien intenta lo mismo obtendrá un resultado similar al siguiente:luisa:~$ telnet anita
Trying 192.168.0.3...
Connected to anita.
Escape character is '^]'.
Connection closed by foreign host.
luisa:~$
De esta forma, incluso si el atacante conociera un nombre de usuario y su clave le sería más difícil acceder a nuestro equipo por telnet.
- Ficheros setuidados y setgidados
En un sistema Unix recién instalado podemos tener incluso más de cincuenta ficheros con los modos setuid o setgid activados; cualquiera de estos programas representa un potencial agujero a la seguridad de nuestro sistema, y aunque muchos son necesarios (como /bin/passwd en la mayoría de situaciones), de otros se puede prescindir. Para localizar los ficheros setuidados podemos utilizar la ordenanita:/# find / -perm -4000 -type f -print
mientras que para localizar los setgidados podemos utilizar anita:/# find / -perm -2000 -type f -print
Es conveniente que reduzcamos al mínimo el número de estos archivos, pero tampoco se recomienda borrarlos del sistema de ficheros; es mucho más habitual resetear el bit de setuid o setgid, y en caso de que sea necesario volverlo a activar. Para desactivar estos bits podemos usar la orden chmod -s, mientras que para activarlos utilizaremos chmod u+s o chmod g+s.
Por ejemplo, si el fichero /usr/lib/fs/ufs/ufsdump está setuidado, un listado largo del mismo nos mostrará una s en el campo de ejecución para propietario, mientras que si está setgidado aparecerá una s en el campo de ejecución para grupo; podemos resetear los dos bits con la orden vista anteriormente:anita:/# ls -l /usr/lib/fs/ufs/ufsdump
-r-sr-sr-x 1 root tty 144608 Oct 6 1998 /usr/lib/fs/ufs/ufsdump
anita:/# chmod -s /usr/lib/fs/ufs/ufsdump
anita:/# ls -l /usr/lib/fs/ufs/ufsdump
-r-xr-xr-x 1 root tty 144608 Oct 6 1998 /usr/lib/fs/ufs/ufsdump
- Cifrado de datos
El principal problema de las claves viajando en texto claro por la red es que cualquier atacante puede leerlas: si usamos telnet, rlogin o ftp, cualquier persona situada entre nuestra estación de trabajo y el servidor al que conectamos puede `esnifar' los paquetes que circulan por la red y obtener así nuestro nombre de usuario y nuestro password. Para evitar este problema es conveniente utilizar software que implemente protocolos cifrados para conectar; el más habitual hoy en día es SSH (Secure Shell). Por una parte, tenemos el programa servidor sshd, que se ha de instalar en el equipo al que conectamos, y por otra programas clientes (ssh para sustituir a rsh/rlogin y scp para sustituir a rcp).
Una vez instalado, este software es transparente al usuario: simplemente ha de recordar su clave, igual que si conectara por telnet o rlogin.
- Relaciones de confianza
En el fichero /etc/hosts.equiv se indican, una en cada línea, las máquinas confiables. >Qué significa confiables? Básicamente que confiamos en su seguridad tanto como en la nuestra, por lo que para facilitar la compartición de recursos, no se van a pedir contraseñas a los usuarios que quieran conectar desde estas máquinas con el mismo login, utilizando las órdenes BSD r (rlogin, rsh, rcp...). Por ejemplo, si en el fichero /etc/hosts.equiv del servidor anita hay una entrada para el nombre de host luisa, cualquier usuarioA.1 de este sistema puede ejecutar una orden como la siguiente para conectar a anita <sin necesidad de ninguna clave!:luisa:~$ rlogin anita
Last login: Sun Oct 31 08:27:54 from localhost
Sun Microsystems Inc. SunOS 5.7 Generic October 1998
anita:~$
Obviamente, esto supone un gran problema de seguridad, por lo que lo más recomendable es que el fichero /etc/hosts.equiv esté vacío o no exista. De la misma forma, los usuarios pueden crear ficheros $HOME/.rhosts para establecer un mecanismo de confiabilidad bastante similar al de /etc/hosts.equiv; es importante para la seguridad de nuestro sistema el controlar la existencia y el contenido de estos archivos .rhosts. Por ejemplo, podemos aprovechar las facilidades de planificación de tareas de Unix para, cada cierto tiempo, chequear los directorios $HOME de los usuarios en busca de estos ficheros, eliminándolos si los encontramos. Un shellscript que hace esto puede ser el siguiente:#!/bin/sh
for i in `cat /etc/passwd |awk -F: '{print $6}'`; do
cd $i
if [ -f .rhosts ]; then
echo "$i/.rhosts detectado"|mail -s "rhosts" root
rm -f $i/.rhosts
fi
done
Este programa envía un correo al root en caso de encontrar un fichero .rhosts, y lo elimina; podemos planificarlo mediante cron para que se ejecute, por ejemplo, cada cinco minutos. La forma de planificarlo depende del clon de Unix en el que trabajemos, por lo que se recomienda consultar la página del manual de cron o crond; en el caso de Solaris, para que se ejecute cada vez que cron despierte, y suponiendo que el script se llame /usr/local/sbin/busca, pondríamos en nuestro crontab (con crontab -e) una línea como* * * * * /usr/local/sbin/busca 2>&1 >/dev/null
Hemos de estar atentos a la carga que este tipo de actividades periódicas puede introducir en el sistema; la orden anterior se va a ejecutar cada vez que cron despierta, generalmente una vez por minuto, lo que implica que en máquinas con un gran número de usuarios puede introducir un factor importante de operaciones de I/O. Una solución más adecuada en estas situaciones sería planificar el programa para que se ejecute cada cinco o diez minutos, o el tiempo que estimemos necesario en nuestro equipo.
- Política de cuentas
Muchos clones de Unix se instalan con cuentas consideradas `del sistema', es decir, que no corresponden a ningún usuario concreto sino que existen por cuestiones de compatibilidad o para la correcta ejecución de algunos programas. Algunas de estas cuentas no tienen contraseña, o tienen una conocida por todo el mundo, por lo que representan una grave amenaza a la seguridad: hemos de deshabilitarlas para evitar que alguien pueda conectar a nuestro equipo mediante ellas. Algunos ejemplos de este tipo de cuentas son guest, demo, uucp, games, 4DGifts o lp.
Para deshabilitar una cuenta, en Unix no tenemos más que insertar un asterisco en el campo passwd en la línea correspondiente del fichero de claves (generalmente /etc/passwd o
/etc/shadow). De esta forma, una entrada como toni:7atzxSJlPVVaQ:1001:10:Toni Villalon:/export/home/toni:/bin/sh
pasaría a convertirse en toni:*7atzxSJlPVVaQ:1001:10:Toni Villalon:/export/home/toni:/bin/sh
Aparte de este tipo de cuentas, hemos de tener un especial cuidado con las cuentas de usuario que no tienen contraseña o que tienen una clave débil; para detectar este último problema podemos utilizar programas adivinadores como Crack, mientras que para evitarlo podemos utilizar NPasswd o Passwd+, además de sistemas Shadow Password para que los usuarios no puedan leer las claves cifradas. Para detectar cuentas sin contraseña (aunque también Crack nos las indicará), podemos utilizar la siguiente orden, obviamente sustituyendo /etc/passwd por el fichero de claves de nuestro sistema:anita:~# awk -F: '$2" " {print $1}' /etc/passwd
Por último, hay que decir que una correcta política de cuentas pasa por deshabilitar la entrada de usuarios que no utilicen el sistema en un tiempo prudencial; por ejemplo, podemos cancelar las cuentas de usuarios que no hayan conectado a la máquina en los últimos dos meses, ya que son firmes candidatas a que un pirata las aproveche para atacarnos; la orden finger nos puede ayudar a detectar este tipo de usuarios.
- Negaciones de servicio
Un tipo de ataque que ni siquiera suele necesitar de un acceso al sistema es el conocido como la negación de servicio (Denial of Service). Consiste básicamente en perjudicar total o parcialmente la disponibilidad de un recurso, por ejemplo utilizando grandes cantidades de CPU, ocupando toda la memoria del sistema o incluso deteniendo una máquina. Obviamente las negaciones de servicio más peligrosas son las que detienen el sistema o alguno de sus servicios de forma remota:
Las paradas de máquina son, por norma general, fruto de un fallo en la implementación de red del núcleo: por ejemplo, la llegada de un paquete con una cabecera extraña, de una longitud determinada, o con una cierta prioridad, puede llegar a detener la máquina si ese paquete no se trata correctamente en la implementación del sistema de red. La mejor forma de prevenir estos ataques (que no suelen dejar ningún rastro en los ficheros de log) es actualizar el núcleo de nuestro Unix periódicamente, manteniendo siempre la última versión estable.
En el caso de la detención de servicios determinados, habitualmente los ofrecidos desde inetd, la negación de servicio suele ser fruto de un excesivo número de peticiones `falsas' al demonio correspondiente; por ejemplo, un atacante enmascara su dirección IP para sobrecargar de peticiones un demonio como in.telnetd hasta detenerlo. Para evitar estos ataques podemos incrementar el número de peticiones simultáneas que un demonio acepta (en la opción wait de la línea correspondiente en el fichero /etc/inetd.conf), aunque esto también implica peligros de negación de servicio (puede aumentar demasiado el tiempo de respuesta del equipo); una forma mucho más recomendable de actuar no es prevenir estos ataques sino minimizar sus efectos: si enviamos una señal SIGHUP al demonio inetd éste relee su configuración, por lo que el servicio bloqueado vuelve a funcionarA.2. Por tanto, es recomendable enviar una de estas señales de forma automática cada cierto tiempo; podemos planificar esta acción para que cron la ejecute cada vez que despierte, incluyendo en nuestro archivo crontab una línea como la siguiente:* * * * * /usr/bin/pkill -HUP inetd 2>&1 >/dev/null