FTP (
File Transfer Protocol, puerto 21 TCP) es, como su nombre indica, un protocolo de transferencia de ficheros entre sistemas. Desde un equipo cliente conectamos a un servidor para descargar ficheros desde él - lo habitual - o para enviarle nuestros propios archivos.
Un problema básico y grave de FTP es que está pensado para ofrecer la máxima velocidad en la conexión, pero ni mucho menos para ofrecer la máxima seguridad; todo el intercambio de información, desde el
login y
password del usuario en el servidor hasta la transferencia de cualquier fichero, se realiza en texto claro, con lo que un atacante lo tiene muy fácil para capturar todo ese tráfico y conseguir así un acceso válido al servidor. Incluso puede ser una amenaza a la privacidad de nuestros datos el hecho de que ese atacante también pueda capturar y reproducir los ficheros transferidos. Para solucionar este problema es conveniente concienciar a nuestros usuarios de la utilidad de aplicaciones como
scp y
sftp, incluidas en el paquete SSH, que permiten transferir ficheros pero cifrando todo el tráfico; de esta forma, son el mejor sustituto de FTP.
Parece evidente que la conexión FTP a nuestro sistema ha de estar restringida a los usuarios que realmente lo necesiten: por ejemplo, un usuario como
root en principio no va a necesitar utilizar este servicio, ya que por lo general va a trabajar en consola; otros usuarios considerados `del sistema' (donde se incluye por ejemplo a
postmaster,
bin,
uucp,
shutdown,
daemon...) tampoco necesitarán hacer uso de FTP. Podemos indicar este tipo de usuarios a los que
no les está permitida una conexión vía FTP a nuestra máquina en
/etc/ftpusers, con un nombre por línea; un ejemplo de este fichero es el siguiente:
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:~#
FTP anónimo
Los problemas relacionados con la seguridad del servicio FTP son especialmente preocupantes cuando se trata de configurar un servidor de FTP anónimo; muchos de estas máquinas situadas en universidades españolas se convierten en servidores de imágenes pornográficas o de
warez (copias ilegales de programas comerciales). Conseguir un servidor de FTP anónimo seguro puede llegar a ser una tarea complicada: incluso en las páginas de ayuda de algunas variantes de Unix (como Solaris) se trata de facilitar el proceso para el administrador mediante un
shellscript que - por defecto - presenta graves problemas de seguridad, ya que deja una copia del fichero de claves del sistema como un archivo de acceso público y anónimo.
Para configurar correctamente un servidor de este tipo necesitamos en primer lugar crear al usuario
ftp en
/etc/passwd y
/etc/shadow, así como su directorio de conexión (algunos Unices, como Linux, ya incorporan esto al instalar el sistema). Este directorio ha de pertenecer a
root (
ningún fichero o subdirectorio ha de pertenecer
nunca a
ftp) y al grupo al que pertenece
ftp: con esto conseguimos que los permisos de propietario sean para el administrador y los de grupo para los usuarios anónimos; estos permisos serán 555.
Dentro del
$HOME de
ftp hemos de crear el árbol de directorios mínimo para poder trabajar correctamente; esto es debido a la llamada a
chroot() que se utiliza en los accesos anónimos, que permite a esos usuarios ver el directorio raíz de su conexión en el directorio real
~ftp/. Al menos dos directorios son necesarios:
etc/ y
bin/, ambos propiedad de
root y con modo 111. En el primero de ellos hemos de crear un fichero
passwd y otro
group, utilizados
no con propósitos de autenticación sino para visualizar el propietario y grupo de cada fichero en el entorno sobre el que se ha aplicado
chroot() al ejecutar
ls: por tanto, no hace falta
ninguna contraseña en ese fichero
passwd, y sólo ha de contener entradas para los usuarios que posean ficheros bajo la jerarquía de
ftp, como
root; de la misma forma, el fichero
group sólo ha de contener las entradas correspondientes a grupos que posean ficheros en dicha jerarquía:
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 AIX15.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 esperábamos:
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/lib
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`
/usr/sbin/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/.
Un 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.
Otra negación de servicio muy habitual contra los servidores de FTP anónimo es obligar a las máquinas a consumir una excesiva cantidad de CPU, ralentizando el sistema hasta que la calidad de servicio es muy baja; esto se produce en servidores que permiten descargar directorios completos como un único archivo, empaquetados con tar y/o comprimidos con gzip. Veamos un ejemplo extraído de una sesión de FTP anónimo:
ftp> pwd
257 "/pub/utils" is current directory.
ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
total 377
drwxr-xr-x 2 root root 1024 Sep 18 22:28 .
drwxrwxr-x 3 root wheel 1024 Sep 18 22:28 ..
-rw-r--r-- 1 root root 163519 Sep 18 22:28 transfig-3.1.2.tar.gz
-rw-r--r-- 1 root root 217850 Sep 18 22:27 tth_C.tgz
226 Transfer complete.
ftp> cd ..
250 CWD command successful.
ftp> pwd
257 "/pub" is current directory.
ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
total 3
drwxrwxr-x 3 root wheel 1024 Sep 18 22:28 .
drwxrwxr-x 8 root wheel 1024 Aug 1 1994 ..
drwxr-xr-x 2 root root 1024 Sep 18 22:28 utils
226 Transfer complete.
ftp> get utils.tar.gz
local: utils.tar.gz remote: utils.tar.gz
200 PORT command successful.
150 Opening BINARY mode data connection for /bin/tar.
226 Transfer complete.
381369 bytes received in 1.07 secs (3.5e+02 Kbytes/sec)
ftp>
Como podemos ver, acabamos de descargar un directorio completo empaquetado y comprimido sin más que añadir al nombre de dicho directorio la extensión .tar.gz (o simplemente .tar si no hubiéramos querido comprimirlo). Evidentemente esto resulta muy útil en determinadas situaciones pero, >nos hemos parado a pensar que sucedería si alguien intentara descargar el directorio /pub.tar.gz en un servidor con unos cuantos Gigabytes
de ficheros? La carga del sistema crecería muchísimo, ya que estamos empaquetando y comprimiendo un archivo de grandes dimensiones; si ahora extendemos esto a varios usuarios que ejecutan la misma orden simultáneamente podemos hacernos una idea de la sobrecarga introducida en la máquina: una razón más que suficiente para tener cuidado con los directorios que permitimos descargar de esta forma...
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:~#
FTP invitado
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/sh
Como 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 192.168.0.3...
Connected to anita.
Escape character is '^]'.
SunOS 5.7
login: toni
Password:
Connection closed by foreign host.
luisa:~#