Seguridad en Unix y Redes
Libro en Formato HTML y PDF de seguridad informática realizado por Antonio Villalon Huerta

Los contenidos pueden estar desactualizados con respecto al original

Este documento se encuentra disponible también en formato PDF

El sistema de ficheros


next up previous contents
Siguiente: Programas seguros, inseguros y Subir: Seguridad del sistema Anterior: Seguridad del sistema   Índice General

Subsecciones

El sistema de ficheros

Introducción

Dentro del sistema Unix todo son archivos: desde la memoria física del equipo hasta el ratón, pasando por módems, teclado, impresoras o terminales. Esta filosofía de diseño es uno de los factores que más éxito y potencia proporciona a Unix ([KP84]), pero también uno de los que más peligros entraña: un simple error en un permiso puede permitir a un usuario modificar todo un disco duro o leer los datos tecleados desde una terminal. Por esto, una correcta utilización de los permisos, atributos y otros controles sobre los ficheros es vital para la seguridad de un sistema.

En un sistema Unix típico existen tres tipos básicos de archivos: ficheros planos, directorios, y ficheros especiales (dispositivos) 5.1; generalmente, al hablar de ficheros nos solemos referir a todos ellos si no se especifica lo contrario. Los ficheros planos son secuencias de bytes que a priori no poseen ni estructura interna ni contenido significante para el sistema: su significado depende de las aplicaciones que interpretan su contenido. Los directorios son archivos cuyo contenido son otros ficheros de cualquier tipo (planos, más directorios, o ficheros especiales), y los ficheros especiales son ficheros que representan dispositivos del sistema; este último tipo se divide en dos grupos: los dispositivos orientados a carácter y los orientados a bloque. La principal diferencia entre ambos es la forma de realizar operaciones de entrada/salida: mientras que los dispositivos orientados a carácter las realizan byte a byte (esto es, carácter a carácter), los orientados a bloque las realizan en bloques de caracteres.

El sistema de ficheros es la parte del núcleo más visible por los usuarios; se encarga de abstraer propiedades físicas de diferentes dispositivos para proporcionar una interfaz única de almacenamiento: el archivo. Cada sistema Unix tiene su sistema de archivos nativo (por ejemplo, ext2 en Linux, UFS en Solaris o EFS en IRIX), por lo que para acceder a todos ellos de la misma forma el núcleo de Unix incorpora una capa superior denominada VFS (Virtual File System) encargada de proporcionar un acceso uniforme a diferentes tipos de sistema de ficheros.

Un inodo o nodo índice es una estructura de datos que relaciona un grupo de bloques de un dispositivo con un determinado nombre del sistema de ficheros. Internamente, el núcleo de Unix no distingue a sus archivos por su nombre sino por un número de inodo; de esta forma, el fichero con número de inodo 23421 será el mismo tanto si se denomina /etc/passwd como si se denomina /usr/fichero. Mediante la orden ln(1) se pueden asignar a un mismo inodo varios nombres de fichero diferentes en el sistema de archivos.

Sistemas de ficheros

Cuando un sistema Unix arranca una de las tareas que obligatoriamente ha de realizar es incorporar diferentes sistemas de ficheros - discos completos, una partición, una unidad de CD-ROM...- a la jerarquía de directorios Unix; este proceso se llama montaje, y para realizarlo generalmente se utiliza la orden mount. Es obligatorio montar al menos un sistema de ficheros durante el arranque, el sistema raíz (`/'), del que colgarán todos los demás.

Montar un sistema de ficheros no significa más que asociar un determinado nombre de directorio, denominado mount point o punto de montaje, con el sistema en cuestión, de forma que al utilizar dicha ruta estaremos trabajando sobre el sistema de ficheros que hemos asociado a ella. Para saber qué sistemas de ficheros se han de montar en el arranque de la máquina, y bajo qué nombre de directorio, Unix utiliza un determinado archivo; aunque su nombre depende del clon utilizado (/etc/vfstab en Solaris, /etc/fstab en Linux...), su función - e incluso su sintaxis - es siempre equivalente. Un ejemplo de este fichero es el siguiente:
luisa:~# cat /etc/fstab
/dev/hda3       /        ext2        defaults   1   1
/dev/hda4       /home    ext2        defaults   1   2
none            /proc    proc        defaults   1   1
luisa:~#
Cuando el sistema arranque, el fichero anterior viene a indicar que en /dev/hda3 se encuentra el sistema de ficheros raíz, de tipo ext2 (el habitual en Linux), y que se ha de montar con las opciones que se toman por defecto. La segunda línea nos dice que /home es un sistema diferente del anterior, pero del mismo tipo y que se montará con las mismas opciones; finalmente, la última entrada hace referencia al directorio /proc/, donde se encuentra un sistema de ficheros especial que algunos Unices utilizan como interfaz entre estructuras de datos del núcleo y el espacio de usuario (no entraremos en detalles con él). Si cualquiera de las entradas anteriores fuera errónea, el sistema o bien no arrancaría o bien lo haría incorrectamente. Por lo que evidentemente el fichero /etc/fstab o sus equivalentes ha de ser sólo modificable por el root, aunque nos puede interesar - como veremos luego - que los usuarios sin privilegios puedan leerlo.

Lo visto hasta aquí no suele representar ningún problema de seguridad en Unix; si hemos dicho que no hablaríamos de aspectos generales de los sistemas de ficheros, >por qué comentamos este aspecto? Muy sencillo: diferentes problemas radican en una gestión incorrecta del montaje de sistemas de ficheros. Por ejemplo, algo muy habitual en un atacante que consigue privilegios de administrador en una máquina es instalar ciertas utilidades que le permitan seguir gozando de ese privilegio (por ejemplo, un rootkit o un simple shell setuidado); si guarda el fichero setuidado - hablaremos más tarde de estos permisos `especiales' - en cualquier directorio de nuestro sistema, su localización será muy rápida: una orden tan simple como find nos alertará de su presencia. En cambio, >qué sucede si el atacante utiliza una parte del sistema de ficheros oculta? Cuando montamos un sistema bajo un nombre de directorio, todo lo que había en ese directorio desaparece de la vista, y es sustituido por el contenido del sistema montado; no volverá a estar accesible hasta que no desmontemos el sistema:
luisa:~# mount
/dev/hda3 on / type ext2 (rw)
/dev/hda4 on /home type ext2 (rw)
none on /proc type proc (rw)
luisa:~# ls /home/
ftp/   toni/    lost+found/ 
luisa:~# umount /home
luisa:~# ls /home/
luisa:~#
El atacante puede desmontar una parte de nuestra jerarquía de directorios, guardar ahí ciertos ficheros, y volver a montar el sistema que había anteriormente; localizar esos archivos puede ser complicado, no por motivos técnicos sino porque a muy poca gente se le ocurre hacerlo. La orden ncheck, existente en Unices antiguos, puede detectar estos ficheros ocultos bajo un mount point; si no disponemos de esta utilidad podemos buscar por Internet aplicaciones que consiguen lo mismo, o simplemente desmontar manualmente los sistemas (a excepción del raíz) y comprobar que no hay nada oculto bajo ellos.

El tema de desmontar sistemas de ficheros también puede ocasionar algún dolor de cabeza a muchos administradores; aunque no se trata de algo estrictamente relativo a la seguridad, vamos a comentar un problema típico que se podría considerar incluso una negación de servicio (no causada por un fallo de Unix sino por el desconocimiento del administrador). En ocasiones, al intentar desmontar un sistema de ficheros, encontramos el siguiente resultado:
luisa:~# umount /home/
umount: /home: device is busy
luisa:~#
>Qué sucede? Simplemente que existe un determinado proceso haciendo uso de recursos bajo ese nombre de directorio. Hasta que dicho proceso no finalice (por las buenas o por las malas), será imposible desmontar el sistema; es fácil determinar de qué proceso se trata - y posteriormente eliminarlo - mediante la orden fuser.

Otro problema clásico de los sistemas de ficheros viene de la necesidad que en muchos entornos existe de permitir a los usuarios - sin privilegios - montar y desmontar sistemas de ficheros (típicamente, discos flexibles o CD-ROMs). Por ejemplo, imaginemos un laboratorio de máquinas Unix donde es deseable que todos los usuarios puedan acceder a la disquetera, tanto para copiar prácticas realizadas en casa como para hacer una copia de las que se han hecho en el propio laboratorio (este es uno de los casos más frecuentes en cualquier organización). Unix permite dar una solución rápida a este problema, pero esta solución puede convertirse en una amenaza a la seguridad si no es implantada correctamente:

Al hablar de /etc/fstab hemos comentado el montaje con ciertas opciones tomadas por defecto; dichas opciones son - en el caso de Linux, consultar la página del manual de mount en otros sistemas - `rw' (se permite tanto la lectura como la escritura), `suid' (se permite la existencia de ficheros setuidados), `dev' (se permite la existencia de dispositivos), `exec' (se permite la ejecución de binarios), `auto' (el sistema se monta automáticamente al arrancar o al utilizar mount -a), `nouser' (sólo puede ser montado por el root) y `async' (la entrada/salida sobre el dispositivo se realiza de forma asíncrona). Evidentemente, se trata de las opciones más lógicas para sistemas de ficheros `normales', pero no para los que puedan montar los usuarios; si deseamos que un usuario sin privilegios pueda montar y desmontar cierto dispositivo, hemos de especificar la opción `user' en la entrada correspondiente de /etc/fstab. Parece lógico también utilizar `noauto' para que el sistema no se monte automáticamente en el arranque de la máquina (si esto sucediera, el root tendría que desmontar la unidad manualmente para que otros usuarios puedan montarla), pero otras opciones importantes no son tan inmediatas. Es imprescindible que si permitimos a un usuario montar una unidad utilicemos `nodev', de forma que si en el sistema montado existen ficheros de tipo dispositivo (por ejemplo, un archivo que haga referencia a nuestros discos duros) ese fichero sea ignorado; en caso contrario, cualquiera podría acceder directamente a nuestro hardware, por ejemplo para destruir completamente los discos duros o bloquear toda la máquina. También es importante especificar `nosuid', de forma que se ignore el bit de setuid en cualquier fichero contenido en el sistema que el usuario monta: así evitamos que con un simple shell setuidado en un disco flexible el usuario consiga privilegios de administrador en nuestro sistema. Incluso puede ser conveniente especificar `noexec', de forma que no se pueda ejecutar nada de lo que está en el dispositivo montado - esto parece lógico, ya que en principio se va a tratar de una unidad utilizada simplemente para transferir datos entre la máquina y un sistema externo a la red, como el ordenador de casa de un alumno -. Todas estas opciones (`noexec', `nosuid' y `nodev') en Linux se asumen simplemente al indicar `user', pero en otros sistemas Unix quizás no, por lo que nunca está de más ponerlas explícitamente (o al menos consultar el manual en otros clones de Unix para asegurarse del efecto de cada opción); de esta forma, si queremos que los usuarios puedan montar por ejemplo la disquetera, una entrada correcta en /etc/fstab sería la siguiente:
luisa:~# grep fd0 /etc/fstab
/dev/fd0    /floppy     ext2        user,noauto,nodev,nosuid,noexec
luisa:~#
Otro aspecto relacionado con el montaje de sistemas de ficheros que puede afectar a nuestra seguridad es el uso de sistemas de ficheros diferentes del raíz bajo ciertos directorios; una elección incorrecta a la hora de elegir dónde montar sistemas puede causar ciertos problemas, sobre todo negaciones de servicio. Generalmente, es recomendable montar dispositivos diferentes bajo todos y cada uno de los directorios sobre los que los usuarios tienen permiso de escritura; esto incluye el padre de sus $HOME, /tmp/ o /var/tmp/ (que puede ser un simple enlace a /tmp/). Con esto conseguimos que si un usuario llena un disco, esto no afecte al resto del sistema: un disco lleno implica muchos problemas para la máquina, desde correo electrónico que no se recibe, logs que no se registran, o simplemente una negación de servicio contra el resto de usuarios, que no podrán almacenar nada. Aunque algunos Unices reservan una parte de cada disco o partición para escritura sólo del root o de procesos que corran bajo el UID 0 - típicamente un 10% de su capacidad total -, no podemos confiar ciegamente en este mecanismo para mantener segura nuestra máquina; así, una configuración más o menos adecuada sería la siguiente5.2:
rosita:~# mount
/dev/hda1 on / type ext2 (rw)
/dev/hda2 on /tmp type ext2 (rw)
/dev/hdb1 on /home type ext2 (rw)
none on /proc type proc (rw)
rosita:~#
Como podemos comprobar, si un usuario lanza un ftp en background desde su $HOME durante la noche - típico proceso que llena gran cantidad de disco -, en todo caso podrá afectar al resto de usuarios, pero nunca al sistema en global (correo, logs, root...); este tipo de problemas no suelen ser ataques, sino más bien descuidos de los usuarios que no tienen en cuenta el espacio disponible antes de descargar ficheros de forma no interactiva. Si queremos que ni siquiera pueda afectar al resto de usuarios, podemos establecer un sistema de quotas de disco en nuestra máquina.

Permisos de un archivo

Los permisos de cada fichero son la protección más básica de estos objetos del sistema operativo; definen quién puede acceder a cada uno de ellos, y de qué forma puede hacerlo. Cuando hacemos un listado largo de ciertos archivos podemos ver sus permisos junto al tipo de fichero correspondiente, en la primera columna de cada línea:
anita:~# ls -l /sbin/rc0
-rwxr--r--   3 root     sys         2689 Dec  1  1998 /sbin/rc0
anita:~#
En este caso vemos que el archivo listado es un fichero plano (el primer carácter es un `-') y sus permisos son `rwxr-r-'. >Cómo interpretar estos caracteres? Los permisos se dividen en tres ternas en función de a qué usuarios afectan; cada una de ellas indica la existencia o la ausencia de permiso para leer, escribir o ejecutar el fichero: una r indica un permiso de lectura, una w de escritura, una x de ejecución y un `-' indica que el permiso correspondiente no está activado. Así, si en una de las ternas tenemos los caracteres rwx, el usuario o usuarios afectados por esa terna tiene o tienen permisos para realizar cualquier operación sobre el fichero. >De qué usuarios se trata en cada caso? La primera terna afecta al propietario del fichero, la segunda al grupo del propietario cuando lo creó (recordemos un mismo usuario puede pertenecer a varios grupos) y la tercera al resto de usuarios. De esta forma, volviendo al ejemplo anterior, tenemos los permisos mostrados en la figura 4.1.
Figura 4.1: Permisos de un fichero

Cuando un usuario5.3 intenta acceder en algún modo a un archivo, el sistema comprueba qué terna de permisos es la aplicable y se basa únicamente en ella para conceder o denegar el acceso; así, si un usuario es el propietario del fichero sólo se comprueban permisos de la primera terna; si no, se pasa a la segunda y se aplica en caso de que los grupos coincidan, y de no ser así se aplican los permisos de la última terna. De esta forma es posible tener situaciones tan curiosas como la de un usuario que no tenga ningún permiso sobre uno de sus archivos, y en cambio que el resto de usuarios del sistema pueda leerlo, ejecutarlo o incluso borrarlo; obviamente, esto no es lo habitual, y de suceder el propietario siempre podrá restaurar los permisos a un valor adecuado.

El propietario y el grupo de un fichero se pueden modificar con las órdenes chown y chgrp respectivamente; ambas reciben como parámetros al menos el nombre de usuario o grupo (los nombres válidos de usuario son los que poseen una entrada en /etc/passwd mientras que los grupos válidos se leen de /etc/group) al que vamos a otorgar la posesión del fichero, así como el nombre de archivo a modificar:
anita:~# ls -l /tmp/fichero 
-rw-r--r--   1 root     other          799 Feb  8 19:47 /tmp/fichero
anita:~# chown toni /tmp/fichero
anita:~# ls -l /tmp/fichero 
-rw-r--r--   1 toni     other          799 Feb  8 19:47 /tmp/fichero
anita:~# chgrp staff /tmp/fichero
anita:~# ls -l /tmp/fichero 
-rw-r--r--   1 toni     staff          799 Feb  8 19:47 /tmp/fichero
anita:~#
En muchas variantes de Unix es posible cambiar a la vez el propietario y el grupo de un fichero mediante chown, separando ambos mediante un carácter especial, generalmente `:' o `.':
anita:~# ls -l /tmp/fichero 
-rw-r--r--   1 root     other          799 Feb  8 19:47 /tmp/fichero
anita:~# chown toni:staff /tmp/fichero
anita:~# ls -l /tmp/fichero 
-rw-r--r--   1 toni     staff          799 Feb  8 19:47 /tmp/fichero
anita:~#
Como vemos, ninguna de estas órdenes altera el campo de permisos5.4; para modificar los permisos de un archivo se utiliza la orden chmod. Este comando generalmente recibe como parámetro el permiso en octal que queremos asignar a cierto fichero, así como el nombre del mismo:
anita:~# ls -l /tmp/fichero 
-rw-r--r--   1 root     staff          799 Feb  8 19:47 /tmp/fichero
anita:~# chmod 755 /tmp/fichero
anita:~# ls -l /tmp/fichero 
-rwxr-xr-x   1 root     staff          799 Feb  8 19:47 /tmp/fichero
anita:~#
>Cómo podemos obtener el número en octal a partir de una terna de permisos determinada, y viceversa? Evidentemente no podemos entrar aquí a tratar todas las características de este sistema de numeración, pero vamos a proporcionar unas ideas básicas. Imaginemos que tenemos un fichero con unos determinados permisos de los que queremos calcular su equivalente octal, o que conocemos los permisos a asignar pero no su equivalente numérico; por ejemplo, necesitamos asignar a un fichero la terna rw-r--wx, que en la práctica no tiene mucho sentido pero que a nosotros nos sirve de ejemplo. Lo primero que debemos hacer a partir de estos bits rwx es calcular su equivalente binario, para lo que asignamos el valor `1' si un determinado permiso está activo (es decir, si aparece una r, w o x en él) y un `0' si no lo está (aparece un `-'); así, el equivalente binario de la terna propuesta es 110100011. Ahora simplemente hemos de pasar el número del sistema binario al octal: lo dividimos en grupos de tres elementos (110 100 011) y de cada uno de ellos calculamos su equivalente octal:


Ya tenemos los tres números de nuestra terna de permisos, o lo que es lo mismo, la representación octal de los bits iniciales: 643. Por tanto, si necesitamos asignar esta terna a un cierto fichero, simplemente hemos de ejecutar la orden chmod indicándole este número y el nombre del archivo:
anita:~# chmod 643 /tmp/fichero
anita:~# ls -l /tmp/fichero 
-rw-r---wx   1 root     root          799 Feb  8 19:47 /tmp/fichero*
anita:~#
La forma de trabajar de chmod comentada requiere que se indique explícitamente el valor octal de los bits rwx que queremos otorgar a un fichero; sin importar el valor de las ternas que poseía antes de ejecutar la orden, estamos asignando a los permisos del archivo el nuevo valor valor indicado en la línea de comandos. Existe otra forma de trabajo de chmod denominada `simbólica' en la que no necesitamos indicar el valor octal de todos los bits, sino que especificamos únicamente parámetros para los valores de los permisos que el archivo posee y deseamos modificar. En lugar de utilizar el equivalente octal, utilizamos símbolos (de ahí el nombre de esta forma de trabajo) que representan la activación o desactivación de ciertos bits en cada una de las tres ternas; la sintaxis básica5.5 de chmod en este caso es la siguiente:
chmod ugoa{+,-}{rwxst} fichero
Podemos ver que el valor simbólico comienza por cero o más letras que indican sobre que terna de los permisos se van a realizar los cambios (u para propietario del fichero, g para grupo, o para resto de usuarios y a para las tres ternas; si no se especifica ninguna letra, se asume a). A ellas les sigue un signo `+' o `-' en función de si deseamos activar o resetar el bit sobre el que trabajamos, parámetro indicado por el último conjunto formado por una o más letras, r para el permiso de lectura, w para escritura, x para ejecución, s para SUID o SGID y t para bit de permanencia (el significado de estos dos últimos se explicará en el punto siguiente). Entre los tres campos del valor simbólico no se insertan espacios:
anita:~# ls -l /tmp/fichero
-r--------   1 root     other          902 Feb  9 05:05 /tmp/fichero
anita:~# chmod +x /tmp/fichero
anita:~# ls -l /tmp/fichero
-r-x--x--x   1 root     other          902 Feb  9 05:05 /tmp/fichero
anita:~# chmod og-x /tmp/fichero
anita:~# ls -l /tmp/fichero
-r-x------   1 root     other          902 Feb  9 05:05 /tmp/fichero
anita:~# chmod ug+rwx /tmp/fichero
anita:~# ls -l /tmp/fichero
-rwxrwx---   1 root     other          902 Feb  9 05:05 /tmp/fichero
anita:~#
Esta forma de trabajo simbólica es menos utilizada en la práctica que la forma octal, pero en ciertas situaciones es muy útil, por ejemplo si deseamos activar todos los permisos de ejecución de un archivo o si queremos setuidarlo: un simple chmod +x o chmod u+s es suficiente en estos casos, y evitamos preocuparnos por si modificamos el resto de permisos.

Quizás después de ver el procedimiento de modificación de los permisos de un fichero, este puede parecer demasiado complicado y arcaico para un sistema operativo moderno; a fin de cuentas, mucha gente prefiere gestores gráficos de permisos - igual que prefiere gestores gráficos para otras tareas de administración -, programas que dan todo hecho y no obligan al administrador a `complicarse', llenos de menús desplegables y diálogos que una y otra vez preguntan si realmente deseamos modificar cierto permiso (>Está usted seguro? >Realmente seguro? >Es mayor de edad? >Me lo jura?). Incluso esas personas aseguran que el procedimiento gráfico es mucho más claro y más potente que el que Unix ofrece en modo texto. Nada más lejos de la realidad; por un lado, aunque todo el mundo reconoce que la explicación detallada de cómo funcionan los permisos de ficheros es algo farragosa, en la práctica el convertir una terna de bits rwx a octal o viceversa es una tarea trivial, algo que ningún administrador con unas cuantas horas de práctica ni siquiera necesita pensar. Por otro, algo más importante que la facilidad o dificultad de modificar los permisos: su robustez; hemos de pensar que este modelo de protección está vigente desde hace casi treinta años y no ha cambiado absolutamente nada. Si en todo este tiempo no se ha modificado el mecanismo, obviamente es porque siempre ha funcionado - y lo sigue haciendo - bien.

Los bits SUID, SGID y sticky

Habitualmente, los permisos de los archivos en Unix se corresponden con un número en octal que varía entre 000 y 777; sin embargo, existen unos permisos especiales que hacen variar ese número entre 0000 y 7777: se trata de los bits de permanencia (1000), SGID (2000) y SUID (4000).

El bit de SUID o setuid se activa sobre un fichero añadiéndole 4000 a la representación octal de los permisos del archivo y otorgándole además permiso de ejecución al propietario del mismo; al hacer esto, en lugar de la x en la primera terna de los permisos, aparecerá una s o una S si no hemos otorgado el permiso de ejecución correspondiente (en este caso el bit no tiene efecto):
anita:~# chmod 4777 /tmp/file1
anita:~# chmod 4444 /tmp/file2
anita:~# ls -l /tmp/file1 
-rwsrwxrwx   1 root     other            0 Feb  9 17:51 /tmp/file1*
anita:~# ls -l /tmp/file2
-r-Sr--r--   1 root     other            0 Feb  9 17:51 /tmp/file2*
anita:~#
El bit SUID activado sobre un fichero indica que todo aquél que ejecute el archivo va a tener durante la ejecución los mismos privilegios que quién lo creó; dicho de otra forma, si el administrador crea un fichero y lo setuida, todo aquel usuario que lo ejecute va a disponer, hasta que el programa finalice, de un nivel de privilegio total en el sistema. Podemos verlo con el siguiente ejemplo:
luisa:/home/toni# cat testsuid.c
#include <stdio.h>
#include <unistd.h>

main(){
  printf("UID: %d, EUID: %d\n",getuid(),geteuid());
}
luisa:/home/toni# cc -o testsuid testsuid.c
luisa:/home/toni# chmod u+s testsuid
luisa:/home/toni# ls -l testsuid
-rwsr-xr-x   1 root     root         4305 Feb 10 02:34 testsuid
luisa:/home/toni# su toni
luisa:~$ id
uid=1000(toni) gid=100(users) groups=100(users)
luisa:~$ ./testsuid
UID: 1000, EUID: 0
luisa:~$
Podemos comprobar que el usuario toni, sin ningún privilegio especial en el sistema, cuando ejecuta nuestro programa setuidado de prueba está trabajando con un EUID (Effective UID) 0, lo que le otorga todo el poder del administrador (fijémonos que éste último es el propietario del ejecutable); si en lugar de este código el ejecutable fuera una copia de un shell, el usuario toni tendría todos los privilegios del root mientras no finalice la ejecución, es decir, hasta que no se teclee exit en la línea de órdenes.

Todo lo que acabamos de comentar con respecto al bit setuid es aplicable al bit setgid pero a nivel de grupo del fichero en lugar de propietario: en lugar de trabajar con el EUID del propietario, todo usuario que ejecute un programa setgidado tendrá los privilegios del grupo al que pertenece el archivo. Para activar el bit de setgid sumaremos 2000 a la representación octal del permiso del fichero y además habremos de darle permiso de ejecución a la terna de grupo; si lo hacemos, la s o S aparecerá en lugar de la x en esta terna. Si el fichero es un directorio y no un archivo plano, el bit setgid afecta a los ficheros y subdirectorios que se creen en él: estos tendrán como grupo propietario al mismo que el directorio setgidado, siempre que el proceso que los cree pertenezca a dicho grupo.

Pero, >cómo afecta todo esto a la seguridad del sistema? Muy sencillo: los bits de setuid y setgid dan a Unix una gran flexibilidad, pero constituyen al mismo tiempo la mayor fuente de ataques internos al sistema (entendiendo por ataques internos aquellos realizados por un usuario - autorizado o no - desde la propia máquina, generalmente con el objetivo de aumentar su nivel de privilegio en la misma). Cualquier sistema Unix tiene un cierto número de ejecutables setuidados y/o setgidados. Cada uno de ellos, como acabamos de comentar, se ejecuta con los privilegios de quien lo creó (generalmente el root u otro usuario con ciertos privilegios) lo que directamente implica que cualquier usuario tiene la capacidad de lanzar tareas que escapen total o parcialmente al control del sistema operativo: se ejecutan en modo privilegiado si es el administrador quien creó los ejecutables. Evidentemente, estas tareas han de estar controladas de una forma exhaustiva, ya que si una de ellas se comporta de forma anormal (un simple core dump) puede causar daños irreparables al sistema5.6; tanto es así que hay innumerables documentos que definen, o lo intentan, pautas de programación considerada `segura' (en la sección 5.5 se citan algunos de ellos y también se explican algunas de estas técnicas). Si por cualquier motivo un programa setuidado falla se asume inmediatamente que presenta un problema de seguridad para la máquina, y se recomienda resetear el bit de setuid cuanto antes.

Está claro que asegurar completamente el comportamiento correcto de un programa es muy difícil, por no decir imposible; cada cierto tiempo suelen aparecer fallos (bugs) en ficheros setuidados de los diferentes clones de Unix que ponen en peligro la integridad del sistema. Entonces, >por qué no se adopta una solución radical, como eliminar este tipo de archivos? Hay una sencilla razón: el riesgo que presentan no se corre inútilmente, para tentar al azar, sino que los archivos que se ejecutan con privilegios son estrictamente necesarios en Unix, al menos algunos de ellos. Veamos un ejemplo: un fichero setuidado clásico en cualquier clon es /bin/passwd, la orden para que los usuarios puedan cambiar su contraseña de entrada al sistema. No hace falta analizar con mucho detalle el funcionamiento de este programa para darse cuenta que una de sus funciones consiste en modificar el fichero de claves (/etc/passwd o /etc/shadow). Está claro que un usuario per se no tiene el nivel de privilegio necesario para hacer esto (incluso es posible que ni siquiera pueda leer el fichero de claves), por lo que frente a este problema tan simple existen varias soluciones: podemos asignar permiso de escritura para todo el mundo al fichero de contraseñas, podemos denegar a los usuarios el cambio de clave o podemos obligarles a pasar por la sala de operaciones cada vez que quieran cambiar su contraseña. Parece obvio que ninguna de ellas es apropiada para la seguridad del sistema (quizás la última lo sea, pero es impracticable en máquinas con un número de usuarios considerable). Por tanto, debemos asumir que el bit de setuid en /bin/passwd es imprescindible para un correcto funcionamiento del sistema. Sin embargo, esto no siempre sucede así: en un sistema Unix instalado out of the box el número de ficheros setuidados suele ser mayor de cincuenta; sin perjudicar al correcto funcionamiento de la máquina, este número se puede reducir a menos de cinco, lo que viene a indicar que una de las tareas de un administrador sobre un sistema recién instalado es minimizar el número de ficheros setuidados o setgidados. No obstante, tampoco es conveniente eliminarlos, sino simplemente resetear su bit de setuid mediante chmod:
luisa:~# ls -l /bin/ping 
-r-sr-xr-x   1 root     bin         14064 May 10  1999 /bin/ping*
luisa:~# chmod -s /bin/ping
luisa:~# ls -l /bin/ping 
-r-xr-xr-x   1 root     bin         14064 May 10  1999 /bin/ping*
luisa:~#
También hemos de estar atentos a nuevos ficheros de estas características que se localicen en la máquina; demasiadas aplicaciones de Unix se instalan por defecto con ejecutables setuidados cuando realmente este bit no es necesario, por lo que a la hora de instalar nuevo software o actualizar el existente hemos de acordarnos de resetear el bit de los ficheros que no lo necesiten. Especialmente grave es la aparición de archivos setuidados de los que el administrador no tenía constancia (ni son aplicaciones del sistema ni un aplicaciones añadidas), ya que esto casi en el 100% de los casos indica que nuestra máquina ha sido comprometida por un atacante. Para localizar los ficheros con alguno de estos bits activos, podemos ejecutar la siguiente orden:
luisa:~# find / \( -perm -4000 -o -perm -2000 \) -type f -print
Por otra parte, el sticky bit o bit de permanencia se activa sumándole 1000 a la representación octal de los permisos de un determinado archivo y otorgándole además permiso de ejecución; si hacemos esto, veremos que en lugar de una x en la terna correspondiente al resto de usuarios aparece una t (si no le hemos dado permiso de ejecución al archivo, aparecerá una T):
anita:~# chmod 1777 /tmp/file1
anita:~# chmod 1774 /tmp/file2
anita:~# ls -l /tmp/file1 
-rwxrwxrwt   1 root     other            0 Feb  9 17:51 /tmp/file1*
anita:~# ls -l /tmp/file2
-rwxrwxr-T   1 root     other            0 Feb  9 17:51 /tmp/file2*
anita:~#
Si el bit de permanencia de un fichero está activado (recordemos que si aparece una T no lo está) le estamos indicando al sistema operativo que se trata de un archivo muy utilizado, por lo que es conveniente que permanezca en memoria principal el mayor tiempo posible; esta opción se utilizaba en sistemas antiguos que disponían de muy poca RAM, pero hoy en día prácticamente no se utiliza. Lo que si que sigue vigente es el efecto del sticky bit activado sobre un directorio: en este caso se indica al sistema operativo que, aunque los permisos `normales' digan que cualquier usuario pueda crear y eliminar ficheros (por ejemplo, un 777 octal), sólo el propietario de cierto archivo y el administrador pueden borrar un archivo guardado en un directorio con estas características. Este bit, que sólo tiene efecto cuando es activado por el administrador (aunque cualquier usuario puede hacer que aparezca una t o una T en sus ficheros y directorios), se utiliza principalmente en directorios del sistema de ficheros en los que interesa que todos puedan escribir pero que no todos puedan borrar los datos escritos, como /tmp/ o /var/tmp/: si el equivalente octal de los permisos de estos directorios fuera simplemente 777 en lugar de 1777, cualquier usuario podría borrar los ficheros del resto. Si pensamos que para evitar problemas podemos simplemente denegar la escritura en directorios como los anteriores también estamos equivocados: muchos programas - como compiladores, editores o gestores de correo - asumen que van a poder crear ficheros en /tmp/ o /var/tmp/, de forma que si no se permite a los usuarios hacerlo no van a funcionar correctamente; por tanto, es muy recomendable para el buen funcionamiento del sistema que al menos el directorio /tmp/ tenga el bit de permanencia activado.

Ya para finalizar, volvemos a lo que hemos comentado al principio de la sección: el equivalente octal de los permisos en Unix puede variar entre 0000 y 7777. Hemos visto que podíamos sumar 4000, 2000 o 1000 a los permisos `normales' para activar respectivamente los bits setuid, setgid o sticky. Por supuesto, podemos activar varios de ellos a la vez simplemente sumando sus valores: en la situación poco probable de que necesitáramos todos los bits activos, sumaríamos 7000 a la terna octal 777:
luisa:~# chmod 0 /tmp/fichero
luisa:~# ls -l /tmp/fichero 
----------   1 root     root            0 Feb  9 05:05 /tmp/fichero
luisa:~# chmod 7777 /tmp/fichero
luisa:~# ls -l /tmp/fichero 
-rwsrwsrwt   1 root     root            0 Feb  9 05:05 /tmp/fichero*
luisa:~#
Si en lugar de especificar el valor octal de los permisos queremos utilizar la forma simbólica de chmod, utilizaremos +t para activar el bit de permanencia, g+s para activar el de setgid y u+s para hacer lo mismo con el de setuid; si queremos resetearlos, utilizamos un signo `-' en lugar de un `+' en la línea de órdenes.

Atributos de un archivo

En el sistema de ficheros ext2 (Second Extended File System) de Linux existen ciertos atributos para los ficheros que pueden ayudar a incrementar la seguridad de un sistema. Estos atributos son los mostrados en la tabla 4.1.

Tabla 4.1: Atributos de los archivos en ext2fs.
Atributo Significado
A Don´t update Atime
S Synchronous updates
a Append only
c Compressed file
i Immutable file
d No Dump
s Secure deletion
u Undeletable file



De todos ellos, de cara a la seguridad algunos no nos interesan demasiado, pero otros sí que se deben tener en cuenta. Uno de los atributos interesantes - quizás el que más - es `a'; tan importante es que sólo el administrador tiene el privilegio suficiente para activarlo o desactivarlo. El atributo `a' sobre un fichero indica que este sólo se puede abrir en modo escritura para añadir datos, pero nunca para eliminarlos. >Qué tiene que ver esto con la seguridad? Muy sencillo: cuando un intruso ha conseguido el privilegio suficiente en un sistema atacado, lo primero que suele hacer es borrar sus huellas; para esto existen muchos programas (denominados zappers, rootkits...) que, junto a otras funciones, eliminan estructuras de ciertos ficheros de log como lastlog, wtmp o utmp. Así consiguen que cuando alguien ejecute last, who, users, w o similares, no vea ni rastro de la conexión que el atacante ha realizado a la máquina; evidentemente, si estos archivos de log poseen el atributo `a' activado, el pirata y sus programas lo tienen más difícil para borrar datos de ellos. Ahora viene la siguiente cuestión: si el pirata ha conseguido el suficiente nivel de privilegio como para poder escribir - borrar - en los ficheros (en la mayoría de Unices para realizar esta tarea se necesita ser root), simplemente ha de resetear el atributo `a' del archivo, eliminar los datos comprometedores y volver a activarlo. Obviamente, esto es así de simple, pero siempre hemos de recordar que en las redes habituales no suelen ser atacadas por piratas con un mínimo nivel de conocimientos, sino por los intrusos más novatos de la red; tan novatos que generalmente se limitan a ejecutar programas desde sus flamantes Windows sin tener ni la más remota idea de lo que están haciendo en Unix, de forma que una protección tan elemental como un fichero con el flag `a' activado se convierte en algo imposible de modificar para ellos, con lo que su acceso queda convenientemente registrado en el sistema.

Otro atributo del sistema de archivos ext2 es `i' (fichero inmutable); un archivo con este flag activado no se puede modificar de ninguna forma, ni añadiendo datos ni borrándolos, ni eliminar el archivo, ni tan siquiera enlazarlo mediante ln. Igual que sucedía antes, sólo el administrador puede activar o desactivar el atributo `i' de un fichero. Podemos aprovechar esta característica en los archivos que no se modifican frecuentemente, por ejemplo muchos de los contenidos en /etc/ (ficheros de configuración, scripts de arranque... incluso el propio fichero de contraseñas si el añadir o eliminar usuarios tampoco es frecuente en nuestro sistema); de esta forma conseguimos que ningún usuario pueda modificarlos incluso aunque sus permisos lo permitan. Cuando activemos el atributo `i' en un archivo hemos de tener siempre en cuenta que el archivo no va a poder ser modificado por nadie, incluido el administrador, y tampoco por los programas que se ejecutan en la máquina; por tanto, si activáramos este atributo en un fichero de log, no se grabaría ninguna información en él, lo que evidentemente no es conveniente. También hemos de recordar que los archivos tampoco van a poder sen enlazados, lo que puede ser problemático en algunas variantes de Linux que utilizan enlaces duros para la configuración de los ficheros de arranque del sistema.

Atributos que también pueden ayudar a implementar una correcta política de seguridad en la máquina, aunque menos importantes que los anteriores, son `s' y `S'. Si borramos un archivo con el atributo `s' activo, el sistema va a rellenar sus bloques con ceros en lugar de efectuar un simple unlink(), para así dificultar la tarea de un atacante que intente recuperarlo; realmente, para un pirata experto esto no supone ningún problema, simplemente un retraso en sus propósitos: en el punto 4.7 se trata más ampliamente la amenaza de la recuperación de archivos, y también ahí se comenta que un simple relleno de bloques mediante bzero() no hace que la información sea irrecuperable.

Por su parte, el atributo `S' sobre un fichero hace que los cambios sobre el archivo se escriban inmediatamente en el disco en lugar de esperar el sync del sistema operativo. Aunque no es lo habitual, bajo ciertas circunstancias un fichero de log puede perder información que aún no se haya volcado a disco: imaginemos por ejemplo que alguien conecta al sistema y cuando éste registra la entrada, la máquina se apaga súbitamente; toda la información que aún no se haya grabado en disco se perderá. Aunque como decimos, esto no suele ser habitual - además, ya hemos hablado de las ventajas de instalar un S.A.I. -, si nuestro sistema se apaga frecuentemente sí que nos puede interesar activar el bit `S' de ciertos ficheros importantes.

Ya hemos tratado los atributos del sistema de ficheros ext2 que pueden incrementar la seguridad de Linux; vamos a ver ahora, sin entrar en muchos detalles (recordemos que tenemos a nuestra disposición las páginas del manual) cómo activar o desactivar estos atributos sobre ficheros, y también cómo ver su estado. Para lo primero utilizamos la orden chattr, que recibe como parámetros el nombre del atributo junto a un signo `+' o `-', en función de si deseamos activar o desactivar el atributo, y también el nombre de fichero correspondiente. Si lo que deseamos es visualizar el estado de los diferentes atributos, utilizaremos lsattr, cuya salida indicará con la letra correspondiente cada atributo del fichero o un signo - en el caso de que el atributo no esté activado:
luisa:~# lsattr /tmp/fichero 
-------- /tmp/fichero
luisa:~# chattr +a /tmp/fichero 
luisa:~# chattr +Ss /tmp/fichero 
luisa:~# lsattr /tmp/fichero 
s--S-a-- /tmp/fichero
luisa:~# chattr -sa /tmp/fichero 
luisa:~# lsattr /tmp/fichero 
---S---- /tmp/fichero
luisa:~#

Listas de control de acceso: ACLs

Las listas de control de acceso (ACLs, Access Control Lists) proveen de un nivel adicional de seguridad a los ficheros extendiendo el clásico esquema de permisos en Unix: mientras que con estos últimos sólo podemos especificar permisos para los tres grupos de usuarios habituales (propietario, grupo y resto), las ACLs van a permitir asignar permisos a usuarios o grupos concretos; por ejemplo, se pueden otorgar ciertos permisos a dos usuarios sobre unos ficheros sin necesidad de incluirlos en el mismo grupo. Este mecanismo está disponible en la mayoría de Unices (Solaris, AIX, HP-UX...), mientras que en otros que no lo proporcionan por defecto, como Linux, puede instalarse como un software adicional. A pesar de las agresivas campañas de marketing de alguna empresa, que justamente presumía de ofrecer este modelo de protección en sus sistemas operativos frente al `arcaico' esquema utilizado en Unix, las listas de control de acceso existen en Unix desde hace más de diez años ([Com88]).

Los ejemplos que vamos a utilizar aquí (órdenes, resultados...) se han realizado sobre Solaris; la idea es la misma en el resto de Unices, aunque pueden cambiar las estructuras de las listas. Para obtener una excelente visión de las ACLs es recomendable consultar [Fri95], y por supuesto la documentación de los diferentes clones de Unix para detalles concretos de cada manejo e implementación.

La primera pregunta que nos debemos hacer sobre las listas de control de acceso es obvia: >cómo las vemos? Si habitualmente queremos saber si a un usuario se le permite cierto tipo de acceso sobre un fichero no tenemos más que hacer un listado largo:
anita:~# ls -l /usr/local/sbin/sshd 
-rwx------   1 root     bin      2616160 Apr 28  1997 /usr/local/sbin/sshd
anita:~#
Viendo el resultado, directamente sabemos que el fichero sshd puede ser ejecutado, modificado y leído por el administrador, pero por nadie más; sin embargo, no conocemos el estado de la lista de control de acceso asociada al archivo. Para ver esta lista, en Solaris se ha de utilizar la orden getfacl:
anita:/# getfacl /usr/local/sbin/sshd 

# file: /usr/local/sbin/sshd
# owner: root
# group: bin
user::rwx
group::---              #effective:---
mask:---
other:---
anita:/#
Acabamos de visualizar una lista de control de acceso de Solaris; en primer lugar se nos indica el nombre del fichero, su propietario y su grupo, todos precedidos por `#'. Lo que vemos a continuación es la propia lista de control: los campos user, group y other son básicamente la interpretación que getfacl hace de los permisos del fichero (si nos fijamos, coincide con el resultado del ls -l). El campo mask es muy similar al umask clásico: define los permisos máximos que un usuario (a excepción del propietario) o grupo puede tener sobre el fichero. Finalmente, el campo effective nos dice, para cada usuario (excepto el propietario) o grupo el efecto que la máscara tiene sobre los permisos: es justamente el campo que tenemos que analizar si queremos ver quién puede acceder al archivo y de qué forma.

Sin embargo, hasta ahora no hemos observado nada nuevo; podemos fijarnos que la estructura de la lista de control de acceso otorga los mismos permisos que las ternas clásicas. Esto es algo normal en todos los Unix: si no indicamos lo contrario, al crear un fichero se le asocia una ACL que coincide con los permisos que ese archivo tiene en el sistema (cada archivo tendrá una lista asociada, igual que tiene unos permisos); de esta forma, el resultado anterior no es más que la visión que getfacl tiene de los bits rwx del fichero ([Gal96c]).

Lo interesante de cara a la protección de ficheros es extender los permisos clásicos del archivo, modificando su lista asociada. Esto lo podemos conseguir con la orden setfacl:
anita:~# setfacl -m user:toni:r-x /usr/local/sbin/sshd 
anita:~# getfacl /usr/local/sbin/sshd 

# file: /usr/local/sbin/sshd
# owner: root
# group: bin
user::rwx
user:toni:r-x           #effective:---
group::---              #effective:---
mask:---
other:---
anita:~#
Como vemos, acabamos de modificar la lista de control de acceso del archivo para asignarle a toni permiso de ejecución y lectura sobre el mismo. La orden setfacl se utiliza principalmente de tres formas: o bien añadimos entradas a la ACL, mediante la opción -m seguida de las entradas que deseemos añadir separadas por comas (lo que hemos hecho en este caso, aunque no se han utilizado comas porque sólo hemos añadido una entrada), o bien utilizamos el parámetro -s para reemplazar la ACL completa (hemos de indicar todas las entradas, separadas también por comas), o bien borramos entradas de la lista con la opción -d (de sintaxis similar a -m). Cada entrada de la ACL tiene el siguiente formato:
tipo:UIDGID:permisos
El tipo indica a quién aplicar los permisos (por ejemplo, user para el propietario del archivo, o mask para la máscara), el UID indica el usuario al que queremos asociar la entrada (como hemos visto, se puede utilizar también el login, y el GID hace lo mismo con el grupo (de la misma forma, se puede especificar su nombre simbólico). Finalmente, el campo de permisos hace referencia a los permisos a asignar, y puede ser especificado mediante símbolos rwx- o de forma octal.

Acabamos de indicar que el usuario toni tenga permiso de lectura y ejecución en el archivo; no obstante, si ahora este usuario intenta acceder al fichero en estos modos obtendrá un error:
anita:/usr/local/sbin$ id
uid=100(toni) gid=10(staff)
anita:/usr/local/sbin$ ./sshd
bash: ./sshd: Permission denied
anita:/usr/local/sbin$
>Qué ha sucedido? Nada anormal, simplemente está actuando la máscara sobre sus permisos (antes hemos dicho que debemos fijarnos en el campo effective, y aquí podemos comprobar que no se ha modificado). Para solucionar esto hemos de modificar el campo mask:
anita:~# setfacl -m mask:r-x /usr/local/sbin/sshd
anita:~#
Si ahora toni intenta acceder al fichero para leerlo o ejecutarlo, ya se le va a permitir:
anita:/usr/local/sbin$ id
uid=100(toni) gid=10(staff)
anita:/usr/local/sbin$ ./sshd
/etc/sshd_config: No such file or directory
...
Aunque obtenga un error, este error ya no depende de la protección de los ficheros sino de la configuración del programa: el administrador obtendría el mismo error. No obstante, sí que hay diferencias entre una ejecución de toni y otra del root, pero también son impuestas por el resto del sistema operativo Unix: toni no podría utilizar recursos a los que no le está permitido el acceso, como puertos bien conocidos, otros ficheros, o procesos que no le pertenezcan. Hay que recordar que aunque un usuario ejecute un archivo perteneciente al root, si el fichero no está setuidado los privilegios del usuario no cambian. Sucede lo mismo que pasaría si el usuario tuviera permiso de ejecución normal sobre el fichero, pero éste realizara tareas privilegiadas: podría ejecutarlo, pero obtendría error al intentar violar la protección del sistema operativo.

En Solaris, para indicar que una lista de control de acceso otorga permisos no reflejados en los bits rwx se situa un símbolo `+' a la derecha de los permisos en un listado largo:
anita:~# ls -l /usr/local/sbin/sshd
-rwx------+  1 root     bin      2616160 Apr 28  1997 /usr/local/sbin/sshd
anita:~#
Otra característica que tiene Solaris es la capacidad de leer las entradas de una lista de control de acceso desde un fichero en lugar de indicarlas en la línea de órdenes, mediante la opción -f de setfacl; el formato de este fichero es justamente el resultado de getfacl, lo que nos permite copiar ACLs entre archivos de una forma muy cómoda:
anita:~# getfacl /usr/local/sbin/sshd >/tmp/fichero
anita:~# setfacl -f /tmp/fichero /usr/local/sbin/demonio
anita:~# getfacl /usr/local/sbin/demonio

# file: /usr/local/sbin/demonio
# owner: root
# group: other
user::rwx
user:toni:r-x           #effective:r-x
group::---              #effective:---
mask:r-x
other:---
anita:~#
Esto es equivalente a utilizar una tubería entre las dos órdenes, lo que produciría el mismo resultado:
anita:~# getfacl /usr/local/sbin/sshd | setfacl -f - /usr/local/sbin/demonio
Antes de finalizar este apartado dedicado a las listas de control de acceso, quizás sea conveniente comentar el principal problema de estos mecanismos. Está claro que las ACLs son de gran ayuda para el administrador de sistemas Unix, tanto para incrementar la seguridad como para facilitar ciertas tareas; sin embargo, es fácil darse cuenta de que se pueden convertir en algo también de gran ayuda, pero para un atacante que desee situar puertas traseras en las máquinas. Imaginemos simplemente que un usuario autorizado de nuestro sistema aprovecha el último bug de sendmail (realmente nunca hay un `último') para conseguir privilegios de administrador en una máquina; cuando se ha convertido en root modifica la lista de control de acceso asociada a /etc/shadow y crea una nueva entrada que le da un permiso total a su login sobre este archivo. Una vez hecho esto, borra todo el rastro y corre a avisarnos del nuevo problema de sendmail, problema que rápidamente solucionamos, le damos las gracias y nos olvidamos del asunto. >Nos olvidamos del asunto? Tenemos un usuario que, aunque los bits rwx no lo indiquen, puede modificar a su gusto un archivo crucial para nuestra seguridad. Contra esto, poco podemos hacer; simplemente comprobar frecuentemente los listados de todos los ficheros importantes (ahora entendemos por qué aparece el símbolo `+' junto a las ternas de permisos), y si encontramos que un fichero tiene una lista de control que otorga permisos no reflejados en los bits rwx, analizar dicha lista mediante getfacl y verificar que todo es correcto. Es muy recomendable programar un par de shellscripts simples, que automaticen estos procesos y nos informen en caso de que algo sospechoso se detecte.


Recuperación de datos

La informática forense es un campo que día a día toma importancia; de la misma forma que la medicina forense es capaz de extraer información valiosa de un cadáver, incluso mucho después de haber muerto, la informática forense pretende extraer información de un `cadáver' computerizado (por ejemplo, un sistema en el que un intruso ha borrado sus huellas), también incluso mucho después de haber `muerto' (esto es, haber sido borrado). Aunque las técnicas de recuperación de datos en Unix se aplican habitualmente para potenciar la seguridad de un equipo (por ejemplo, como hemos dicho, para analizar el alcance real de un acceso no autorizado), éstas mismas técnicas utilizadas por un atacante pueden llegar a comprometer gravemente la seguridad del sistema: un intruso que haya conseguido cierto nivel de privilegio puede recuperar, por ejemplo, el texto plano de un documento que un usuario haya cifrado con PGP y posteriormente borrado - almacenando únicamente el documento cifrado -. Aunque esto no es tan trivial como en otros sistemas menos seguros (en los que incluso se facilitan herramientas tipo undelete), parece claro que este tipo de actividades constituyen una amenaza a la seguridad (principalmente a la privacidad) de cualquier sistema Unix.

En 1996, Peter Gutmann publicó [Gut96], un excelente artículo donde se demostró que la recuperación de datos de memoria (esto incluye por supuesto la memoria secundaria) es posible con un equipamiento relativamente barato, de entre 1000 y 2500 dólares, incluso tras sobreescribir varias veces los datos a borrar. Hasta ese momento casi todo el trabajo realizado en el campo de la destrucción `segura' de datos se habia limitado a estándares de organismos de defensa estadounidenses ([Cen91], [Age85]...). Como el propio Gutmann explica, estos trabajos - aparte de quedar anticuados - no mostraban toda la realidad sobre la destrucción y recuperación de datos, sino que ofrecían una información posiblemente inexacta; de esta forma las agencias estadounidenses confundían a la opinión pública (y a los servicios de países hostiles) asegurando así el acceso de la propia agencia a la información, y al mismo tiempo protegían sus propios datos mediante guías y estándares clasificados para uso interno.

El artículo de Gutmann se puede considerar la base de la informática forense actual, un campo que como hemos dicho, día a día cobra importancia; centrándonos en la rama de este campo relativa a Unix (se le suele denominar Unix Forensics) podemos encontrar herramientas para realizar borrado seguro de datos, como srm (Secure rm), del grupo de hackers THC (The Hacker´s Choice); este software implementa un algoritmo de borrado seguro basándose en [Gut96]. Dicho algoritmo efectua principalmente un procedimiento de sobreescritura casi 40 veces, haciendo un flush de la caché de disco después de cada una de ellas; además trunca el fichero a borrar y lo renombra aleatoriamente antes de efectuar el unlink(), de forma que para un potencial atacante sea más difícil obtener cualquier información del archivo una vez borrado. srm se distribuye dentro de un paquete software denominado secure-delete, donde también podemos encontrar otras herramientas relacionadas con la eliminación segura de datos: smem (borrado seguro de datos en memoria principal), sfill (borrado seguro de datos en el espacion disponible de un disco) y por último sswap (borrado seguro de datos en el área de swap de Linux); todos los algoritmos utilizados en estos programas están basados en el artículo de Gutmann del que antes hemos hablado.

Otra herramienta importante a la hora de hablar de Unix Forensics, pero esta vez desde el lado opuesto a secure-delete - esto es, desde el punto de vista de la recuperación de datos - es sin duda The Coroner´s Toolkit, obra de dos reconocidos expertos en seguridad: Wietse Venema y Dan Farmer. En verano de 1999, concretamente el 6 de agosto, en el IBM T.J. Watson Research Center de Nueva York estos dos expertos dieron una clase sobre Unix Forensics, en la que mostraban cómo extraer información de este sistema operativo, no sólo del sistema de ficheros, sino también de la red, el sistema de logs o los procesos que se ejecutan en una máquina. Lamentablemente, The Coroner´s Toolkit aún no se encuentra disponible, pero es posible ojear lo que va a ser esta herramienta en las transparencias de esta conferencia, disponibles en http://www.porcupine.org/forensics/, donde se muestra todo lo que un exhaustivo análisis sobre Unix puede - y también lo que no puede - conseguir.

El análisis forense, especialmente la recuperación de datos, es especialmente importante a la hora de analizar los alcances de una intrusión a un equipo. En estas situaciones, es muy posible que el atacante modifique ficheros de log (cuando no los borra completamente), troyanize programas o ejecute procesos determinados: es aquí donde la persona encargada de retornar al sistema a la normalidad debe desconfiar de cuanto la máquina le diga, y recurrir al análisis forense para determinar el impacto real del ataque y devolver el sistema a un correcto funcionamiento.

Almacenamiento seguro

La orden crypt(1)

La orden crypt permite cifrar y descifrar ficheros en diferentes sistemas Unix; si no recibe parámetros lee los datos de la entrada estándar y los escribe en la salida estándar, por lo que seguramente habremos de redirigir ambas a los nombres de fichero adecuados. Un ejemplo simple de su uso puede ser el siguiente:
$ crypt <fichero.txt >fichero.crypt
Enter key:
$
En el anterior ejemplo hemos cifrado utilizando crypt el archivo fichero.txt y guardado el resultado en fichero.crypt; el original en texto claro se mantiene en nuestro directorio, por lo que si queremos evitar que alguien lo lea deberemos borrarlo.

Para descifrar un fichero cifrado mediante crypt (por ejemplo, el anterior) utilizamos la misma orden y la misma clave:
$ crypt <fichero.crypt>salida.txt
Enter key:
$
El anterior comando ha descifrado fichero.crypt con la clave tecleada y guardado el resultado en el archivo salida.txt, que coincidirá en contenido con el anterior fichero.txt.

crypt no se debe utilizar nunca para cifrar información confidencial; la seguridad del algoritmo de cifra utilizado por esta orden es mínima, ya que crypt se basa en una máquina con un rotor de 256 elementos similar en muchos aspectos a la alemana Enigma, con unos métodos de ataque rápidos y conocidos por todos ([RW84]). Por si esto fuera poco, si en lugar de teclear la clave cuando la orden nos lo solicita lo hacemos en línea de comandos, como en el siguiente ejemplo:
$ crypt clave < fichero.txt > fichero.crypt
$
Entonces a la debilidad criptográfica de crypt se une el hecho de que en muchos Unices cualquier usuario puede observar la clave con una orden tan simple como ps (no obstante, para minimizar este riesgo, el propio programa guarda la clave y la elimina de su línea de argumentos nada más leerla).

Obviamente, la orden crypt(1) no tiene nada que ver con la función crypt(3), utilizada a la hora de cifrar claves de usuarios, que está basada en una variante del algoritmo DES y se puede considerar segura en la mayoría de entornos.

PGP: Pretty Good Privacy

El software PGP, desarrollado por el criptólogo estadounidense Phil Zimmermann ([Zim95a],
[Zim95b]), es mundialmente conocido como sistema de firma digital para correo electrónico. Aparte de esta función, PGP permite también el cifrado de archivos de forma convencional mediante criptografía simétrica ([Gar95]); esta faceta de PGP convierte a este programa en una excelente herramienta para cifrar archivos que almacenamos en nuestro sistema; no es el mismo mecanismo que el que se emplea para cifrar un fichero que vamos a enviar por correo, algo que hay que hacer utilizando la clave pública del destinatario, sino que es un método que no utiliza para nada los anillos de PGP, los userID o el cifrado asimétrico. Para ello utilizamos la opción -c5.7 desde línea de órdenes:
anita:~$ pgp -c fichero.txt
No configuration file found.
Pretty Good Privacy(tm) 2.6.3i - Public-key encryption for the masses.
(c) 1990-96 Philip Zimmermann, Phil's Pretty Good Software. 1996-01-18
International version - not for use in the USA. Does not use RSAREF.
Current time: 2000/03/02 07:18 GMT

You need a pass phrase to encrypt the file. 
Enter pass phrase: 
Enter same pass phrase again: 
Preparing random session key...Just a moment....
Ciphertext file: fichero.txt.pgp
anita:~$
Esta orden nos preguntará una clave para cifrar, una pass phrase, que no tiene por qué ser (ni es recomendable que lo sea) la misma que utilizamos para proteger la clave privada, utilizada en el sistema de firma digital. A partir de la clave tecleada (que obviamente no se muestra en pantalla), PGP generará un archivo denominado fichero.txt.pgp cuyo contenido es el resultado de comprimir y cifrar (en este orden) el archivo original. Obviamente, fichero.txt no se elimina automáticamente, por lo que es probable que deseemos borrarlo a mano.

Si lo que queremos es obtener el texto en claro de un archivo previamente cifrado simplemente hemos de pasar como parámetro el nombre de dicho fichero:
anita:~$ pgp fichero.txt.pgp 
No configuration file found.
Pretty Good Privacy(tm) 2.6.3i - Public-key encryption for the masses.
(c) 1990-96 Philip Zimmermann, Phil's Pretty Good Software. 1996-01-18
International version - not for use in the USA. Does not use RSAREF.
Current time: 2000/03/02 07:24 GMT

File is conventionally encrypted.  
You need a pass phrase to decrypt this file. 
Enter pass phrase: 
Just a moment....Pass phrase appears good. .
Plaintext filename: fichero.txt
anita:~$
Como vemos, se nos pregunta la clave que habíamos utilizado para cifrar el archivo, y si es correcta se crea el fichero con el texto en claro; como sucedía antes, el archivo original no se elimina, por lo que tendremos ambos en nuestro directorio.

PGP ofrece un nivel de seguridad muchísimo superior al de crypt(1), ya que utiliza algoritmos de cifra más robustos: en lugar de implementar un modelo similar a Enigma, basado en máquinas de rotor, PGP ofrece cifrado simétrico principalmente mediante IDEA, un algoritmo de clave secreta desarrollado a finales de los ochenta por Xuejia Lai y James Massey. Aparte de IDEA, en versiones posteriores a la utilizada aquí se ofrecen también Triple DES (similar a DES pero con una longitud de clave mayor) y CAST5, un algoritmo canadiense que hasta la fecha sólo ha podido ser atacado con éxito mediante fuerza bruta; este último es el cifrador simétrico utilizado por defecto en PGP 5.x.

TCFS: Transparent Cryptographic File System

TCFS es un software desarrollado en la Universidad de Salerno y disponible para sistemas Linux que proporciona una solución al problema de la privacidad en sistemas de ficheros distribuidos como NFS: típicamente en estos entornos las comunicaciones se realizan en texto claro, con la enorme amenaza a la seguridad que esto implica. TCFS almacena los ficheros cifrados, y son pasados a texto claro antes de ser leídos; todo el proceso se realiza en la máquina cliente, por lo que las claves nunca son enviadas a través de la red.

La principal diferencia de TCFS con respecto a otros sistemas de ficheros cifrados como CFS es que, mientras que éstos operan a nivel de aplicación, TCFS lo hace a nivel de núcleo, consiguiendo así una mayor transparencia y seguridad. Obviamente esto tiene un grave inconveniente: TCFS sólo está diseñado para funcionar dentro del núcleo de sistemas Linux, por lo que si nuestra red de Unix utiliza otro clon del sistema operativo, no podremos utilizar TCFS correctamente. No obstante, esta gran integración de los servicios de cifrado en el sistema de los ficheros hace que el modelo sea transparente al usuario final.

Para utilizar TCFS necesitamos que la máquina que exporta directorios vía NFS ejecute el demonio xattrd; por su parte, los clientes han de ejecutar un núcleo compilado con soporte para TCFS. Además, el administrador de la máquina cliente ha de autorizar a los usuarios a que utilicen TCFS, generando una clave que cada uno de ellos utilizará para trabajar con los ficheros cifrados; esto se consigue mediante tcfsgenkey, que genera una entrada para cada usuario en /etc/tcfspasswd:
rosita:~# tcfsgenkey
login: toni
password:
now we'll generate the des key.
press 10 keys:**********
Ok.
rosita:~# cat /etc/tcfspasswd
toni:2rCmyOUsM5IA=
rosita:~#
Una vez que un usuario tiene una entrada en /etc/tcfspasswd con su clave ya puede acceder a ficheros cifrados; para ello, en primer lugar utilizará tcfslogin para insertar su clave en el kernel, tras lo cual puede ejecutar la variante de mount distribuida con TCFS para montar los sistemas que el servidor exporta. Sobre los archivos de estos sistemas, se utiliza la variante de chattr de TCFS para activar o desactivar el atributo X (podemos visualizar los atributos de un fichero con lsattr), que indica que se trata de archivos que necesitan al demonio de TCFS para trabajar sobre ellos (cifrando o descifrando). Finalmente, antes de abandonar una sesión se ha de ejecutar tcfslogout, cuya función es eliminar la clave del kernel de Linux. También es necesaria una variante de passwd, proporcionada con TCFS, que regenera las claves de acceso a archivos cifrados cuando un usuario cambia su password.

TCFS utiliza uno de los cuatro modos de funcionamiento que ofrece el estándar DES ([oS80]) denominado CBC (Cipher Block Chaining). El principal problema de este modelo (aparte de la potencial inseguridad de DES) es la facilidad para insertar información al final del fichero cifrado, por lo que es indispensable recurrir a estructuras que permitan detectar el final real de cada archivo; otro problema, menos peligroso a priori, es la repetición de patrones en archivos que ocupen más de 34 Gigabytes (aproximadamente), que puede conducir, aunque es poco probable, a un criptoanálisis exitoso en base a estas repeticiones. Más peligroso es el uso del mismo password de entrada al sistema como clave de cifrado utilizando la función resumen MD5 (el peligro no proviene del uso de esta función hash, sino de la clave del usuario); previsiblemente en futuras versiones de TCFS se utilizarán passphrases similares a las de PGP para descifrar y descifrar.

Otros métodos de almacenamiento seguro

En los últimos años los usuarios de Unix se han concienciado cada vez más con la seguridad de los datos que poseen en sus sistemas, especialmente de la privacidad de los mismos: un sistema fiable ha de pasar necesariamente por un método de almacenamiento seguro; por supuesto, esta preocupación de los usuarios automáticamente se traduce en más investigaciones y nuevos desarrollos en este campo de la seguridad. En este capítulo hemos analizado las ventajas, las desventajas y el funcionamiento de algunos de estos sistemas, desde el modelo clásico y habitual en Unix hasta las últimas herramientas de análisis forense y su problemática, pasando por aplicaciones tan simples como crypt o tan complejas como PGP; aunque se ha pretendido dar una visión general de lo que se entiende por un almacenamiento seguro en Unix, es imposible tratar todas las implementaciones de sistemas que incrementan la seguridad en la actualidad. No obstante, antes de finalizar este capítulo hemos preferido comentar algunas de las características de sistemas que se han hecho ya, se están haciendo, o previsiblemente se harán en un futuro no muy lejano un hueco importante entre los mecanismos de almacenamiento seguro en Unix.

No podemos finalizar sin hablar del sistema CFS (Cryptographic File System), del experto en seguridad Matt Blaze ([Bla93]), que se ha convertido en el sistema más utilizado en entornos donde coexisten diferentes clones de Unix (ya hemos comentado el problema de TCFS y su dependencia con Linux). Provee de servicios de cifrado a cualquier sistema de ficheros habitual en Unix, NFS incluido, utilizando una combinación de varios modos de trabajo de DES que son lo suficientemente ligeros como para no sobrecargar demasiado a una máquina normal pero lo suficientemente pesados como para proveer de un nivel aceptable de seguridad. Los usuarios no tienen más que asociar una clave a los directorios a proteger para que CFS cifre y descifre sus contenidos de forma transparente utilizando dicha clave; el texto en claro de los mismos nunca se almacena en un dispositivo o se transmite a través de la red, y los procedimientos de copia de seguridad en la máquina no se ven afectados por el uso de CFS. Todo el proceso se realiza en el espacio de usuario (a diferencia de TCFS, que operaba dentro del kernel de Linux) utilizando principalmente el demonio cfsd en la máquina donde se encuentren los sistemas cifrados.

Peter Gutmann, del que ya hemos hablado en este capítulo, desarrolló en la primera mitad de los noventa SFS (Secure File System). Este modelo de almacenamiento seguro se diseñó originalmente para sistemas MS-DOS o Windows, donde funciona como un manejador de dispositivos más, aunque en la actualidad existen también versiones para Windows 95, Windows NT y OS/2. No está portado a Unix, pero aquí lo citamos porque existe un sistema de almacenamiento seguro para Unix denominado también Secure File System, SFS, pero no tiene nada que ver con el original de Gutmann. El SFS de Unix funciona de una forma similar a CFS pero utilizando el criptosistema Blowfish y una versión minimalista de RSA en lugar de DES; no vamos a entrar en detalles de este software principalmente porque su uso en entornos Unix no está ni de lejos tan extendido como el de CFS.

La criptografía es la herramienta principal utilizada en la mayoría de los sistemas de almacenamiento seguro; sin embargo, todos ellos plantean un grave problema: toda su seguridad reside en la clave de cifrado, de forma que el usuario se encuentra indefenso ante métodos legales - o ilegales - que le puedan obligar a desvelar esta clave una vez que se ha determinado la presencia de información cifrada en un dispositivo de almacenamiento. Esto, que nos puede parecer algo exagerado, no lo es en absoluto: todos los expertos en criptografía coinciden en afirmar que los métodos de ataque más efectivos contra un criptosistema no son los efectuados contra el algoritmo, sino contra las personas (chantaje, amenazas, presiones judiciales...). Intentando dar solución a este problema, durante los últimos años de la década de los noventa, prestigiosos investigadores de la talla de Roger Needham, Ross Anderson o Adi Shamir ([ANS98]) han establecido las bases de sistemas seguros basados en modelos esteganográficos, con desarrollos especialmente importantes sobre plataformas Linux ([MK99], [vSS98]...). La disponibilidad del código fuente completo de este clon de Unix unida a su política de desarrollo ha propiciado enormemente estos avances, hasta el punto de que existen en la actualidad sistemas de ficheros basados en esteganografía que se insertan en el kernel igual que lo hace un sistema normal como ufs o nfs, o que se añaden a ext2 proporcionando funciones de cifrado.

La idea es sencilla: si por ejemplo tenemos cinco archivos cifrados con una aplicación como PGP, cualquier atacante con acceso al dispositivo y que haga unas operaciones sobre ficheros puede determinar que tenemos exactamente esos archivos cifrados; con esta información, su labor para obtener la información está muy clara: se ha de limitar a obtener las cinco claves privadas usadas para cifrar los ficheros. Conocer el número exacto es de una ayuda incalculable para el atacante. Con los sistemas esteganográficos, a pesar de que es imposible ocultar la existencia de cierta información cifrada, alguien que la inspeccione no va a poder determinar si la clave de descifrado que el propietario le ha proporcionado otorga acceso a toda la información o sólo a una parte de la misma. Un atacante que no posea todas las claves no va a poder descifrar todos los ficheros, y lo más importante: no va a poder saber ni siquiera si otros archivos aparte de aquellos a los que ha accedido existen o no, aunque posea un acceso total al software y al soporte físico. Para conseguir esto se utiliza una propiedad de ciertos mecanismos de seguridad denominada plausible deniability, algo que se vendría a traducir como `negación creible'; dicha propiedad permitiría a un usuario negar de forma creible que en un dispositivo exista más información cifrada de la que ya se ha podido descubrir, o que cierta transacción se haya llevado a cabo. Volviendo al ejemplo de PGP, el usuario podría revelar la clave de cifrado de sólo uno o dos de los archivos, aquellos que no considere vitales, ocultando las claves y la existencia del resto sin que el atacante sea capaz de determinar que la información accedida no es toda la existente.
next up previous contents
Siguiente: Programas seguros, inseguros y Subir: Seguridad del sistema Anterior: Seguridad del sistema   Índice General
2002-07-15