* * * * * killall -HUP inetdSi en nuestro clon de Unix no disponemos de una órden para enviar señales a los procesos en función de su nombre (como pkill en Solaris o killall en Linux o IRIX) podemos utilizar un poco de programación shellscript para conseguirlo:
* * * * * kill -HUP `ps -auxw|grep inetd|grep -v grep|awk '{print $2}'`
anita:~# grep systat /etc/inetd.conf systat stream tcp nowait root /usr/bin/ps ps -ef anita:~#Bien se ofrezca la tabla de procesos o bien otro tipo de información sobre el sistema, este servicio es habitual encontarlo deshabilitado, ya que cualquier dato sobre nuestro sistema (especialmente procesos, nombres de usuario, máquinas desde las que conectan...) puede ser aprovechado por un pirata para atacar el equipo. Si por motivos de comodidad a la hora de administrar varios hosts dentro de una red local necesitamos tener abierto systat, debemos restringir las direcciones desde las que se puede acceder al servicio mediante TCP Wrappers.
anita:~# telnet rosita daytime Trying 195.195.5.1... Connected to rosita. Escape character is '^]'. Thu Apr 20 05:02:33 2000 Connection closed by foreign host. anita:~#Aunque a primera vista este servicio no represente un peligro para la integridad de nuestro sistema, siempre hemos de recordar una norma de seguridad fundamental: sólo hay que ofrecer los servicios estrictamente necesarios para el correcto funcionamiento de nuestras máquinas. Como daytime no es un servicio básico, suele ser recomendable cerrarlo; además, la información que proporciona, aunque escasa, puede ser suficiente para un atacante: le estamos indicando el estado del reloj de nuestro sistema, lo que por ejemplo le da una idea de la ubicación geográfica del equipo.
anita:~# telnet rosita time Trying 195.195.5.1... Connected to rosita. Escape character is '^]'. ['^Connection closed by foreign host. anita:~#Este servicio suele ser más útil que el anterior: aunque una persona no entienda la información mostrada por time, sí que lo hace una máquina Unix. De esta forma, se utiliza time en un servidor para que las estaciones cliente puedan sincronizar sus relojes con él con órdenes como netdate o rdate:
luisa:~# date Thu Apr 20 02:19:15 CEST 2000 luisa:~# rdate rosita [rosita] Thu Apr 20 05:10:49 2000 luisa:~# date Thu Apr 20 02:20:02 CEST 2000 luisa:~# rdate -s rosita luisa:~# date Thu Apr 20 05:11:59 2000 luisa:~#Los problemas de time son en principio los mismos que los de daytime; aunque también es recomendable mantener este servicio cerrado, es más fácil imaginar situaciones en las que un administrador desee ofrecer time en varias máquinas que imaginar la necesidad de ofrecer daytime.
anita:~# telnet rosita netstat Trying 195.195.5.1... Connected to rosita. Escape character is '^]'. Active Internet connections (w/o servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 rosita:netstat anita:4990 ESTABLISHED Connection closed by foreign host. anita:~#Como sucedía con systat, es recomendable deshabilitar este servicio comentando la línea correspondiente de /etc/inetd.conf, o en todo caso restringir el acceso al mismo a máquinas de nuestra red local, mediante TCP Wrappers. La información sobre el estado del sistema de red - o al menos de parte del mismo - puede ser muy útil para un atacante, ya que por ejemplo le está mostrando nombres de hosts y además le permite hacerse una idea del tráfico que soporta la máquina, de los servicios que ofrece, de los hábitos de conexión de los usuarios...
luisa:~# tftp rosita tftp> get vmlinuz Received 531845 bytes in 3.4 seconds tftp> quit luisa:~#Podemos ver que en ningún momento se solicita un nombre de usuario o una clave, lo que nos da una idea de los graves problemas de seguridad que el ofrecer este servicio puede implicarnos. Hasta hace unos años, era normal que los fabricantes de sistemas Unix vendieran sus productos con tftp abierto y sin configurar, con lo que un pirata lo tenía muy fácil para conseguir cualquier fichero de contraseñas:
luisa:~# tftp victima tftp> get /etc/passwd /tmp/salida Received 1845 bytes in 0.6 seconds tftp> quit luisa:~#
anita:~# finger @rosita
[rosita]
Login Name Tty Idle Login Time Office Office Phone
toni Toni at ROSITA */0 28 Apr 20 04:43 (anita)
root El Spiritu Santo 1 12 Apr 11 02:10
anita:~# finger toni@rosita
[rosita]
Login: toni Name: Toni at ROSITA
Directory: /home/toni Shell: /bin/bash
On since Thu Apr 20 04:43 (CEST) on pts/0 from anita
30 minutes 28 seconds idle
(messages off)
No mail.
No Plan.
anita:~#
Como podemos ver, finger está proporcionando mucha información
que podría ser de utilidad para un atacante: nombres de usuario, hábitos
de conexión, cuentas inactivas...incluso algunas organizaciones rellenan
exhaustivamente el campo gecos del fichero de contraseñas, con
datos como números de habitación de los usuarios o incluso su teléfono.
Está claro que esto es fácilmente aprovechable por un pirata para
practicar ingeniería social contra nuestros usuarios - o contra el propio
administrador -. Es básico para la integridad de nuestras máquinas
deshabilitar este servicio, restringir su acceso a unos cuantos equipos
de la red local mediante TCP Wrappers o utilizar versiones del demonio
fingerd como ph ( Phone Book), que permiten especificar
la información que se muestra al acceder al servicio desde cada máquina.
ircd:x:1001:100:Gestion IRC,,,:/home/ircd:/usr/bin/pineEn este caso hemos de tomar una precaución adicional: la mayoría de programas de correo ( elm, pine...) permiten escapes al shell, procedimientos que tarde o temprano ejecutan con éxito un intérprete de órdenes; por ejemplo, con elm no tenemos más que iniciar vi para escribir un mensaje y en el editor ejecutar :!/bin/sh para ejecutar este intérprete. Para evitar estos escapes o bien podemos modificar el código del gestor de correo - algo no muy habitual - o utilizar ya versiones modificadas disponibles a través de Internet.
luisa:~# tail -2 ~adm/syslog
Apr 24 04:16:19 luisa wu.ftpd[1306]: connect from rosita
Apr 24 04:16:21 luisa ftpd[1306]: ANONYMOUS FTP LOGIN FROM \
rosita [195.195.5.1], toni@
luisa:~#
No obstante, si el sistema desde el que esa persona conecta no tiene habilitado
dicho servicio, el nombre de usuario no se va a poder conseguir:
luisa:~# tail -2 ~adm/syslog
Apr 24 04:19:37 luisa wu.ftpd[1331]: connect from root@anita
Apr 24 04:19:39 luisa ftpd[1331]: ANONYMOUS FTP LOGIN FROM \
root @ anita [195.195.5.3], toni@
luisa:~#
El servicio auth no se debe utilizar nunca con propósitos
de autenticación robusta, ya que dependemos no de nuestros sistemas, sino
de la honestidad de la máquina remota; un atacante con el suficiente nivel
de privilegio en esta puede enviarnos cualquier nombre de usuario que desee. Incluso
en ciertas situaciones, si ident no está habilitado ni siquiera
hacen falta privilegios para devolver un nombre falso: cualquier usuario puede
hacerlo. En cambio, sí que es útil para detectar pequeñas
violaciones de seguridad, por lo que quizás interese habilitar el servicio
en nuestras máquinas (aunque limitemos su uso mediante TCP Wrappers.
luisa:~# cat /etc/ftpusers halt operator root shutdown sync bin daemon adm lp mail postmaster news uucp man games guest postgres # 'postgres' NO hace ftp nobody inferno luisa:~#
anita:~# cat /export/home/ftp/etc/passwd root:*:0:1:El Spiritu Santo:/:/sbin/sh anita:~# cat /export/home/ftp/etc/group root::0: other::1: daemon::2: ftp::30000: anita:~#
Como vemos, el usuario ftp tiene un shell denominado
/bin/false; aunque aquí no tiene ningún efecto, en el archivo
de contraseñas real de la máquina esto es útil para prevenir
que dicho usuario pueda conectar mediante TELNET o similar.
Por su parte, en el otro directorio que hemos creado (bin/) hemos de
almacenar una copia del programa ls, de forma que los usuarios puedan
listar los contenidos de los directorios cuyos permisos lo permitan; si utilizamos
una versión estática del programa, como hace por ejemplo Linux,
no hemos de configurar nada para que la aplicación funcione, pero si
en cambio utilizamos un ls dinámico (como SunOS o Solaris)
hemos de crear el directorio lib/ dentro de ~ftp/ y copiar
en él las librerías necesarias para que el programa funcione (podemos
ver de cuáles se trata con ldd).
Con estos pasos ya tenemos configurada la base de nuestro servidor de FTP
anónimo; no obstante, es habitual crear dos directorios más, uno
denominado pub/ y otro incoming/, dentro de la misma jerarquía
que los anteriores (esto es, en el $HOME del usuario ftp).
El primero suele contener directorios con todos los ficheros que deseemos ofrecer
a los usuarios anónimos; su modo ha de ser 555, o 2555 en los sistemas
que utilicen el bit setgid en un directorio para que sus subdirectorios
y ficheros hereden el grupo del propietario. El directorio incoming
es justo lo contrario: sirve para que esos usuarios anónimos puedan enviar
archivos a nuestra máquina. Y es aquí donde suelen comenzar muchos
problemas: al permitir el upload de software, es posible que
algunos piratas utilicen nuestra máquina para crear servidores warez,
subiendo programas comerciales a este directorio y luego indicando su localización
exacta a otras personas, para que los puedan descargar. Por tanto, los permisos
de incoming son vitales para nuestra seguridad (incluso si no deseamos
que los usuarios anónimos nos envíen ficheros podemos borrar este
directorio): esos permisos han de ser 1733, y el propietario del directorio
es el root. ¿Para qué ponemos el bit de permanencia?
Muy sencillo: para que los usuarios no puedan sobreescribir o borrar ficheros
existentes; aunque la mayoría de servidores FTP no permiten
a los usuarios anónimos sobreescribir ficheros, si no pusiéramos
este modo un usuario normal del sistema sí que podría hacerlo.
El siguiente shellscript puede utilizarse para configurar cómodamente un entorno restringido destinado a los usuarios de ftp anónimo siguiendo las directrices que acabamos de comentar; funciona correctamente (en teoría) sobre Solaris, Linux y AIX 3. Al igual que sucede con muchas tareas automatizadas, conviene repasar manualmente la estructura de directorios y ficheros creados para comprobar que todo es como esperabamos:
anita:~# cat /usr/local/sbin/creaentorno #!/bin/sh # Script para crear un entorno chroot()eado.
# Funciona OK en Linux, Solaris y AIX.
#
# Esta variable es una lista con los programas que necesitamos en el
# entorno restringido.
PROGS="/bin/ls"
# Imprime modo de uso
if (test $# -lt 1); then
echo "Usage: $0 /path/to/chroot-environment"
exit
fi
# Detectamos clon de Unix
OS=`uname -s`
# Creamos estructura de directorios
echo "Creando estructura de directorios para $OS"
if [ ! -d $1 ]; then
mkdir -p $1
fi
chown root $1
for i in bin etc; do
if [ ! -d $1/$i ] ; then
mkdir -p $1/$i
fi
chown root $1/$i
done
# En funcion del Unix, la estructura sera una u otra...
if [ $OS = "Linux" ]; then
if [ ! -d $1/lib ]; then
mkdir -p $1/lib
fi
chown root $1/li fi if ( test $OS = "SunOS" || test $OS = "AIX" ); then if [ ! -d $1/usr/lib ]; then mkdir -p $1/usr/lib fi chown root $1/usr/lib cd $1 ln -s ./usr/lib $1/lib fi # Instalamos programas y las librerias que necesitan echo "Instalando programas y librerias..." for i in $PROGS; do if [ ! -f $1/$i ]; then cp $i $1/bin fi chmod 111 $1/bin chown root $1/bin if [ $OS = "AIX" ]; then for j in `ldd $i|awk -F"(" '{if(NR!=1) print $1}'`; do if [ ! -f $1/$j ]; then cp $j $1/lib fi chown root $1/$j done else for j in `ldd $i|awk '{print $3}'`; do if [ ! -f $1/$j ]; then cp $j $1/lib fi chown root $1/$j done fi done # Estos ficheros quizas sea necesario retocarlos a mano, en funcion del tipo # de entorno restringido que fabriquemos. # Generamos PASSWD echo "Generando /etc/passwd..." awk -F: '$1=="root" {print $1":*:"$3":"$4":"$5":"$6":"$7}' /etc/passwd >\ $1/etc/passwd awk -F: '$1=="bin" {print $1":*:"$3":"$4":"$5":"$6":"$7}' /etc/passwd>>\ $1/etc/passwd awk -F: '$1=="daemon" {print $1":*:"$3":"$4":"$5":"$6":"$7}' /etc/passwd>>\ $1/etc/passwd chmod 444 $1/etc/passwd chown root $1/etc/passwd # Quizas hay que anyadir otros grupos que nos interesen # Generamos GROUP con algunas entradas echo "Generando /etc/group..." awk -F: '$1=="root" {print $1":*:"$3":"$4}' /etc/group>$1/etc/group awk -F: '$1=="bin" {print $1":*:"$3":"}' /etc/group>>$1/etc/group awk -F: '$1=="daemon" {print $1":*:"$3":"}' /etc/group>>$1/etc/group chmod 444 $1/etc/group chown root $1/etc/group # Generamos pub/ e incoming/ echo "Generando pub/ e incoming/..." if [ ! -d $1/pub ]; then mkdir -p $1/pub fi chmod 2555 $1/pub chown root $1/pub if [ ! -d $1/incoming ]; then mkdir -p $1/incoming fi chmod 1733 $1/incoming chown root $1/incoming # Si estamos en Solaris, aun no hemos acabado if [ $OS = "SunOS" ]; then # Mas librerias echo "$OS: Instalando librerias..." for i in ld.so.1 libc.so.1 libdl.so.1 libmp.so.2 libnsl.so.1 \ libsocket.so.1 nss_compat.so.1 nss_dns.so.1 nss_files.so.1 \ nss_nis.so.1 nss_nisplus.so.1 nss_xfn.so.1 straddr.so \ straddr.so.2; do cp /usr/lib/$i $1/usr/lib done if [ ! -d $1/dev ]; then mkdir -p $1/dev fi chown root $1/dev # Generamos dispositivos echo "$OS: Generando dispositivos..." for i in /dev/zero /dev/tcp /dev/udp /dev/ticotsord; do MAJOR=`ls -lL $i|awk '{print $5}'|sed s/","//g` MINOR=`ls -lL $i|awk '{print $6}'` TYPE=`ls -lL $i|cut -c1-1` mknod $1/$i $TYPE $MAJOR $MINOR done chmod 666 $1/dev/* fi echo "FIN" # FIN de Solaris anita:~#
Algunos problemas relacionados con incoming/ provienen de los permisos
con que se crean sus ficheros y subdirectorios: aunque los usuarios anónimos
no puedan leer el directorio, con algunos servidores ftpd sí
que es posible que puedan leer los ficheros contenidos en él (y sus subdirectorios),
con lo que sigue siendo posible acceder a los archivos conociendo su nombre
exacto; para evitar este problema, muchos administradores planifican un sencillo
shellscript para que cada cierto tiempo mueva los contenidos de
incoming a otro lugar, fuera del alcance de los usuarios anónimos
(por ejemplo, un subdirectorio con modo 000 de /tmp/). Ciertos servidores,
como WU-ftpd, tienen un fichero de configuración (/etc/ftpaccess)
donde indicar - entre otras cosas - los modos con que se van a crear entradas
en incoming/.
Otro ataque típico a los servidores de FTP es una negación
de servicio llenando todo el espacio disponible para el upload de ficheros;
para minimizar las consecuencias de este ataque, es conveniente situar el directorio
~ftp/ en una partición separada del resto del sistema de ficheros,
donde sólo se encuentre dicho directorio; algunos demonios permiten directamente
limitar la cantidad de ficheros subidos al servidor en cada sesión.
Por último, es una buena idea mostrar un mensaje cuando los usuarios
anónimos conectan a nuestra máquina donde se indiquen claramente
los fines del sistema y la atención a su uso indebido; este mensaje puede
sernos útil tanto con fines jurídicos (así el atacante
no podrá argumentar que desconocía la finalidad del sistema) como
con fines disuasorios: si el pirata se da cuenta de que nos preocupamos por
la seguridad de nuestro servidor, es posible que lo abandone y busque otro menos
protegido. Por ejemplo, si utilizamos WU-ftpd, en ~ftp/welcome.msg
podemos escribir el mensaje mostrado al conectar al sistema, y en diferentes
ficheros .message el mensaje que se vuelca a acceder a un directorio
(estos nombres son configurables en /etc/ftpaccess). Un ejemplo del
mensaje de entrada puede ser el siguiente:
anita:~# cat /export/home/ftp/welcome.msg
* * * ANITA * * *
----------- Bienvenid@s a ANITA ------------
Esta maquina es propiedad de la Universidad Politecnica de Valencia y
sus fines son exclusivamente academicos y de investigacion. Cualquier
otro uso sera perseguido y castigado con el maximo rigor.
Cualquier actividad realizada en, desde o hacia este sistema esta
sujeta a monitorizacion sin previo aviso.
anita:~#
Hasta ahora hemos visto dos formas de transferir ficheros desde una máquina Unix mediante ftp: o bien el usuario conecta utilizando su login y su clave al sistema y descarga de él cualquier archivo sobre el que tenga permiso de lectura, de cualquier parte del sistema de ficheros, o bien accede a un entorno restringido mediante
chroot()como usuario anónimo, con un login genérico y usando como contraseña una dirección de correo - seguramente falsa -. En muchas ocasiones, estos modelos pueden resultar insuficientes o al menos poco adecuados a nuestras necesidades.
Imaginemos esta situación: un proveedor de acceso a Internet decide ofrecer a sus clientes la posibilidad de actualizar sus páginas web personales mediante FTP, de forma que cada uno de ellos no tiene más que conectar con su nombre de usuario y su contraseña al servidor y subir sus ficheros html; dichos login y password serán por supuesto diferentes para cada usuario, por lo que parece claro que un entorno de ftp anónimo no es aplicable - al menos de forma inmediata - en esta situación. El ftp ‘normal’ funcionaría correctamente, pero su utilización tampoco es óptima: si un usuario no necesita acceder más que a su $HOME para actualizar sus páginas, ¿por qué permitirle que vea todo nuestro sistema de ficheros, aunque sea vía FTP, y que pueda descargar archivos tan comprometedores como /etc/passwd?
Los potenciales problemas de seguridad que la situación anterior implica han dado lugar a un tercer tipo de acceso FTP denominado invitado (guest), que se puede contemplar como una mezcla de los dos vistos hasta el momento. La idea de este mecanismo es muy sencilla: se trata de permitir que cada usuario conecte a la máquina mediante su login y su contraseña, pero evitando que tenga acceso a partes del sistema de ficheros que no necesita para realizar su trabajo; conectará a un entorno restringido mediante chroot(), algo muy similar a lo que sucede en los accesos anónimos. Para poder crear fácilmente entornos FTP restringidos a cada usuario es conveniente instalar WU-ftpd en la máquina; este servidor está disponible libremente a través de Internet, en la dirección ftp://ftp.wu-ftpd.org/pub/wu-ftpd/. Otros servidores, como el distribuido con Solaris, permiten crear usuarios FTP invitados pero de una forma más compleja; en los ejemplos que veamos en este punto vamos a asumir que utilizamos WU-ftpd.
Lo primero que necesitamos para configurar el entorno al que van a conectar este tipo de usuarios es una estructura de directorios y archivos muy similar a la que hemos estudiado para los accesos a través de FTP anónimo, pero esta vez colgando del directorio de conexión del usuario invitado; con unas pequeñas variaciones, podemos utilizar para crear este entorno el shellscript que hemos presentado en el punto anterior. Así, si queremos que nuestro usuario toni acceda como invitado vía FTP podemos crear esta estructura en su $HOME:
anita:~# /usr/local/sbin/creaentorno /export/home/toni Creando estructura de directorios para SunOS Instalando programas y librerias... Generando /etc/passwd... Generando /etc/group... Generando pub/ e incoming... SunOS: Instalando librerias... SunOS: Generando dispositivos... FIN anita:~#
Realmente, son necesarias pequeñas modificaciones sobre el esquema anterior para que todo funcione correctamente; por un lado, los directorios pub/ e incoming/ no son necesarios en los accesos como invitado, ya que a priori los usuarios que accedan de esta forma necesitarán escribir en varios directorios del entorno. Además, quizás nos interese repasar los permisos de toda la jerarquía de directorios creada, para afinar más los lugares en los que se les permita escribir a los usuarios; por ejemplo, si sólo van a subir archivos a un directorio $HOME/public html/, donde se ubicarán sus páginas web, no tienen por qué escribir en el resto del entorno. De la misma forma, si el directorio $HOME es propiedad de cada usuario quizás pueda borrar archivos como lib, que es un enlace a usr/lib/, lo que puede llegar a comprometer nuestra seguridad. Otro punto a tener en cuenta es quién va a poseer ficheros dentro del entorno restringido, ya que esos usuarios y sus grupos deberán tener una entrada en los archivos etc/passwd y etc/group; como sucedía con los usuarios anónimos, estos ficheros no se van a usar aquí para realizar autenticación, sino simplemente para ver los nombres del usuario y grupo propietarios de cada fichero al realizar un listado, por lo que en ninguno de ellos es necesaria una contraseña real: basta con un asterisco en el campo correspondiente. Una vez que hemos creado correctamente el entorno es necesario configurar el acceso del usuario en cuestión. Generalmente no nos interesará que acceda por telnet o similar, por lo que su shell en /etc/passwd (el original de la máquina, no el del entorno restringido) ha de ser algo como /bin/false. Es necesario que exista una entrada para este shell en /etc/shells, ya que de lo contrario el usuario no podrá autenticarse; si este último archivo no existe, es necesario crearlo. Su directorio $HOME, indicado en /etc/passwd, también ha de ser modificado de la siguiente forma:
toni:x:1002:10:Toni at ANITA:/export/home/toni/./:/bin/shComo vemos, se añade '/./' al directorio $HOME del usuario. Esta cadena indica dónde se va a efectuar el chroot() (por ejemplo, si quisiéramos que el chroot() se hiciera sobre /export/home/ y tras esta llamada el usuario entrara a su directorio toni, lo indicaríamos como /export/home/./toni/). Tras modificar /etc/passwd hemos de modificar /etc/group para incluir al usuario 'toni' en un grupo que luego definiremos como invitado, por ejemplo 'rftp':
anita:~# grep toni /etc/group rftp::400:toni anita:~#Ahora falta por configurar el archivo /etc/ftpaccess; hemos de indicarle al demonio que utilice este fichero (por ejemplo, mediante la opción '-a'). En él definimos el grupo 'guest' en las clases apropiadas:
class local real,guest,anonymous *.domain 0.0.0.0 class remote real,guest,anonymous *También le damos a los usuarios 'guest' los permisos que consideremos oportunos; habitualmente, interesará que puedan borrar, sobreescribir y renombrar sus archivos. Pero no es normal que necesiten ejecutar cambios en los modos de los ficheros o en su máscara de permisos:
delete no anonymous # delete permission? overwrite no anonymous # overwrite permission? rename no anonymous # rename permission? chmod no anonymous,guest # chmod permission? umask no anonymous,guest # umask permission?Y por último, también en /etc/ftpaccess, definimos al grupo 'rftp' como invitado:
guestgroup rftp
Una vez llegados a este punto el usuario ya está en disposición de conectar como invitado vía ftp; aunque realmente accederá a su $HOME, para él será el directorio raíz, y no verá ningún archivo del sistema que no se encuentre en este directorio.
Antes de finalizar, un último apunte: el entorno restringido que acabamos de ver sólo se aplica para accesos por ftp; así, si el usuario tiene definido un shell estándar en /etc/passwd, cuando conecte mediante telnet o similar seguirá teniendo acceso a todo el sistema de ficheros, por lo que todo el trabajo que hemos realizado perdería su sentido. Aunque en el siguiente punto daremos alguna idea para crear entornos restringidos en los accesos por terminal remota, esta situación es mucho más extraña que la de los accesos invitados, por lo que normalmente (y esto es muy importante) los shells de los usuarios invitados han de ser del tipo /bin/false, es decir, no les permitiremos una sesión interactiva en el sistema por terminal remota. Con un shell de este estilo, si intentan acceder a la máquina (por ejemplo mediante telnet), nada más introducir correctamente su login y su password serán desconectados:
luisa:~# telnet anita Trying 195.195.5.3... Connected to anita. Escape character is '^]'. SunOS 5.7 login: toni Password: Connection closed by foreign host. luisa:~#
El protocolo TELNET (TCP, puerto 23) permite
utilizar una máquina como terminal virtual de otra a través de
la red, de forma que se crea un canal virtual de comunicaciones similar - pero
mucho más inseguro - a utilizar una terminal físicamente conectada
a un servidor; la idea es sencilla: estamos accediendo remotamente en modo texto
a un equipo - en principio potente - igual que si estuviéramos utilizando
su consola o una de sus terminales físicas, lo que nos permite aprovechar
toda su potencia de cálculo si necesidad de desplazarnos hasta la ubicación
de ese servidor, sino trabajando cómodamente desde nuestro propio equipo.
TELNET es el clásico servicio que hasta hace unos años
no se solía deshabilitar nunca: no es habitual adquirir una potente máquina
corriendo Unix y permitir que sólo se trabaje en ella desde su consola;
lo más normal es que este servicio esté disponible para que los
usuarios puedan trabajar remotamente, al menos desde un conjunto de máquinas
determinado. Evidentemente, reducir al mínimo imprescindible el conjunto
de sistemas desde donde es posible la conexión es una primera medida
de seguridad; no obstante, no suele ser suficiente: recordemos que TELNET
no utiliza ningún tipo de cifrado, por lo que todo el tráfico
entre equipos se realiza en texto claro. Cualquier atacante con un analizador
de red (o un vulgar sniffer) puede capturar el login y el
password utilizados en una conexión; el sniffing siempre
es peligroso, pero más aún en sesiones TELNET en
las que transmitimos nombres de usuarios y contraseñas: estamos otorgando
a cualquiera que lea esos datos un acceso total a la máquina destino,
bajo nuestra identidad. Por tanto, es muy recomendable no utilizar TELNET
para conexiones remotas, sino sustituirlo por aplicaciones equivalentes pero
que utilicen cifrado para la transmisión de datos: SSH
o SSL-Telnet son las más comunes. En estos casos necesitamos
además de la parte cliente en nuestro equipo, la parte servidora en la
máquina remota escuchando en un puerto determinado.
Aparte del problema de los atacantes esnifando claves, los demonios telnetd
han sido también una fuente clásica de problemas de programación
(se puede encontrar un excelente repaso a algunos de ellos en el capítulo
29 de [Ano97]); básicamente, cualquier versión
de este demonio que no esté actualizada es una potencial fuente de problemas,
por lo que conviene conseguir la última versión de telnetd
para nuestro Unix particular, especialmente si aún tenemos una versión
anterior a 1997. Otros problemas, como la posibilidad de que un atacante consiga
recuperar una sesión que no ha sido cerrada correctamente, el uso de
telnet para determinar qué puertos de un host están
abiertos, o la utilización del servicio TELNET (junto
a otros, como FTP) para averiguar el clon de Unix concreto (versión
de kernel incluida) que un servidor utiliza, también han hecho
famosa la inseguridad de este servicio.
Antes hemos hablado de la configuración de un entorno restringido para usuarios ftp invitados, que accedían mediante su login y su contraseña pero que no veían la totalidad del sistema de ficheros de nuestra máquina. Es posible -aunque ni de lejos tan habitual- hacer algo parecido con ciertos usuarios interactivos, usuarios que conectarán al sistema mediante telnet utilizando también su login y su password, pero que no verán el sistema de ficheros completo: sólo la parte que a nosotros nos interese (en principio).
Para que un usuario acceda mediante telnet a un entorno restringido con chroot() necesitamos en primer lugar un entorno parecido al que hemos visto antes: a partir de su directorio $HOME, una serie de subdirectorios bin/, lib/, etc/. . . Dentro de este último existirá al menos un fichero group y otro passwd (igual que sucedía antes, no se usan con propósitos de autenticación, por lo que no es necesario -ni recomendable- que existan claves reales en ninguno de ellos). En el directorio bin/ incluiremos los ejecutables que queremos que nuestro usuario pueda ejecutar, y en lib/ (o usr/lib/) las librerías que necesiten; si usamos el shellscript anterior -de nuevo, con alguna pequeña modificación- para crear este entorno, en la variable $PROGS podemos definir tales ejecutables para que automáticamente se copien junto a las librerías necesarias en el directorio correspondiente:
PROGS="/bin/ls /bin/sh"
Finalmente, en el archivo /etc/passwd real hemos de definir un shell para el usuario como el siguiente:
luisa:~# cat /home/toni/prog/shell.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#define SHELL "/bin/sh"
int main(){
struct passwd *entry=(struct passwd *)malloc(sizeof(struct passwd));
char *const ARGS[2]={SHELL,NULL};
while((entry=getpwent())->pw_uid!=getuid());
endpwent();
if(chdir(entry->pw_dir)<0) perror("chdir()");
if(chroot(entry->pw_dir)<0) perror("chroot()");
if(setuid(getuid())<0) perror("setuid()");
if(execvp(SHELL,ARGS)<0) perror("execvp()");
// No alcanzado
return(0);
}
luisa:~#
Este código, convenientemente compilado, será el shell real del usuario restringido; como vemos, obtiene el directorio $HOME del mismo, hace un chroot() a él, y ejecuta en este entorno el shell secundario (bin/sh, que realmente será $HOME/bin/sh). Para que el chroot() sea correcto el programa ha de estar setuidado bajo la identidad de root (sólo el superusuario puede realizar esta llamada), con los riesgos que esto implica; al contrario de lo que diría Knuth, yo sólo defiendo que el código anterior funciona, no que sea correcto... o seguro :)
Si tenemos que crear un entorno como este para usuarios interactivos hemos de tener en cuenta ciertas medidas de seguridad relativas a los ejecutables que situemos -o que permitamos situar- en dicho entorno. Para empezar, hemos de evitar a toda costa los ejecutables setuidados, así como las llamadas mknod(), chmod() o la propia chroot(); además, no debe ser posible obtener privilegios de administrador dentro del entorno restringido, ya que para el root estas restricciones pierden su sentido: no tenemos más que pensar que si un usuario con privilegios de root dentro del entorno es capaz de generar un dispositivo que represente un disco duro, con algo tan sencillo como la utilidad mknod, automáticamente accederá a la totalidad de ese disco, olvidando ya el chroot() y la potencial protección que pueda ofrecernos. Algo similar ocurre con la memoria del sistema, ciertos dispositivos físicos, o estructuras de datos del núcleo: si esto es accesible desde el entorno restringido, es muy probable que nuestra seguridad se vea rota tarde o temprano (más bien temprano).
Tampoco es aconsejable permitir la ejecución de compiladores de C o de intérpretes de Perl.
Como hemos dicho, este tipo de entornos es mucho menos habitual que los de ftp, aparte de bastante más peligrosos. Una tarea tan habitual como cambiar la contraseña no es posible -al menos de forma trivial- en este entorno (aunque podríamos modificar el código anterior para que se ofrezca al usuario esta posibilidad antes de situarlo en el entorno restringido). ¿Y que sucede si necesitamos que el usuario acceda no a un sólo directorio, sino a dos? Las soluciones -al menos las seguras- no son inmediatas.
luisa:~# grep smtp /etc/services smtp 25/tcp mail luisa:~#Tras reconocer el servicio, hemos de añadir una línea en /etc/inetd.conf indicando cómo se ha de ejecutar sendmail cuando inetd reciba una petición en el puerto 25; dicha línea es similar a la siguiente:
luisa:~# grep smtp /etc/inetd.conf smtp stream tcp nowait root /usr/sbin/tcpd sendmail -bs luisa:~#Una vez realizados estos cambios podemos controlar el acceso a nuestro servicio SMTP mediante TCP Wrappers; por ejemplo, en el caso de la Universidad Politécnica, el servidor de correo principal se denomina vega.cc.upv.es. Para que sólo esta máquina nos pueda enviar correo, incluiremos una línea como la siguiente en /etc/hosts.allow:
luisa:~# grep sendmail /etc/hosts.allow sendmail: vega.cc.upv.es luisa:~#El resto de sistemas no han de estar autorizados a conectar al puerto; esto incluye también a la máquina local: para un correcto funcionamiento de nuestro sistema de correo, ni siquiera hace falta que localhost tenga permiso para acceder a su puerto 25. En [Gon97] se explica cómo combinar estas restricciones ofrecidas por TCP Wrappers con un cortafuegos como TIS Firewall Toolkit; en esta obra también se habla con más detalle de los problemas que puede implicar el correo electrónico, y por supuesto de cómo solucionarlos.
luisa:~# rlogin -l toni rosita Overflow on /dev/null, please empty the bit bucket. rosita:~$ exit logout rlogin: connection closed. luisa:~# rsh -l toni rosita id uid=1000(toni) gid=100(users) groups=100(users) luisa:~# rcp prueba.tex toni@rosita:/tmp/ luisa:~#Como vemos, la última orden no ha solicitado ninguna contraseña; ha copiado el fichero local 'prueba.tex' en el directorio /tmp/ del sistema remoto, bajo la identidad del usuario toni. A continuación veremos por qué no se ha pedido clave para realizar esta acción.
rosita:~# cat /etc/hosts.equiv luisa rosita:~# cat ~toni/.rhosts anita rosita:~#En esta situación, cualquier usuario de luisa puede acceder a rosita si su nombre de usuario es el mismo; además, el usuario toni de anita puede también conectar a rosita sin necesidad de ninguna contraseña:
anita:~$ rlogin rosita
In the long run, every program becomes rococo, and then rubble.
-- Alan Perlis
rosita:~$ id
uid=1000(toni) gid=100(users) groups=100(users)
rosita:~$
Aparte de máquinas fiables habíamos hablado de usuarios fiables;
la idea es la misma que antes, pero aplicándola ahora a nombres de usuario
junto a (o en lugar de) nombres de máquina. Podemos indicar estos nombres
tanto en /etc/hosts.equiv como en los archivos .rhosts; no
obstante, la primera opción no es recomendable, ya que estaríamos
permitiendo al usuario fiable del sistema remoto acceder sin contraseña
a cualquier cuenta de nuestra máquina. De esta forma, si deseamos
crear usuarios fiables de sistemas remotos, es necesario hacerlo en los archivos
.rhosts. Por ejemplo, imaginemos que el usuario toni de nuestra
máquina tiene un nombre de usuario distinto ( antonio) en un sistema
remoto, y desea establecer una relación de confianza; para ello creará
en su $HOME el siguiente archivo .rhosts:
rosita:~# cat ~toni/.rhosts amparo antonio rosita:~#Entonces, desde la máquina amparo el usuario antonio podrá acceder a la cuenta de toni en nuestro sistema sin utilizar contraseñas:
amparo:~$ id
uid=102(antonio) gid=10(staff)
amparo:~$ rlogin -l toni rosita
It is practically impossible to teach good programming style to
students that have had prior exposure to BASIC: as potential
programmers they are mentally mutilated beyond hope of
regeneration.
-- Dijkstra
rosita:~$ id
uid=1000(toni) gid=100(users) groups=100(users)
rosita:~$
Como podemos ver, las relaciones de confianza entre equipos Unix pueden ser muy
útiles y cómodas, pero al mismo tiempo muy peligrosas: estamos confiando
plenamente en sistemas remotos, por lo que si su seguridad se ve comprometida
también se ve la nuestra. Las máquinas fiables se han de reducir
a equipos de la misma organización, y administrados por la misma persona;
además, es necesario tener siempre presente que si tenemos habilitados
los servicios r-* cualquier usuario puede establecer relaciones de confianza,
lo que puede suponer una violación de nuestra política de seguridad.
Es conveniente chequear los directorios $HOME en busca de ficheros
.rhosts (en la sección 10.2.6
se presentaba un shellscript que convenientemente planificado puede ayudarnos
en esta tarea); muchos administradores prefieren no complicarse buscando estos
ficheros, y configuran sus sistemas para que en cada $HOME exista un fichero
con este nombre, propiedad de root y con modo 000: así los usuarios
no tienen ocasión de otorgar confianza a sistemas remotos. Esto se puede
conseguir con el siguiente shellscript:
#!/bin/sh
for i in `cat /etc/passwd |awk -F: '{print $6}'`; do
cd $i
> .rhosts
chmod 0 .rhosts
done
Las relaciones de confianza son transitivas: si una máquina confía
en otra, lo hace también en todas en las que confía ella. De esta
forma se crean anillos de confianza entre máquinas, y como las relaciones
suelen estar basadas en el nombre del equipo se trata de objetivos ideales para
un atacante mediante IP Spoofing: si un pirata consigue hacer pasar su
equipo por uno de los confiables, automáticamente ha conseguido acceso
- casi ilimitado - al resto de las máquinas.
anita:~# xhost +luisa luisa being added to access control list anita:~#Si ejecutamos la sentencia anterior en la máquina donde se ejecuta el servidor, cualquier usuario del sistema remoto estará autorizado a lanzar aplicaciones contra él12.3:
luisa:~# xterm -display anita:0.0 & [1] 11974 luisa:~#La orden xhost sin opciones nos dará una lista de los clientes que pueden lanzar aplicaciones contra el servidor, mientras que la opción especial '+' deshabilitará este control de acceso, algo que evidentemente no es recomendable: cualquier usuario de cualquier sistema podrá utilizar nuestro servidor:
anita:~# xhost access control enabled, only authorized clients can connect LOCAL: INET:anita INET:localhost INET:luisa anita:~# xhost + access control disabled, clients can connect from any host anita:~# xhost access control disabled, clients can connect from any host LOCAL: INET:anita INET:localhost INET:luisa anita:~#Una medida de seguridad básica utilizando este modelo es habilitar la máquina en nuestra lista de hosts sólo el tiempo necesario para que el cliente arranque, y deshabilitarla después; así la ejecución de la aplicación cliente funcionará normalmente, pero no se podrán lanzar nuevas peticiones al servidor. También para eliminar una dirección de la lista utilizamos la orden xhost:
anita:~# xhost access control enabled, only authorized clients can connect LOCAL: INET:anita INET:localhost INET:luisa anita:~# xhost -luisa luisa being removed from access control list anita:~# xhost access control enabled, only authorized clients can connect LOCAL: INET:anita INET:localhost anita:~#De esta forma, cuando alguien intente lanzar una aplicación contra nuestro servidor desde un sistema no autorizado verá un mensaje de error similar al siguiente:
luisa:~# xterm -display anita:0.0 Xlib: connection to "anita:0.0" refused by server Xlib: Client is not authorized to connect to Server Error: Can't open display: anita:0.0 luisa:~#Como hemos dicho, este modelo de seguridad es demasiado vulnerable; por un lado, estamos autenticando clientes en base a una dirección o a un nombre de máquina, algo fácilmente falsificable por un atacante. Por otro, aunque los usuarios de los sistemas a los que permitimos utilizar nuestro servidor sean conocidos, fiables, y amantes de la naturaleza, nada nos demuestra que sus sistemas sean seguros, por lo que si sus equipos se ven comprometidos, nuestro servidor también.
luisa:~# xauth list luisa:0 MIT-MAGIC-COOKIE-1 8c1d09aab44573a524467c4e8faaaeb5 luisa/unix:0 MIT-MAGIC-COOKIE-1 8c1d09aab44573a524467c4e8faaaeb5 luisa:~#El comando anterior, xauth, se utiliza para manejar la información de las cookies de cada usuario; por ejemplo, un uso muy habitual es la transferencia de cookies a máquinas remotas, para que puedan así conectar al servidor X de un determinado equipo. Para ello debemos extraer la cookie de nuestro $DISPLAY y enviarla al fichero $HOME/.Xauthority del sistema remoto, con una orden como esta:
luisa:~# xauth extract - $DISPLAY | ssh anita -l toni xauth merge - luisa:~#Este mecanismo tiene principalmente dos problemas de seguridad: por un lado, las cookies se transmiten en texto claro por la red, por lo que son susceptibles de ser interceptadas; por otro, al estar guardadas en el fichero $HOME/.Xauthority, cualquiera que lo pueda leer tendrá acceso a ellas: es muy importante que este archivo tenga permiso de lectura y escritura sólo para su propietario, y que también tomemos precauciones si los directorios $HOME de los usuarios son exportados vía NFS.