Next: Programas seguros, inseguros
y Up: Seguridad del sistema Previous: Seguridad del sistema Índice
General
Subsecciones
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.
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.
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.
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.
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:~#
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:UID
GID: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.
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.
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 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.
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.
Notas al pie
- ... (dispositivos)5.1
- Otros tipos de archivos, como los enlaces simbólicos, los sockets
o los pipes no los vamos a tratar aquí.
- ... siguiente5.2
- Recordemos que en ciertos Unices existe /var/tmp/, directorio
donde los usuarios también pueden escribir; quizás nos interese,
en lugar de dedicar una partición a este directorio, enlazarlo simbólicamente
a /tmp/.
- ... usuario5.3
- Excepto el root, que no se ve afectado por los permisos de un fichero.
- ... permisos5.4
- Esto no siempre es así: bajo ciertas circunstancias en algunos Unix
el cambio de grupo o de propietario puede modificar los permisos del archivo,
como veremos al hablar de ficheros setuidados.
- ... básica5.5
- Se recomienda consultar la página del manual para ver otras opciones
de la orden.
- ... sistema5.6
- Es especialmente preocupante la posibilidad de ejecutar código arbitrario
([One96]), comentada en la sección 5.3.
- ... -c5.7
- Los ejemplos se han realizado con PGP 2.6.3i, en versiones posteriores han
cambiado la sintaxis y la forma de trabajar.
Next: Programas seguros, inseguros
y Up: Seguridad del sistema Previous: Seguridad del sistema Índice
General