Linux kernel capabilities: No solo de sudo vive root

En artículos recientes, se habló de los mecanismos de control de acceso y de su papel en la seguridad de sistemas operativos multiusuario. Gracias a estos mecanismos el sistema crea un contexto de trabajo para cada usuario, en el cual ejecutan sus tareas con los privilegios asignados al mismo. Sin embargo, para realizar operaciones específicas es necesario que un usuario no privilegiado pueda, en ocasiones, adquirir temporalmente un perfil de superusuario (root) y poder realizar una tarea que suponga ciertos privilegios.
Este objetivo se consigue principalmente atribuyendo privilegios a través de sudo, o asignando permisos especiales "set user-id" o setuid a un fichero ejecutable. Este mecanismo permite que un usuario adopte el rol del propietario del fichero cuando lo ejecuta, adquiriendo los mismos privilegios que éste. Este tipo de permiso se identifica con el atributo "s" al listar ficheros ejecutables, indicativo que el bit setuid está activo.
Es posible comprobar los ficheros que tienen permiso setuid con el comando:
find / -perm -4000 -exec ls -l {} \; 2>/dev/null
- Buscando ficheros con setuid en el sistema. Obsérvese la "s" indicativa -
Es importante tener presente que estos mecanismos traen consigo riesgos inherentes al cambio de contexto de privilegios que sucede durante la ejecución. Esta circunstancia constituye una brecha de seguridad cuando existe una vulnerabilidad en el ejecutable que permite tomar el control de la ejecución, escapando del contexto de seguridad del proceso manteniendo los privilegios.
- Elevación de privilegios: sudo, setuid… ¿estás seguro? -
Es por ello que tanto los permisos atribuidos por sudo como por setuid deben ser cuidadosamente asignados y administrarse con precaución por el riesgo de seguridad que suponen.
En Linux, para controlar estas circunstancias existen mecanismos de seguridad adicionales que hacen uso del framework LSM (Linux Security Modules) disponible desde la versión 2.6 del kernel. Un ejemplo de ello es el control de acceso obligatorio (en inglés MAC, Mandatory Access Control), como las implementaciones, SELinux o AppArmor.
No obstante y a pesar de contar con estas protecciones, el hecho de asumir con todas las consecuencias el rol de un usuario privilegiado entraña grandes riesgos y choca de frente con el principio de mínimo privilegio, que debe ser considerado siempre como axioma en cuanto a seguridad se refiere.
No seas root, usa las capabilities
Las capabilities es un concepto que aporta un sistema de seguridad que permite "trocear" los privilegios de root en distintos valores. Estos valores pueden ser asignados de forma independiente a procesos de modo que para realizar una operación privilegiada, ese proceso cuente únicamente con el permiso necesario sin tener que asumir la identidad de superusuario.
Gracias esta granularidad que las capabilities proporcionan y al hecho de no necesitar adquirir la identidad de root, constituyen un método muy útil para ejecutar tareas privilegiadas con los mínimos permisos necesarios. Estas facilidades que las capabilities aportan, las convierten en un elemento de seguridad muy importante en muchos sistemas. Así, las capabilities se utilizan, por ejemplo, en entornos de virtualización como los contenedores de Linux o Docker donde juegan un papel fundamental en la gestión de los contextos de seguridad.
Sin embargo, a pesar de estar presentes desde la versión 2.2 del kernel de Linux, el uso de las capabilities no es frecuente y en general es bastante desconocido por parte de administradores y/o usuarios.
Las capabilities constituyen una buena herramienta para asegurar sistemas Linux, pero siempre bajo un conocimiento de las posibilidades que una capability particular proporciona. En caso contrario, la asignación de forma arbitraria puede ser aprovechada para realizar una operación a priori, no contemplada.
Conociendo las capabilities
Actualmente existe una extensa lista de capabilities disponible, definidas en la librería capability.h.
Sin entrar en detalles demasiado profundos, en un kernel con esta implementación de seguridad, los ficheros y procesos del sistema cuentan con un juego de 3 flags para las capabilities que son: P(Permitted), E(Effective) e I(Inheritable) los cuales son utilizados para decidir, utilizando un algoritmo, el privilegio final a aplicar en la ejecución. La descripción de estos flags y el algoritmo pueden consultarse en detalle en el siguiente enlace: POSIX Capabilities & File POSIX Capabilities
En esencia, asignando a un fichero una capability con flags E y P se puede decir que el proceso derivado en la ejecución tendrá esa capability sin hacerla extensible a los posibles procesos hijos.
¿Pero que ganamos con este lío? Pues básicamente evitar conceder a un proceso elevar privilegios a nivel de superusuario cuando realmente no necesita más que ciertos permisos para una operación concreta.
Capabilities en acción
Por mostrar un ejemplo, la capability CAP_SYS_TIME se puede asignar para que cualquier usuario no privilegiado pueda cambiar la fecha del sistema sin convertirse en root. Lógicamente permitir esto a un usuario cualquiera no es una buena idea, pero sirve para explicar un manejo básico.
Asignando la capability CAP_SYS_TIME, con los flags adecuados al comando date, se autoriza a que su ejecución permita al proceso adquirir el privilegio de cambio de tiempo del sistema:
- Efecto de asignar la capability CAP_SYS_TIME a /bin/date -
Con ello se logra realizar una operación privilegiada sin asumir la identidad de superusuario.
Usando las capabilities es posible conseguir que usuarios o procesos realicen tareas privilegiadas con una mayor granularidad a la hora de asignar los permisos necesarios. Suponiendo que en el caso de usar setuid para conceder privilegio a /bin/date, una vulnerabilidad en el binario se permitiese a un atacante obtener una shell en la ejecución: el resultado sería una shell con privilegios de root.. game over. Sin embargo, en el caso anterior únicamente se obtendría el privilegio cap_sys_time o incluso ninguno, al no especificar como heredable el privilegio.
Manejo de capabilities
Los paquetes de Linux libcap, libcap-bin incorporan las librerías y binarios necesarios para el uso y la administración de capabilities.
Los comandos proporcionados son:
getcap: Lista las capabilities de un fichero
setcap: Asigna/borra capabilities a un fichero
getpcaps: Lista las capabilities de un proceso
capsh: Proporciona un "interfaz" de línea de comandos para probar y explorar el uso de capabilities
Adicionalmente el paquete libpam-cap, proporciona soporte a PAM (Pluggable Authentication Modules). EL uso de capabilities combinado con PAM incrementa aún más las posibilidades, de modo que es posible asignar capabilities dependiendo del usuario que hace login, evitando atribuir de esta forma una capability a un fichero que aplique a la ejecución por cualquiera.
En conclusión, las capabilities aportan una granularidad que facilita la siempre complicada tarea de asegurar un sistema. Para administradores de sistemas representan una herramienta de gran valor para diseñar una estrategia de seguridad más avanzada y precisa en un escenario multiusuario con distintos niveles de privilegio. Este mecanismo contribuye a allanar el terreno cuando se trata de aplicar el "dogma" de seguridad que representa el principio de mínimo privilegio. No obstante y como siempre, la asignación de cualquier tipo de privilegio debe analizarse con sumo cuidado.
Para implementar una estrategia de seguridad robusta basada en capabilities debe tenerse en cuenta su integración con las demás capas de seguridad del sistema: control de acceso DAC, MAC, PAM y SELinux.