chroot

Últimamente, tras ciertos problemas con las jaulas chroot en linux, me puse a investigar un poco, y la verdad es que me sorprendió lo rápido que encontré información acerca de “problemas” de seguridad al usar esta herramienta, y entrecomillo lo de problemas porque éstos están perfectamente documentados y realmente la herramienta trabaja como debe hacerlo. Primero de todo, para situarnos en contexto: chroot.

Yo siempre había usado chroot como herramienta de seguridad, como una capa más (nunca como única medida, por supuesto) para evitar que un servicio comprometido ponga en peligro a todo el sistema. Cual es mi sorpresa cuando en multitud de páginas encuentro que si se obtienen permisos de superusuario en la jaula, se puede salir de ella forma trivial. La siguiente frase es lapidaria: “Ability to chroot() implies the ability to break out of it”. Esto no es baladí: si se compromete, por ejemplo, un servidor BIND en una jaula y se consigue el usuario named, y tras esto se aprovecha una escalada de privilegios y el atacante consigue convertirse en superusuario, ya dispone de acceso a todo nuestro sistema, no solo a nuestra jaula. En esta pagina podemos ver varios ejemplos de como salir de una jaula, todos ellos muy simples: Secure chroot.

Como resumen, se puede conseguir acceso al sistema padre de tres formas:

  • Creando un directorio temporal y realizando chroot sobre él, si la llamada al original se realizó desde fuera del propio chroot
  • Utilizando múltiples veces la orden chdir(“..”)
  • Utilizando un descriptor de fichero abierto antes de realizar la primera llamada a chroot

Esto hace que exista poca diferencia entre enjaular o no un proceso, ya que con el usuario named, en un sistema sin enjaular, tampoco se podría hacer mucho. Es más, dado que la orden chroot() tan solo restringe a nivel de ficheros, el atacante dispone de nuestra red como si estuviera en el sistema base. El propio Alan Cox, tras una petición de BUG por semejante comportamiento, contesto: chroot is not and never has been a security tool. People have built things based upon the properties of chroot but extended (BSD jails, Linux vserver) but they are quite different.

Tal vez el problema es que muchos administradores usamos chroot pensando que efectivamente, funciona como los jails de BSD o los v-servers de linux, cuando esto no es así. El problema se agrava si además se utilizan herramientas como debootstrap, las cuales crean un entorno base, con las mismas herramientas y librerías que un sistema normal. Esto, que hace muy cómodo el crear un jaula, aumenta el peligro pues existen muchos más vectores de ataque para conseguir una escalada de privilegios una vez comprometido el servicio. Existen multitud de guías para minimizar estos problemas, como por ejemplo: chroot practices. A nivel general, se recomienda:

  • Comprobar que todos los descriptores de ficheros están cerrados cuando invocamos a chroot().
  • Copiar lo mínimo imprescindible para el funcionamiento de la jaula. Con ldd, lsof y revisando /proc podemos averiguar lo mínimo que necesita un servicio para funcionar.
  • NO montar /proc ni /dev con la orden mount –bind, pues el atacante tendrá entonces acceso automático a nuestro sistema.
  • Evitar, dentro de la medida de lo posible, cualquier forma/orden de cambio de usuario nominal a root.
  • Revisar todos los permisos dentro de la jaula, haciendo que el usuario root sea dueño de todos los ficheros posibles y eliminar cualquier permiso no necesario.

Tras todo esto, a los que utilizáis jaulas en Linux para añadir seguridad a ciertos servicios, y si como yo no estábais al corriente de alguno de los comportamientos descritos, recomendaros su revisión, pues la falsa sensación de seguridad es uno de las mayores problemas a los que tiene que enfrentarse un sysadmin.

Introducción a SystemTap

Al hilo de una antigua entrada de nuestro compañero Antonio Villalón acerca de Dtrace y Solaris, me propuse encontrar algo parecido para sistemas Linux. Dtrace te permitia recolectar información del sistema a muy bajo nivel para detectar anomalías en su funcionamiento y en algunos casos poder prevenir futuros problemas.

Tras un tiempo de búsqueda, y después de descartar varias opciones que no me parecían todo lo completas que deberían, me encontré con SystemTap, una maravillosa herramienta que nos permite monitorizar la actividad del kernel mediante scripts sin tener que compilar y reiniciar cada vez que se requiera ejecutar una prueba.

Es crítico para un administrador de sistemas tener un control total sobre que ocurre en el sistema y herramientas como SystemTap, que te permiten obtener datos precisos del funcionamiento del sistema son de gran ayuda. También aporta seguridad el hecho de poder tener controlado cualquier evento del sistema, ya no a nivel de aplicación, sino de kernel. Conocer qué procesos están abriendo cierto fichero, el consumo de red de cualquier proceso o la ejecución de X función dentro del kernel por Y proceso nos proporciona una ingente cantidad de información que puede resultar necesaria en caso de una detectar una intrusión.

Por el propósito que tiene, SystemTap no es sencillo. Aunque la base es fácil de comprender, su programación requiere conocimientos avanzados de los diferentes módulos del kernel de Linux, aparte de conocimientos de su propia sintaxis. Por suerte para nosotros, existe abundante documentación, y para facilitarnos la tarea y reducir la barrera de entrada existen ya multitud de scripts de ejemplo que nos permitirán ver su funcionamiento e imaginar las posibilidades que tiene SytemTap.

Su instalación es bastante sencilla. En una distribución Fedora basta con instarlo mediante Yum y luego usar un comando propio para que descargue e instale el paquete kernel-debug correspondiente a nuestra versión actual del kernel:

#yum install systemtap
#stap-prep

Como breve introducción, en SystemTap tenemos “events” y “handlers”. La construcción habitual en SystamTap suele ser la siguiente

#codigo#
probe event {statements}

En lo que respecta a los “events” existen dos tipos, síncronos y asíncronos. Un evento síncrono ocurre cuando cualquier proceso ejecuta una operación en el kernel. Como ejemplos de este tipo de eventos tenemos:

  • Entradas a syscalls.
  • Operaciones en el VFS (Virtual File System).
  • Entradas en funciones del kernel, por ejemplo “sys_open”.
  • Entradas en funciones de cierto modulo.
  • etc.

Los eventos asíncronos, por contra, son aquellos que no están ligados a ninguna instrucción particular en el código. Consisten principalmente en contadores y temporizadores.

Los “handlers” es la parte del código llamada cuando ocurre un “event”. Este código, una mezcla entre C y AWK, nos permite tratar la información del evento, almacenarla y en general, utilizar cualquier función disponible en cualquier lenguaje moderno (salida por pantalla, uso de vectores, funciones,…). Vamos a ver un ejemplo básico de “probe”, el cual almacenaremos en un fichero denominado probe.stp, y ejecutaremos con la orden stap probe.stp

####código####
probe syscall.open
{
printf ("%s(%d) open\n", execname(), pid())
}

En este ejemplo, cuando se realice cualquier llamada a la función open() el script escribirá por pantalla el nombre y el PID del proceso que ha ejecutado la llamada.

A partir de aquí, el asunto se puede complicar todo lo que queramos. Se pueden declarar funciones para factorizar el código, variables globales para compartir información entre handlers, timers, etc., y como con cualquier lenguaje nuevo hay que aprender la sintaxis. Por si esto no fuera suficiente, hay que conocer los eventos que podemos registrar en el kernel. Lo más recomendable para no desanimarnos es comenzar probando los scripts de prueba, ver como trabajan, y adaptarlos a nuestras necesidades, leyendo la documentación correspondiente en cada caso. En el wiki del proyecto encontrareis abundante información, y en Fedora, además, podemos instalar el paquete systemtap-testsuite el cual nos copiará en /usr/share/systemtap/testsuite/systemtap.examples/ multitud de ejemplos.

Por último, me gustaría compartir un script que me gustó mucho (está en el paquete de testsuite que hemos mencionado) y que nos permite conocer el consumo de red de cada proceso del sistema en tiempo real. Os invito a que lo reviséis, veáis como se programa un script un poco complejo y las posibilidades de la herramienta. El script tan sólo almacena en cada llamada a las funciones transmit y receive la información en vectores para luego, en función de un timer, mostrarla por pantalla. Aquí esta:

#! /usr/bin/env stap 
global ifxmit, ifrecv 
global ifmerged 

probe netdev.transmit 
{ 
  ifxmit[pid(), dev_name, execname(), uid()] < << length 
}

probe netdev.receive 
{ 
  ifrecv[pid(), dev_name, execname(), uid()] <<< length 
} 

function print_activity() 
{ 
  printf("%5s %5s %-7s %7s %7s %7s %7s %-15s\n", 
          "PID", "UID", "DEV", "XMIT_PK", "RECV_PK", 
          "XMIT_KB", "RECV_KB", "COMMAND") 

  foreach ([pid, dev, exec, uid] in ifrecv) { 
    ifmerged[pid, dev, exec, uid] += @count(ifrecv[pid,dev,exec,uid]); 
  } 

  foreach ([pid, dev, exec, uid] in ifxmit) { 
    ifmerged[pid, dev, exec, uid] += @count(ifxmit[pid,dev,exec,uid]); 
  } 

  foreach ([pid, dev, exec, uid] in ifmerged-) { 
     n_xmit = @count(ifxmit[pid, dev, exec, uid]) 
     n_recv = @count(ifrecv[pid, dev, exec, uid]) 
     printf("%5d %5d %-7s %7d %7d %7d %7d %-15s\n", 
            pid, uid, dev, n_xmit, n_recv, 
            n_xmit ? @sum(ifxmit[pid, dev, exec, uid])/1024 : 0, 
            n_recv ? @sum(ifrecv[pid, dev, exec, uid])/1024 : 0, 
            exec) 
  } 

  print("\n") 

  delete ifxmit 
  delete ifrecv 
  delete ifmerged 
} 

probe timer.ms(5000), end, error 
{ 
  print_activity() 
}

SELinux II: Práctica

Bueno, lo prometido es deuda, y aunque debería de pagar intereses por el tiempo transcurrido, vamos a pasar de la teoría de SElinux a la práctica.

Lo primero antes de trabajar con SElinux, es asegurarse de que lo tenemos habilitado, de que esta configurado en el modo adecuado y de la política de que tenemos. Para todo ello, en un sistema Red Hat, inspeccionamos el fichero /etc/sysconfig/selinux

jvela@centos:~$ less /etc/sysconfig/selinux
SELINUX=enforcing 
SELINUXTYPE=targeted

[Read more…]

SELinux: Teoria

Lo prometido es deuda, y hoy vamos a explicar un poco que es SELinux [Security-Enhanced Linux, enlace de la NSA] y cómo opera. Recomiendo releer la entrada sobre MAC y DAC para recordar conceptos. Tras esto, ¿qué es SELinux? SELinux es una implementacion del MAC que funciona a nivel de kernel, lo que implica que las aplicaciones no necesitan ser modificadas para aprovechar toda su potencia, ya que para ellas SELinux es transparente, algo muy importante a la hora de que el sistema sea desplegado en un entorno real.

La razón de usar SELinux es limitar el acceso que tienen las aplicaciones a otras aplicaciones y a los ficheros, impidiendo que un proceso pueda modificar cualquier fichero del usuario con el que se lanzó. Por ejemplo, Firefox jamas debería ser capaz de cambiar los permisos de mi clave privada ssh, lo cual con un sistema de control de acceso DAC sí sería posible si un atacante toma el control del navegador.

¿Cómo funciona SELinux? Pues se basa en atributos extendidos del sistema de ficheros que indican el tipo de usuario (que no tiene porque ser usuario del sistema), el rol, y el tipo del objeto. Por ejemplo, si hacemos un ls -lZ para ver los atributos extendidos del directorio /etc/httpd, podremos ver que permisos le asigna una distribución Red Hat a los ficheros de configuración de Apache (la quinta línea se ha partido para facilitar su presentación):

[root@localhost httpd]# ls -lZ
drwxr-xr-x root root system_u:object_r:httpd_config_t conf
drwxr-xr-x root root system_u:object_r:httpd_config_t conf.d
lrwxrwxrwx root root system_u:object_r:httpd_log_t logs -> ../../var/log/httpd
lrwxrwxrwx root root system_u:object_r:httpd_modules_t modules -> 
           ../../usr/lib/httpd/modules
lrwxrwxrwx root root system_u:object_r:httpd_config_t run -> ../../var/run

Como podemos ver, el usuario y rol es el mismo, pero el tipo cambia. Estos atributos son los que en función de las políticas definidas en SELinux, indican las interacciones entre ellos y los diferentes objetos del sistema. Estos atributos están definidos en el sistema, y puede haber cientos, tantos como la granularidad que queramos obtener. La mayoría de distribuciones que usan SELinux vienen ya con varios tipos predeterminados para ayudarnos en su administración, pero nosotros podemos definir más. Si queremos ver todos los tipos disponibles, lo podemos hacer con el comando

#getsebool -a

Ahora que ya conocemos los atributos, ahora nos falta conocer un poco las políticas que utilizan estos atributos. Al más alto nivel, existen dos tipos de políticas básicas: targeted y strict:

  • strict: En modo estricto, por defecto todo está denegado, y tan sólo se permite lo especificado en la políticas. Esto, que sigue el principio de mínimo privilegio, es complicado de administrar y llevaría a la mayoría de los sysadmins a desactivar SELinux.
  • targeted: Tan sólo ciertos servicios están bajo la supervisión de SELinux, como por ejemplo los demonios httpd, bind, postgresql, etc. El resto están libres de restricciones. La denominacion Targeted proviene del hecho de que tan solo los servicios señalados serán vigilados.

Para saber qué tipo de política tenemos configurada, en un sistema Red Hat podemos averiguarlo en el fichero /etc/sysconfig/selinux.

Por debajo de estos modos, se sitúan las políticas en sí, que definen las interacciones entre objetos. Dichas políticas pueden usar diferentes controles de acceso, en función de los atributos extra que utilicen. Normalmente se suele usar Type Enforcement en el cual tan solo se utiliza el atributo tipo, y es el modo usado por la política targeted. Existen otros métodos como el Role-Based Access Control que utiliza los atributos de usuario y rol pero normalmente, dado que como política se utiliza targeted, el modo de acceso mas común es el Type Enforcement.

Ya tan sólo nos queda saber qué es lo que exactamente permite o impide que podamos acceder a un objeto. Aquí entran en juego las políticas, que dependiendo del control de acceso utilizado y los atributos de los objetos, permiten o deniegan un acceso. Así pues, si usamos el modo de Type Enforcement, es el tipo de un objecto el atributo determinante. En este caso, si un servicio esta en el modo vigilado tan solo podrá acceder a objetos con un tipo similar. En las políticas se definen los tipos similares y las excepciones que permiten que el sistema funcione. Así, por ejemplo, si inspeccionamos con que permisos se ejecuta el demonio Apache (la segunda línea se ha partido para facilitar su presentación):

[root@localhost etc]# ps auxZ | grep httpd
user_u:system_r:httpd_t  root  2869 0.3 0.5 10548 2924 ? Ss 15:46 0:00 
          /usr/sbin/httpd

Vemos que dicho demonio tan solo podrá interactuar con objetos de un tipo similar, como por ejemplo los que están en el directorio /etc/httpd. Esto implica que aunque nosotros tuviéramos un fichero con permisos de lectura y escritura para todos, como por ejemplo:

[root@localhost jvela]# ls -lZ /home/jvela
drwxr-xrwx jvela jvela user_u:object_r:user_home_t  prueba_selinux

Un atacante que comprometiera el demonio de httpd no podría acceder porque dicho demonio esta en el dominio de SELinux y el tipo de nuestro archivo no pertenece a la “familia” httpd.

Hemos visto por encima que es SElinux, cómo esta estructurado y como consigue aplicar el MAC a un sistema GNU\Linux. Por supuesto, como pasa siempre, la mejor manera de comprender su funcionamiento es jugando con él y poniéndolo en marcha, algo a lo que les invito. No obstante, si les queda alguna duda o hay algo de lo dicho sobre lo que les gustaría que incidiese, no tienen más que indicarlo en los comentarios y estaré encantado de aclarar las dudas que tengan. En la siguiente entrada pasaremos de la teoría a la práctica, con ejemplos de su funcionamiento y las múltiples opciones de configuración que lo convierten en un sistema tan flexible.

Saludos, y hasta la próxima.

ACL en GNU/Linux (II)

En el anterior post descubrimos las bondades de las ACL en GNU/Linux, i.e. cómo se podía aumentar la seguridad de nuestro sistemas a través de la granularidad que este sistema de acceso nos ofrecía. Hoy toca la parte menos bonita de las ACL, que en parte son la razón de que no sean ampliamente utilizadas en todo los sistemas Linux.

Una de las primeras cosas que saltan a la vista al usar ACL es que no son fáciles de administrar, al menos no tanto como los tradiciones controles de acceso. La información no está a la vista, y hay que usar comandos para inspeccionar cada fichero. Dado que cada objeto puede tener un numero variable de ACLs, puede resultar tedioso averiguar que permisos tienen los ficheros de un directorio determinado. Esto nos obliga a intentar reducir las ACL al mínimo, intentando usar las mismas ACL en todos los ficheros de un directorio e intentar usar tan sólo ACL de grupo para no tener casos demasiado específicos, lo que obviamente va contra la filosofía de las ACL.

En segundo lugar están los temas puramente técnicos. Como buenos administradores, deberemos hacer copias de seguridad, y es aquí cuando nos encontramos que el comando cp no funciona adecuadamente si no usamos la opción preserve:

# cp --preserve=all  ./ACL/prueba ./ACL2/

Así, al hacer copias de seguridad muchas veces usaremos el comando tar para empaquetar, y aquí es cuando nos damos cuenta de que tar no soporta los atributos extendidos en el sistema de ficheros. Investigando un poco aparece star, un tar “posix compliant” que permite realizar, usando un sistema de empaquetado especial, manejar dichos atributos. Para comprimir y empaquetar usaríamos:

# star -c acl2 -file=acl2.tar -H=exustar -acl 

-c: Directorio a archivar
-file: Nombre del fichero de salida
-H: formato del archivo tar, en nuestro caso exustar que es el que soporta ACL
-acl: Archiva los atributos extendidos de ACL

Para desempaquetar, más sencillo:

#star -vx file=acl2.tar

También querremos poder heredar permisos de las carpetas padre, para facilitar la tarea de asignar ACL y forzar a que dichos archivos tengan unos determinados permisos. Para ello debemos de recurrir a las “default ACL” que nos permiten indicar las ACL por defecto que heredará un objeto al crearse dentro de una carpeta padre, lo cual en Unix con los controles de acceso tradicionales se hace con el bit setgid.

#setfacl -d --set u:jvela:rw-,g:jvela:r--,o::- prueba_dir
# file: prueba_dir/ 
# owner: jvela 
# group: jvela 
user::rwx 
group::r-x 
other::r-- 
default:user::rw- 
default:group::r-- 
default:other::---

Aquí estamos asignando una ACL por defecto, sobrescribiendo las anteriores (se usa –set, no -m) para que todos los archivos que se creen en dicho directorio tengan una ACL para el usuario y grupo jvela
En este caso volvemos a toparnos con el mismo problema. Las posibilidades son enormes, pero su administración es compleja. Las ACL permiten mucho más, desde asignarlas recursivamente con la opción -R como con cualquier comando Unix, hasta clonar ACL desde un objeto. Podremos realizar casi cualquier tarea que realizamos con los controles de acceso normales, y con mucha más flexibilidad, pero con la contrapartida de una administración más compleja.

Hasta aquí todo lo básico para manejar ACL, donde hemos visto desde sus virtudes y la versatilidad que proveen hasta los problemas que conllevan y el cuidado que hay que tener con ellas. Nada más sobre este tema. Sean buenos y no acaben en un Active Directory por usar ACL ;)

ACL en GNU/Linux

Hoy vamos a introducirnos en el fascinante mundo de las ACL (Access Control List) en Linux. En este punto ya habrá alguien que piense que eso es más viejo que el mismo Unix, pero mucha gente, entre que los que me incluyo, no tenia ni idea de que existían las ACL en Linux, y menos cómo usarlas. Empecemos, como siempre, con un poco de teoría cortesía de la Wikipedia:

Una Lista de Control de Acceso o ACL (del inglés, Access Control List) es un concepto de seguridad informática usado para fomentar la separación de privilegios. Es una forma de determinar los permisos de acceso apropiados a un determinado objeto, dependiendo de ciertos aspectos del proceso que hace el pedido.

Así pues, las ACL nos permiten determinar si una entidad puede acceder o no a un objeto, y qué acciones puede realizar contra dicho objeto. Bien, hasta aquí suena exactamente igual que los típicos controles de acceso de Linux, y es normal, dado que la instancia mas simple de una ACL son los permisos habituales de propietario/grupo/otros que tan bien conocemos. La diferencia radica en que con las ACL se puede hilar mucho más fino, especificando varios grupos y usuarios y detallando para cada uno sus permisos específicos.

[Read more…]

Sistemas de control de acceso: MAC Y DAC

En esta entrada quiero refrescar la memoria a todos nuestros lectores acerca de las políticas de acceso más comunes en el área de la informática: Mandatory Acces Control (MAC) y Discretionary Acces Control (DAC). Asimismo, y si el tiempo me lo permite, me gustaría aprovechar para iniciar una serie de entradas acerca de SELinux.

Pero vayamos a la cuestión: ¿qué es el MAC, DAC y cuales son sus diferencias?

Discretionary Acces Control es una forma de acceso a recursos basada en los propietarios y grupos a los que pertenece un objeto. Se dice que es discrecional en el sentido de que un sujeto puede transmitir sus permisos a otro sujeto. La mayoría de sistemas Linux ahora mismo usan este tipo de acceso, estando los permisos orquestados por grupos y usuarios, pudiendo un usuario normal cambiar los permisos de los archivos que posee con el comando chmod.

Mandatory Acces Control en cambio se basa en políticas. Existen un conjunto de reglas de autorización (políticas) las cuales determinan si una operación sobre un objeto realizada por un sujeto esta o no permitida basándose en los atributos de ambos. En este caso, el Mandatory refleja el hecho de que las políticas están centralizadas y no pueden ser sobrescritas por un sujeto.

[Read more…]

Exportación de certificados java

Hace poco tuvimos la necesidad de incluir unos certificados SSL firmados por VeriSign para la navegación HTTPS en un proxy inverso Squid. El problema fue el hecho de que el cliente que lo requería había gestionado personalmente la creación y firma del certificado con una entidad certificadora internacional, la cual realizó todo el proceso con una herramienta propietaria basada en JAVA. Esto hizo que nosotros recibiéramos un fichero binario ilegible por OpenSSL, y sobre todo, por Squid.

Tras indagar un poco y averiguar que la aplicación del cliente había hecho uso de la herramienta keytool de JAVA respiré aliviado, pues suponía que seria trivial obtener los datos. Primeramente, pensando que en 20 minutos podría estar almorzando inspeccione el contenedor mediante el comando:

# keytool -list -v -keystore server_keystore

[Read more…]

¿Peor es mejor?

mvsbEn mis primeros días de Erasmus en Londres, un profesor de una asignatura sobre sistemas distribuidos y seguridad nos dio a leer un curioso texto llamada “Worse is better” (Peor es mejor) donde se explicaba, a través de una curiosa discusión sobre sistemas operativos entre una persona del MIT y otra de la universidad de Berkeley, dos escuelas totalmente diferentes a la hora de llevar a termino un proyecto.

Mientras la escuela nacida del MIT defendía hacer las cosas “correctamente”, tomara el tiempo y el esfuerzo que tomara, y siguiendo los principios básicos de simplicidad, corrección, consistencia y completitud, la postura de la gente de Berkeley era más pragmática y hacia más hincapié en obtener un producto “suficientemente bueno” en un tiempo razonable, de forma que pudiera ser usado lo más pronto posible y a partir de ahí ir corrigiendo los fallos que pudieran quedar.

Estas dos filosofías se pueden aplicar a prácticamente cualquier ámbito de la informática, incluyendo, por supuesto, la seguridad. Ante el lanzamiento de un producto o servicio totalmente nuevo para la compañía, se puede optar por hacer análisis exhaustivos, empaparse de toda la documentación disponible perfilando al milímetro cada aspecto del proyecto y realizando complejas pruebas para asegurar que no existe ningún fallo de seguridad —llegando incluso a descartar el proyecto por no poder conseguir un producto realmente seguro tomando el tiempo que ello requiera— o por el contrario se puede hacer un análisis más ligero, ajustar los aspectos críticos e imprescindibles que realmente puedan poner en verdadero peligro al proyecto, y lanzar una versión suficientemente buena como para que los usuarios la adopten en un plazo aceptable, mientras sobre la marcha se van puliendo todos los aspectos que se quedaron en el tintero.

[Read more…]