Next: Seguridad de la subred
Up: Seguridad del sistema Previous: Autenticación de usuarios
Índice General
Subsecciones
Seguridad del núcleo
El núcleo o kernel de un sistema Unix es la parte más importante
del mismo, hasta tal punto que en términos puristas se considera al núcleo
como el sistema operativo en sí. Pero incluso si no lo consideramos así,
y contemplamos al sistema operativo como el conjunto formado por el núcleo
y una serie de herramientas (editor, compilador, enlazador, shell...),
es innegable que el kernel es la parte del sistema más importante,
y con diferencia: mientras que las aplicaciones operan en el espacio de usuario,
el núcleo siempre trabaja en el modo privilegiado del procesador (RING
0). Esto implica que no se le impone ninguna restricción a la hora de ejecutarse:
utiliza todas las instrucciones del procesador, direcciona toda la memoria, accede
directamente al hardware (más concretamente, a los manejadores
de dispositivos), etc. De esta forma, un error en la programación, o incluso
en la configuración del núcleo puede ser fatal para nuestro sistema.
Por desgracia muchos administradores piensan que un intruso nunca va a actuar
a un nivel tan bajo para comprometer al sistema. Si bien es cierto que en redes
habituales la inmensa mayoría de atacantes no poseen los conocimientos
necesarios para utilizar el kernel del sistema operativo en beneficio
propio, cualquier pirata con el suficiente nivel de experiencia puede conseguir
privilegios de root y aprovecharlos para modificar el núcleo o
configurarlo a su gusto. Y es justamente este tipo de ataques uno de los más
difíciles de detectar: cualquier administrador tiende a confiar ciegamente
en lo que el sistema operativo le dice, de forma que si ejecuta la orden
anita:~# uptime
3:46am up 9 days, 2:22, 6 users, load average: 1.15, 1.05, 1.07
anita:~#
automáticamente va a asumir que su sistema ha permanecido más de
nueve días sin reiniciarse; esto puede ser cierto o no serlo, e independientemente
de la veracidad del resultado de esta orden alguien puede haber accedido a nuestro
kernel y haber comprometido su seguridad. Por ejemplo, si ha modificado
completamente el núcleo, puede haber reprogramado la llamada sysinfo()
para que devuelva un resultado erróneo, de forma que el administrador nunca
se percate que la máquina ha sido reiniciada para cargar el kernel
modificado; incluso en los Unices que soportan la inserción de módulos
en el núcleo (como Linux, Solaris o FreeBSD) el atacante puede haber utilizado
esta facilidad para modificar el kernel sin necesidad de reiniciar el
equipo; excelentes lecturas sobre este tipo de ataques son [Pla99],
[Pra99b] o [Pra99a].
Evidentemente, para cualquier intruso el ataque a un núcleo es mucho más
fácil en clones de Unix cuyo código fuente esté disponible,
como Linux, Minix o algunos BSD, pero el ataque es posible en cualquier sistema
([Pla99] lo demuestra sobre Solaris). El hecho
de la completa disponibilidad del código fuente de un sistema operativo
(ahora no hablamos de aplicaciones, nos referimos al sistema operativo propiamente
dicho) suele despertar controversias entre la comunidad científica dedicada
a la seguridad informática: mientras unos argumentan que esta disponibilidad
supone un problema de seguridad - un atacante puede dedicarse a revisar el código
hasta encontrar un error de programación, y aprovecharlo -, otros les acusan
de defender las teorías de Security through Obscurity y sostienen
que cuanta más gente tenga acceso al código más errores se
localizarán y solucionarán, y por tanto a la larga se va a conseguir
un sistema mucho más robusto. Esta segunda postura es la que más
fuerza está tomando desde hace unos años, y parece también
la más razonable: es cierto que un atacante puede dedicarse a leer código
hasta encontrar un error, pero se ha comprobado que la mayoría de los fallos
no se suelen detectar de esta forma, sino por cualquier circunstancia que genera
un evento extraño sobre el que posteriormente se investiga. Por tanto,
la disponibilidad del código del núcleo no debe suponer una amenaza
a la seguridad a priori10.1. Además, un administrador de sistemas con
un mínimo nivel de conocimientos de programación puede aprovechar
la disponibilidad del código para detectar rápidamente problemas
de seguridad: por ejemplo, si sospecha que alguien utiliza sus recursos para ejecutar
programas adivinadores de contraseñas, puede modificar librerías
para detectar llamadas `sospechosas' a la función crypt(), o
si piensa que determinado usuario ejecuta un programa setuidado para conseguir
privilegios que no le corresponden, puede modificar la llamada al sistema
setuid() para comprobar si sus sospechas son ciertas.
Visto esto, parece claro que el núcleo va a representar un pilar básico
para conseguir un sistema seguro; es más, al ser el kernel la base
del sistema operativo, no sirve de nada esforzarse en conseguir seguridad a nivel
de aplicación si nuestro núcleo es inseguro. En este capítulo
vamos a tratar aspectos relativos a la seguridad de los núcleos de sistemas
Unix, y también hablaremos de aspectos que, sin pertenecer estrictamente
al kernel, son de un nivel tan bajo que su funcionamiento es muy dependiente
de cada versión de Unix. Como cada clon del sistema operativo tiene sus
métodos para configurar o recompilar el kernel, y en además
en este trabajo no podemos tratar extensamente cada uno de ellos, es indispensable
en cada caso consultar los manuales antes de modificar cualquier parámetro
de los vistos aquí.
Linux
A la hora de recompilar un nuevo núcleo de Linux hemos de tener en cuenta
algunas opciones dentro del grupo `Networking Options' que pueden afectar
a la seguridad de nuestra máquina (algunos de estos aspectos, para núcleos
2.0, pueden encontrarse en [Wre98]). Sin embargo, antes de entrar en detalles con
opciones concretas, es muy conveniente que introduzcamos soporte para
sistemas de ficheros proc en `Filesystems' (CONFIG_PROC_FS)
y activemos el interfaz sysctl en `General Setup' (CONFIG_SYSCTL);
con estos pasos habilitamos la capacidad de Linux para modificar ciertos parámetros
del núcleo (en /proc/sys/) sin necesidad de reiniciar el sistema
o recompilar el kernel.
Pasando ya a comentar algunas opciones que nos ofrece Linux, es bastante interesante
para la seguridad configurar nuestro sistema como un cortafuegos a la hora de
compilar el núcleo (CONFIG_IP_FIREWALL).
Linux ofrece en su kernel facilidades para definir un firewall
de paquetes en el sistema, que además permitirá el IP-Masquerading.
Para que el subsistema de filtrado funcione es necesario que el IP-Forwarding
esté activado de la forma que más tarde veremos.
Otra opción que nos puede ayudar a incrementar la seguridad de nuestro
equipo es la defragmentación de paquetes (CONFIG_IP_ALWAYS_
DEFRAG) que llegan a través de la red. Cuando un equipo situado
entre el origen y el destino de los datos decide que los paquetes a enviar son
demasiado grandes, los divide en fragmentos de longitud menor; sin embargo, los
números de puerto sólamente viajan en el primer fragmento, lo que
implica que un atacante puede insertar información en el resto de tramas
que en teoría no debe viajar en ellas. Activando esta opción, en
nuestra máquina estos fragmentos se reagruparán de nuevo incluso
si van a ser reenviados a otro host.
Siguiendo con las diferentes opciones del subsistema de red, podemos habilitar
el soporte para `SYN Cookies' (CONFIG_SYN_COOKIES)
en el núcleo que estamos configurando. Una red TCP/IP habitual no puede
soportar un ataque de negación de servicio conocido como `SYN Flooding',
consistente básicamente en enviar una gran cantidad de tramas con el bit
SYN activado para así saturar los recursos de una máquina determinada
hasta que los usuarios no pueden ni siquiera conectar a ella. Las `SYN Cookies'
proporcionan cierta protección contra este tipo de ataques, ya que la pila
TCP/IP utiliza un protocolo criptográfico para permitir que un usuario
legítimo pueda seguir accediendo al sistema incluso si este está
siendo atacado. Aunque configuremos y ejecutemos un núcleo con esta opción
soportada, hemos de activar las `SYN Cookies' cada vez que el sistema
arranca (como veremos luego), ya que por defecto están deshabilitadas.
En ciertas situaciones es interesante analizar en espacio de usuario - es decir,
sin sobrecargar al núcleo más de lo estrictamente necesario - un
paquete o parte de él (típicamente, los 128 primeros bytes) que
llega a través de la red hasta nuestra máquina; de esta forma, un
analizador simple puede tomar ciertas decisiones en función del contenido
del paquete recibido, como enviar un correo al administrador en caso de sospecha
o grabar un mensaje mediante syslog. Justamente esto es lo que conseguimos
si habilitamos la opción Firewall Packet Netlink Device (CONFIG_IP_FIREWALL_
NETLINK).
Hasta ahora hemos hablado de la posibilidad que tiene Linux para modificar parámetros
del núcleo sin necesidad de recompilarlo o de reiniciar el equipo, mediante
el interfaz sysctl; esto implica por ejemplo que podemos modificar el
comportamiento del subsistema de red simplemente modificando determinados ficheros
de /proc/sys/ (recordemos que el sistema de ficheros /proc/
de algunos Unix es una interfaz entre estructuras de datos del núcleo y
el espacio de usuario). Vamos a ver ahora algunos de estos parámetros configurables
que tienen mucho que ver con la seguridad del sistema:
Uno de los parámetros que nos interesa es la habilitación o deshabilitación
del IP Forwarding en el núcleo de Linux; como hemos dicho antes,
el sistema de filtrado de paquetes sólo funciona cuando esta opción
está habilitada, lo que se consigue con la orden
luisa:~# echo 1 > /proc/sys/net/ipv4/ip_forward
Sin embargo, si no utilizamos las facilidades de firewalling del núcleo
de Linux esta opción ha de estar desabilitada (introduciríamos un
`0' en lugar de un `1' en el fichero anterior), ya que de lo contrario corremos
el peligro de que nuestra máquina se convierta en un router.
Antes hemos hablado de las `SYN Cookies', y hemos comentado que aunque
el soporte para esta característica se introduce al compilar el núcleo,
realmente el mecanismo se ha de activar desde espacio de usuario, por ejemplo
con una orden como la siguiente:
luisa:~# echo 1 >/proc/sys/net/ipv4/tcp_syncookies
También es muy recomendable que el subsistema de red del kernel
descarte las tramas con Source Routing o encaminamiento en origen activado.
Este tipo de paquetes contienen el camino que han de seguir hasta su destino,
de forma que los routers por los que pasa no han de examinar su contenido
sino simplemente reenviarlo, hecho que puede causar la llegada de datos que constituyan
una amenaza a nuestras políticas de seguridad. En los núcleos 2.0
esto se conseguía activando la opción CONFIG_IP_NOSR,
mientras que en los 2.2 la forma más sencilla de ignorar estos paquetes
es introduciendo un `0' en los diferentes ficheros accept_source_route
del directorio /proc/sys/net/ipv4/; por ejemplo la siguiente orden descarta
las tramas con encaminamiento en origen que llegan al dispositivo de red
eth0:
luisa:~# echo 0 >/proc/sys/net/ipv4/conf/eth0/accept_source_route
Hemos de recordar que las modificaciones que hacemos sobre el interfaz sysctl
son dinámicas (se pueden efectuar con el sistema funcionando, sin necesidad
de reiniciarlo), pero se pierden cuando la máquina se apaga para establecerse
a unos valores por defecto al arrancar de nuevo el sistema operativo; seguramente
nos interesará mantener los cambios realizados, por lo que en alguno de
los ficheros de inicialización de la máquina hemos de incluir las
órdenes que acabamos de explicar, obviamente después de haber montado
el sistema de ficheros /proc/.
Linux (no así otros Unices) proporciona dos dispositivos virtuales denominados
/dev/random y /dev/urandom que pueden utilizarse para generar
números pseudoaleatorios, necesarios para aplicaciones criptográficas.
El primero de estos ficheros, /dev/random, utiliza lo que su autor denomina
`ruido ambiental' (por ejemplo, temporizadores de IRQs, accesos a disco
o tiempos entre pulsaciones de teclas) para crear una fuente de entropía
aceptable y - muy importante - que apenas introduce sobrecarga en el sistema.
El segundo archivo, /dev/urandom, crea un resumen de la entropía
de /dev/random utilizando la función hash SHA ( Secure
Hash Algorithm), diseñada por el NIST y la NSA para su Digital
Signature Standard ([oST84]). Por tanto, tenemos una fuente de entropía
aceptable, /dev/urandom, y otra incluso mejor, pero de capacidad limitada,
/dev/random. Para detalles concretos sobre su funcionamiento se puede
consultar el fichero que las implementa dentro del núcleo de Linux,
drivers/char/random.c.
Como en el propio código se explica, cuando un sistema operativo arranca
ejecuta una serie de acciones que pueden ser predecidas con mucha facilidad por
un potencial atacante (especialmente si en el arranque no interactua ninguna persona,
como es el caso habitual en Unix). Para mantener el nivel de entropía en
el sistema se puede almacenar el desorden que existía en la parada de la
máquina para restaurarlo en el arranque; esto se consigue modificando los
scripts de inicialización del sistema. En el fichero apropiado
que se ejecute al arrancar (por ejemplo, /etc/rc.d/rc.M) debemos añadir
las siguientes líneas:
echo "Initializing random number generator..."
random_seed=/var/run/random-seed
# Carry a random seed from start-up to start-up
# Load and then save 512 bytes, which is the size of the entropy pool
if [ -f $random_seed ]; then
cat $random_seed >/dev/urandom
fi
dd if=/dev/urandom of=$random_seed count=1
chmod 600 $random_seed
Mientras que en un fichero que se ejecute al parar el sistema añadiremos
lo siguiente:
# Carry a random seed from shut-down to start-up
# Save 512 bytes, which is the size of the entropy pool
echo "Saving random seed..."
random_seed=/var/run/random-seed
dd if=/dev/urandom of=$random_seed count=1
chmod 600 $random_seed
Con estas pequeñas modificaciones de los archivos de arranque y parada
del sistema conseguimos mantener un nivel de entropía aceptable durante
todo el tiempo que el sistema permanezca encendido. Si de todas formas no consideramos
suficiente la entropía proporcionada por estos dispositivos de Linux, podemos
conseguir otra excelente fuente de desorden en el mismo sistema operativo a partir
de una simple tarjeta de sonido y unas modificaciones en el núcleo ([Men98]), o utilizar alguno de los generadores
- algo más complejos - citados en [Sch94].
En esta sección vamos a comentar algunos aspectos de modificaciones del
núcleo que se distribuyen libremente en forma de parches, y que contribuyen
a aumentar la seguridad de un sistema Linux; para obtener referencias actualizadas
de estos códigos - y otros no comentados aquí - es recomendable
consultar [Sei99]; para información de estructuras de datos,
ficheros o límites del núcleo de Linux se puede consultar [BBD$^$96] o [CDM97].
En include/asm/resource.h tenemos la inicialización de algunas
estructuras de datos del núcleo relacionadas con límites a la cantidad
de recursos consumida por un determinado proceso; por ejemplo, el máximo
número de procesos por usuario (RLIMIT_NPROC)
se inicializa a
MAX_TASKS_PER_ USER,
valor que en include/linux/tasks.h podemos comprobar que se corresponde
con la mitad de NR_TASKS (número máximo
de procesos en el sistema); en arquitecturas i86 el valor del límite
de procesos por usuario se fija a 256. De la misma forma, el número máximo
de ficheros abiertos por un proceso (RLIMIT_NOFILE)
se inicializa al valor NR_OPEN, que en el archivo
include/asm/limits.h se define como 1024.
Estos límites se pueden consultar desde espacio de usuario con la llamada
getrlimit(); esta función utiliza una estructura de datos
rlimit, definida en include/linux/resource.h, que contiene dos
datos enteros para representar lo que se conoce como límite soft
o blando y límite hard o duro. El límite blando de un recurso
puede ser modificado por cualquier proceso sin privilegios que llame a setrlimit(),
ya sea para aumentar o para disminuir su valor; por el contrario, el límite
hard define un valor máximo para la utilización de un recurso,
y sólo puede ser sobrepasado por procesos que se ejecuten con privilegios
de administrador.
En el fichero include/linux/nfs.h podemos definir el puerto máximo
que los clientes NFS pueden utilizar (NFS_PORT);
si le asignamos un valor inferior a 1024 (puertos privilegiados), sólo
el administrador de otro sistema Unix podrá utilizar nuestros servicios
NFS, de forma similar a la variable nfs_portmon de algunos
Unices.
Para cambiar los límites de los parámetros vistos aquí la
solución más rápida pasa por modificar los ficheros de cabecera
del kernel, recompilarlo y arrancar la máquina con el nuevo núcleo;
sin embargo, a continuación vamos a hablar brevemente de Fork Bomb
Defuser, un módulo que permite al administrador modificar algunos de
estos parámetros sin reiniciar el sistema.
El kernel de Linux no permite por defecto limitar el número máximo
de usuarios y el número máximo de procesos por usuario que se pueden
ejecutar en el sistema sin tener que modificar el código del núcleo;
si no queremos modificarlo, casi no hay más remedio que utilizar un poco
de programación (unos simples shellscripts suelen ser suficientes)
y las herramientas de planificación de tareas para evitar que un usuario
lance demasiados procesos o que conecte cuando el sistema ya ha sobrepasado un
cierto umbral de usuarios conectados a él.
Mediante el módulo Fork Bomb Defuser se permite al administrador
controlar todos estos parámetros del sistema operativo, incrementando de
forma flexible la seguridad de la máquina. El código está
disponible en http://rexgrep.tripod.com/rexfbdmain.htm.
Por Secure Linux se conoce a una colección de parches para el núcleo
de Linux programados por Solar Designer, uno de los hackers más
reconocidos a nivel mundial en la actualidad (entendiendo hacker en el
buen - y único - sentido de la palabra). Este software, disponible
libremente desde http://www.false.com/security/linux/10.2, incrementa la seguridad que el núcleo
proporciona por defecto, ofreciendo cuatro importantes diferencias con respecto
a un kernel normal:
- Área de pila no ejecutable
En un sistema con el área de la pila no ejecutable los ataques de
buffer overflow son más difíciles de realizar que en los
sistemas habituales, ya que muchos de estos ataques se basan en sobreescribir
la dirección de retorno de una función en la pila para que apunte
a código malicioso, también depositado en la pila. Aunque
Secure Linux no es una solución completa, sí que añade
un nivel extra de seguridad en este sentido, haciendo que un atacante que
pretenda utilizar un buffer overflow contra nuestro sistema tenga
que utilizar código más complicado para hacerlo.
- Enlaces restringidos en /tmp
Con esta característica, Secure Linux intenta que los usuarios
sin privilegios puedan crear enlaces en /tmp/ sobre ficheros que
no les pertenecen, eliminando así ciertos problemas de seguridad que
afectan a algunos sistemas Linux, relacionados principalmente con condiciones
de carrera en el acceso a ficheros.
- Tuberías restringidas en /tmp
Esta opción no permite a los usuarios escribir en tuberías (
fifos) que no le pertenezcan a él o al root en directorios
con el bit de permanencia activo, como /tmp. De esta forma se evitan
ciertos ataques de Data Spoofing.
- /proc restringido
Esta es quizás la característica más útil de este
parche, aparte de la más visible para el usuario normal. Permite que
los usuarios no tengan un acceso completo al directorio /proc/ (que
recordemos permite un acceso a estructuras de datos del núcleo, como
la tabla de procesos, desde el espacio de usuario) a no ser que se encuentren
en un determinado grupo con el nivel de privilegio suficiente. De esta forma
se consigue un aumento espectacular en la privacidad del sistema, ya que por
ejemplo los usuarios sólo podrán ver sus procesos al ejecutar
un ps aux, y tampoco tendrán acceso al estado de las conexiones
de red vía netstat; así, órdenes como
ps o top sólo muestran información relativa a
los procesos de quién las ejecuta, a no ser que esta persona sea el
administrador o un usuario perteneciente al grupo 0.
El demonio auditd permite al administrador de un sistema Linux recibir
la información de auditoría de seguridad que el núcleo genera,
a través del fichero /proc/audit, filtrarla y almacenarla en
ficheros. Esta información tiene el siguiente formato:
AUDIT_CONNECT pid ruid shost sport dhost dport
Conexión desde la máquina al host remoto dhost.
AUDIT_ACCEPT pid ruid shost sport dhost dport
Conexión desde el host remoto dhost a la máquina.
AUDIT_LISTEN pid ruid shost sport
El puerto indicado está esperando peticiones de servicio.
AUDIT_OPEN pid ruid file
Se ha abierto el fichero file.
AUDIT_SETUID pid old_ruid ruid euid
Se ha llamado con éxito a setuid(), modificando el UID de
ruid a euid.
AUDIT_EXEC pid ruid file
Se ha ejecutado el fichero file.
AUDIT_MODINIT pid ruid file
Se ha insertado en el kernel el módulo file.
Al leer la información de /proc/audit, el demonio auditd
lee las reglas de filtrado del fichero /etc/security/audit.conf, comparando
los flags, PID y RUID ( Real User IDentifier)
recibidos con cada una de las reglas del archivo de configuración hasta
encontrar la apropiada para tratar el evento. Una vez que el demonio auditd
ha encontrado el fichero donde almacenar la información recibida, la guarda
en él con un formato legible.
Como en el caso de Linux - y de cualquier Unix - es conveniente detener los paquetes
que contengan en ellos el camino a seguir hasta su destino (lo que se conoce por
source routing, encaminamiento en origen); en Solaris esto se consigue
con la orden
anita:~# /usr/sbin/ndd -set /dev/ip ip_forward_src_routed 0
La orden ndd se utiliza para visualizar y modificar los parámetros
de un determinado driver; por ejemplo, si quisiéramos comprobar
los parámetros de /dev/ip, lo haríamos con la orden
anita:~# /usr/sbin/ndd /dev/ip \?
El uso del carácter `
' no es más que un escape del shell para el
símbolo `?'.
Mientras que en Linux era necesario el IP Forwarding para que el sistema
de filtrado de paquetes funcione correctamente, en Solaris es conveniente deshabilitar
esta opción para evitar que nuestro equipo se convierta en un router.
En algunas versiones de Solaris basta crear el fichero /etc/notrouter
para deshabilitar el rutado, pero se suele utilizar más a menudo la siguiente
orden:
anita:~# /usr/sbin/ndd -set /dev/ip ip_forwarding 0
Si queremos prevenir ataques de ARP Spoofing es conveniente dar un tiempo
de vida a las entradas de la tabla de direcciones físicas. En este caso
las órdenes a ejecutar (para un tiempo de vida de un minuto) son
anita:~# /usr/sbin/ndd -set /dev/ip ip_ire_flush_interval 60000
anita:~# /usr/sbin/ndd -set /dev/arp arp_cleanup_interval 60
Una máquina Solaris con más de un interfaz de red actúa automáticamente
como un router de paquetes entre los interfaces; hemos desabilitado el
IP Forwarding, pero para conseguir que los paquetes que lleguen por un
interfaz y tengan otro como destino se descarten, previniendo así el
Host Spoofing hemos de modificar las siguientes variables del kernel:
anita:~# /usr/sbin/ndd -set /dev/ip ip_strict_dst_multihoming 1
anita:~# /usr/sbin/ndd -set /dev/ip ip_ignore_redirect 1
Hay que resaltar que la configuración mediante ndd de los parámetros
anteriores permanecerá hasta que el sistema se reinicie, pero en ese momento
se perderá y todos los parámetros volverán a sus valores
por defecto; para solucionarlo, podemos crear un script que se ejecute
al iniciar el sistema y que lanze todas las órdenes vistas anteriormente.
Esto se puede hacer, por ejemplo, creando el fichero /etc/init.d/nddconfig
con el siguiente contenido10.3:
#!/bin/sh
#
# /etc/init.d/nddconfig
#
# Fix for broadcast ping bug
/usr/sbin/ndd -set /dev/ip ip_respond_to_echo_broadcast 0
# Block directed broadcast packets
/usr/sbin/ndd -set /dev/ip ip_forward_directed_broadcasts 0
# Prevent spoofing
/usr/sbin/ndd -set /dev/ip ip_strict_dst_multihoming 1
/usr/sbin/ndd -set /dev/ip ip_ignore_redirect 1
# No IP forwarding
/usr/sbin/ndd -set /dev/ip ip_forwarding 0
# Drop source routed packets
/usr/sbin/ndd -set /dev/ip ip_forward_src_routed 0
# Shorten ARP expiration to one minute to minimize ARP spoofing/hijacking
# [Source: Titan adjust-arp-timers module]
/usr/sbin/ndd -set /dev/ip ip_ire_flush_interval 60000
/usr/sbin/ndd -set /dev/arp arp_cleanup_interval 60
Tras crear este archivo hemos de enlazarlo con otro nombre en /etc/rc2.d/,
para que se ejecute al entrar en un runlevel 2, por ejemplo con la orden
anita:~# ln /etc/init.d/nddconfig /etc/rc2.d/S70nddconfig
En este archivo el administrador de un equipo Solaris puede definir variables
para el núcleo del sistema operativo, como el número máximo
de ficheros abiertos por un proceso o el uso de memoria compartida, semáforos
y mensajes para intercomunicación entre procesos. En este apartado vamos
a comentar algunos de estos parámetros que pueden afectar a la seguridad;
hablaremos especialmente de aquellos que pueden y deben ser limitados para evitar
diversos tipos de negaciones de servicio, ataques que recordemos afectan a la
disponibilidad de nuestros recursos. Los cambios que se realicen en este archivo
no tendrán efecto hasta que la máquina se reinicie con la orden
anita:~# reboot -- -r
o se cree el archivo /reconfigure y se reinice con un reboot
normal:
anita:~# touch /reconfigure
anita:~# reboot
Si deseamos ver el valor de alguno de los parámetros en el kernel
que se está ejecutando en este momento, lo podemos hacer con la orden
adb (nótese que no ofrece ningún prompt, hemos de escribir
directamente el parámetro a visualizar, con un `/D' al final
para que nos muestre el valor en decimal):
anita:~# adb -k /dev/ksyms /dev/mem
physmem 38da
maxusers/D
maxusers:
maxusers: 56
maxuprc/D
maxuprc:
maxuprc: 901
^d
anita:~#
Una negación de servicio muy típica en Unix es el consumo excesivo
de recursos por parte de usuarios que lanzan - voluntaria o involuntariamente
- demasiados procesos; esto es especialmente común en sistemas de I+D,
donde muchos usuarios se dedican a programar, y un pequeño error en el
código (a veces denominado `runaway fork') puede hacer que el sistema
se vea parado por un exceso de procesos activos en la tabla. La gravedad del problema
aumenta si pensamos que también es muy habitual que los usuarios lancen
simulaciones que tardan en ejecutarse varios días (o varias semanas), de
forma que una parada inesperada puede causar la pérdida de muchas horas
de trabajo. Por tanto, parece obvio que es recomendable limitar el número
de procesos simultáneos por usuario; en Solaris este número está
ilimitado por defecto, por lo que si deseamos asignar un valor máximo hemos
de editar el fichero /etc/system e incluir una línea como la
siguiente:
set maxuprc=60
De esta forma limitamos el número de procesos por usuario a 60 (un número
aceptable en la mayoría de sistemas10.4), consiguiendo así que un error en un programa
no sea capaz de detener la máquina.
Un parámetro del sistema operativo especialmente importante, y que quizás
nos interese modificar (sobre todo en máquinas con pocos recursos) es
maxusers. Al contrario de lo que mucha gente cree, maxusers no
hace referencia al número máximo de usuarios que pueden conectar
simultáneamente al sistema, sino que es un valor que escala a otros parámetros
del núcleo (como max_nproc, número máximo de procesos
en la tabla) o maxuprc. Para modificarlo, podemos incluir en /etc/system
una línea con el valor deseado, generalmente el tamaño en MB de
la memoria física de nuestra máquina ([Dik99]):
set maxusers=128
También puede ser conveniente limitar parámetros del sistema operativo
relativos al sistema de ficheros, ya que también se pueden producir negaciones
de servicio relacionadas con ellos. Por ejemplo, es interesante poder limitar
el número máximo de ficheros abiertos mediante los parámetros
rlim_fd_max (límite hard) y rlim_fd_cur (límite
soft) o evitar que los usuarios puedan utilizar chown() en sus
ficheros, especificando un valor 1 para la variable rstchown (este es
el comportamiento por defecto; si no lo seguimos, aparte de comprometer la seguridad
los usuarios sin privilegios podrían ignorar el sistema de quotas).
En algunas arquitecturas SPARC (concretamente en sun4u, sun4d
y sun4m) es posible establecer una protección hardware
para prevenir ataques de buffer overflow; para ello, en /etc/system
hemos de incluir una línea como
set noexec_user_stack=1
Y si además queremos monitorizar los intentos de ataque de este tipo, incluimos
en el archivo la línea
set noexec_user_stack_log=1
Si administramos un servidor NFS y deseamos que ignore las peticiones
de clientes que no provengan de puertos privilegiados (es decir, que no hayan
sido solicitadas por un usuario privilegiado de la máquina cliente) podemos
definir la variable NFS_PORTMON en /etc/system;
si usamos versiones de Solaris anteriores a la 2.5, debemos incluir una línea
como
set nfs:nfs_portmon = 1
mientras que en Solaris 2.5 y posteriores utilizaremos
set nfssrv:nfs_portmon = 1
Generalmente se recomienda utilizar la herramienta SAM ( System Administration
Manager) en los sistemas HP-UX, que además de las tareas clásicas
de administración permite modificar parámetros de un núcleo,
reconstruirlo e instalarlo en el sistema de una forma sencilla, guardando una
copia del kernel actual en /SYSBACKUP (algo muy útil,
ya que recordemos que un núcleo mal configurado puede hacer que la máquina
no arranque). Por tanto, desde SAM hemos de entrar en el menú `Kernel
Configuration' y desde ahí elegir los parámetros que deseamos
modificar para construir el nuevo kernel; como en el caso de Solaris,
podemos fijar el parámetro maxusers (también con un significado
similar al que esta variable posee en Solaris) y también el número
máximo de procesos por usuario (parámetro maxuprc).
Si deseamos modificar y reconstruir el nuevo núcleo a mano, el proceso
difiere de HP-UX 9.x a HP-UX 10.x. Los pasos en ambos casos son los siguientes:
| HP-UX 9.x |
| # cd /etc/conf |
| # cp dfile dfile.old |
| # vi dfile |
| # config dfile |
| # make -f config.mk |
| # mv /hp-ux /hp-ux.old |
| # mv /etc/conf/hp-ux /hp-ux |
| # cd / ; shutdown -ry 0 |
| HP-UX 10.x |
| # cd /stand/build |
| # /usr/lbin/sysadm/system_prep -s system |
| # vi system |
| # mk_kernel -s system |
| # mv /stand/system /stand/system.prev |
| # mv /stand/build/system /stand/system |
| # mv /stand/vmunix /stand/vmunix.prev |
| # mv /stand/build/vmunix_test /stand/vmunix |
| # cd / ; shutdown -ry 0 |
Al editar los ficheros /etc/conf/dfile (HP-UX 9.x) o /stand/build/system
(HP-UX 10.x) hemos de especificar los parámetros comentados anteriormente,
de la forma
maxuprc 60
maxusers 100
Otros parámetros a tener en cuenta relacionados con la gestión de
procesos son nproc (número máximo de procesos en el sistema),
nkthread (número máximo de hilos simultáneos en
el núcleo) o max_thread_proc (número máximo de
hilos en un proceso).
Igual que en Solaris - y en cualquier Unix en general - también nos puede
interesar limitar algunos parámetros relacionados con el sistema de ficheros,
de cara a evitar posibles consumos excesivos de recursos que puedan comprometer
nuestra seguridad. Por ejemplo, maxfiles indica un límite
soft a los ficheros abiertos por un proceso y maxfiles_lim un límite
hard (que obviamente ha de ser mayor que el anterior); nfile
indica el número máximo de ficheros abiertos en el sistema y
ninode el número de inodos (se recomienda que ambos coincidan). Por
último, nflocks indica el número máximo de ficheros
abiertos y bloqueados en la máquina.
Como en cualquier Unix, antes de pasar a modificar parámetros del núcleo
es conveniente guardar una copia del mismo para poder arrancar la máquina
en caso de problemas; en IRIX, esto lo conseguimos con una orden como la siguiente:
# cp /unix /unix.SAV
Una vez tenemos la copia, podemos pasar a modificar el kernel del sistema
operativo, igual que en los ejemplos anteriores, para evitar principalmente negaciones
de servicio por un consumo excesivo de recursos. Para modificar parámetros
hemos de utilizar la orden systune, como se muestra a continuación:
# systune -i
Updates will be made to running system and /unix.install
systune-> nproc
nproc = 400 (0x190)
systune-> nproc = 500
nproc = 400 (0x190)
Do you really want to change nproc to 500 (0x1f4)? (y/n) y
In order for the change in parameter nproc to become effective /unix.install
must be moved to /unix and the system rebooted
systune-> quit
#
En este ejemplo acabamos de consultar y modificar el valor del parámetro
nproc, que indica el número máximo de procesos en la máquina
(a continuación se comentarán con detalle algunos de estos parámetros
útiles de cara a la seguridad). Podemos comprobar que tras modificar su
valor los cambios se almacenan en un fichero llamado /unix.install,
que no es más que la nueva imagen del núcleo que acabamos de crear;
para que los cambios tengan efecto hemos de reiniciar el sistema, lo que automáticamente
moverá este nuevo kernel al fichero /unix: por eso hemos
de guardar previamente una copia de la imagen original en /unix.SAV,
por ejemplo.
Limitando el número total de procesos en la máquina a un valor aceptable
para nuestro sistema podemos evitar muchas negaciones de servicio; otra forma
de evitarlas es modificando el parámetro maxup, que representa
el número máximo de procesos por usuario; su valor, que por defecto
es 150, siempre se recomienda que sea menor que nproc-5 ([Zur94]).
Si lo que queremos es limitar el el número máximo de ficheros abiertos
por cada proceso podemos asignar el valor que nos interese al parámetro
rlimit_nofile_cur, que por defecto está a 200; el valor que le
asignemos siempre ha de ser menor que rlimit_nofile_max, por lo que
quizás también necesitemos modificar este parámetro.
Otros parámetros del núcleo que quizás nos resulte interesante
modificar de cara a nuestra seguridad son nosuidshells (si su valor
es distinto de 0 evita que las aplicaciones puedan crear shells setuidados
y pertenecientes al administrador), restricted_chown, que define si
el estilo de la llamada chown() es BSD (con un valor
0, indicando que sólo el administrador puede cambiar la propiedad de los
archivos) o System V (si su valor es 1 indica que cualquier usuario puede
utilizar chown()) o nfs_portmon (si es 1 los clientes NFS
sólo pueden ser lanzados por administradores remotos, porque han de utilizar
puertos privilegiados).
Pasando ya a la configuración del subsistema de red, si en IRIX queremos
deshabilitar el IP Forwarding (por defecto está activado en máquinas
con más de un interfaz) hemos de editar una configuración del
kernel ( /var/sysgen/master.d/bsd) y modificar el valor de la variable
ipforwarding de 1 a 0:
int ipforwarding = 0;
Una vez modificado este archivo hemos de ejecutar la orden autoconfig -f,
que al igual que sysinfo -i genera un fichero /unix.install
que se convierte en /unix (la imagen del núcleo) al reiniciar
el sistema.
Antes de finalizar esta sección hay que citar como consulta obligatoria
[JZRT99], una obra que proporciona a cualquier
administrador de IRIX una visión particular de la seguridad para este sistema
operativo, desde un repaso a las herramientas de backup hasta una descripción
de las listas de control de acceso, pasando por el sistema de monitorización
en IRIX.
La configuración de tunables en SCO Openserver se puede realizar
utilizando diversas herramientas del operativo, generalmente configure,
idtune, getconf o inconfig ([MS94]); por ejemplo, si deseamos modificar el número
máximo de procesos en el sistema, lo podemos hacer a través de
/etc/conf/cf.d/configure. Esta utilidad nos mostrará un menú
con diferentes categorías de parámetros configurables; en nuestro
caso debemos elegir MAX_PROC, disponible en la sección
Table limits de Configuration Tunables. También podemos
configurar aquí el máximo número de descriptores de fichero
en uso en el sistema, modificando el valor del parámetro MAX_FILE
(<este parámetro no controla el número máximo
de ficheros abiertos por proceso!).
Utilizando esta misma utilidad, pero ahora en la sección User and group
configuration podemos definir el número máximo de ficheros que
un proceso puede abrir simultáneamente (NOFILES), el tamaño
de fichero máximo que un usuario puede crear (ULIMIT), el
número de procesos simultáneos bajo un mismo identificador de usuario
distinto del root (MAXUP), el límite de memoria
virtual de un proceso sin privilegios (MAXUMEM) y el comportamiento
de la orden chown (CHOWN_RES, donde 0
- valor por defecto - indica que los usuarios no pueden modificar la propiedad
de los archivos).
Si modificamos parámetros del núcleo mediante configure
debemos reconstruir el kernel del sistema y situarlo en /etc/conf/cf.d/;
ambas cosas se consiguen mediante la orden link_unix, situada en ese
mismo directorio. Esta utilidad copiará además el núcleo
actual, /unix, en /unix.old, para poder arrancar con él
en caso de que algo grave suceda al introducir modificaciones:
cristina:~# cd /etc/conf/cf.d/
cristina:/etc/conf/cf.d# ./link_unix
The UNIX Operating System will now be rebuilt.
This will take a few minutes. Please wait.
Root for this system build is /.
The UNIX Kernel has been rebuilt.
Do you want this kernel to boot by default? (y/n) y
Backing up /unix to /unix.old
Installing new /unix
The kernel environment includes device node files and /etc/inittab.
The new kernel may require changes to /etc/inittab or device nodes.
Do you want the kernel environment rebuilt? (y/n) y
The kernel has been successfully linked and installed.
To activate it, reboot your system.
cristina:/etc/conf/cf.d#
Para configurar parámetros globales del subsistema de red en SCO Openserver
podemos utilizar la orden inconfig. Esta utilidad actualizará
los datos definidos en /etc/default/inet, así como los que el
núcleo en ejecución está utilizando; de esta forma, y al
contrario de lo que sucede al utilizar configure, no es necesario
reiniciar el sistema para que los nuevos valores se inserten en el kernel,
ya que inconfig lo actualiza de forma dinámica (si alguno de
los nuevos valores es erróneo, se mantienen los valores actuales para el
parámetro correspondiente).
La orden inconfig recibe como argumentos el parámetro a configurar
y su nuevo valor; así, si por ejemplo deseamos desactivar el IP Forwarding
en nuestra máquina (aunque por defecto ya lo está), podemos conseguirlo
con una orden como la siguiente:
cristina:~# inconfig ipforwarding 0
cristina:~#
Una máquina con el IP Forwarding desactivado aún reenviará
paquetes source route; para evitar que esto ocurra hemos de asignar al
parámetro ipnonlocalsrcroute el valor 0 (utilizado por defecto
en SCO Openserver).
Otro de los parámetros del subsistema de red en nuestra máquina
que nos puede interesar modificar de cara a aumentar la seguridad es el tiempo
de expiración de las entradas de la tabla ARP (por defecto,
establecido a veinte minutos); el parámetro de inconfig en este
caso será arpt_keep seguido del valor deseado. Además,
la tabla ARP se escanea cada cinco minutos en busca de entradas
caducas; podemos modificar este tiempo con el parámetro arpt_prune
de inconfig.
Para prevenir ataques de IP Spoofing contra el sistema, el núcleo
de SCO Openserver introduce un número aleatorio para generar los números
de secuencia y el incremento de los mismos en los paquetes TCP;
el parámetro tcp_secret es la semilla que alimenta al generador
de números aleatorios, y su valor puede ser cualquiera entre 0 y 2147483647.
El número de bits de tcp_secret utilizados realmente
como semilla lo define el parámetro tcp_seqbits, con un valor
entre 16 y 26; el valor por defecto, 21, es una buena elección para nuestra
seguridad: si tcp_seqbits es demasiado bajo, aumenta la posibilidad
de que un pirata pueda adivinar el número aleatorio que se genera - lo
que le facilitaría el ataque -, pero si es demasiado alto se reduce el
intervalo entre la aparición de dos números de secuencia iguales,
lo que evidentemente también facilita un ataque.
En este capítulo hemos hablado de ciertos parámetros del kernel
de un sistema Unix que pueden afectar a su seguridad, principalmente a nivel de
red y de límites de recursos (para prevenir ataques de negación
de servicio, voluntarios o involuntarios, por parte de los usuarios). Aunque las
bases de todos los problemas suelen ser comunes a cualquier Unix, se ha particularizado
la forma de evitarlos para algunos de los clones más utilizados; en el
caso de Unices no vistos aquí, pero también en los que hemos tratado
(se trata de información que puede cambiar entre versiones de un mismo
operativo), es indispensable - se dijo en la introducción y se
insiste ahora - consultar la documentación del sistema y asegurarse muy
bien de lo que se está haciendo antes de reconfigurar un núcleo,
ya que un error aquí puede ser fatal para la máquina.
En la tabla 9.1 se presentan de forma compacta
los parámetros vistos en este capítulo para los diferentes clones
de Unix; hemos preferido no dar valores `óptimos' para cada uno de ellos,
ya que el valor ideal viene dado por las características de cada sistema:
cada administrador ha de conocer lo que es habitual en sus máquinas para
de esta forma detectar lo inusual, y con ello los posibles problemas de seguridad
que puedan existir.
Tabla 9.1: Parámetros del núcleo para
diferentes Unices
|
|
Notas al pie
- ... priori10.1
- Sin embargo, ningún Trusted Unix tiene su código
disponible...
- ... http://www.false.com/security/linux/10.2
- Esta URL ya no existe, ahora los trabajos de Solar Designer se encuentran
en http://www.openwall.com/; gracias, David :).
- ... contenido10.3
- Ejemplo extraído de Solaris Security Guide, documento disponible
en http://www.sabernet.net, de autor desconocido.
- ... sistemas10.4
- Aunque en algunos documentos se recomienda, para otros Unices, un número
máximo de 200 procesos ([CH99]).
Next: Seguridad de la subred
Up: Seguridad del sistema Previous: Autenticación de usuarios
Índice General