Cómo realizar una prueba completa de seguridad del módulo del kernel de Linux

publicado en: drivers | 0

Las pruebas de seguridad se están volviendo esenciales para todos los negocios. Los errores y vulnerabilidades de seguridad no detectados pueden tener consecuencias costosas o incluso pérdidas de las que las empresas no pueden recuperarse.

Los problemas de seguridad se suelen tener en cuenta al desarrollar soluciones web y SaaS. Sin embargo, la seguridad es importante para todos y cada uno de los productos, sin importar los objetivos que sirvan y las tecnologías que utilicen. En Apriorit, siempre incluimos las pruebas de seguridad en nuestras estrategias de control de calidad.

En este artículo, nos centramos en algunos aspectos cruciales de las pruebas de seguridad de Linux. Al desarrollar un sistema para proteger las máquinas virtuales de Linux con la ayuda de un módulo del núcleo de Linux, prestamos atención a la seguridad del componente más esencial – el módulo del núcleo. Los problemas de seguridad cuando se carga y ejecuta un módulo del núcleo pueden hacer vulnerables a las máquinas virtuales y evitar que todo el sistema funcione correctamente.

A continuación, compartimos nuestra experiencia probando la seguridad de los módulos del núcleo de Linux. Este post será útil para los desarrolladores e ingenieros de control de calidad que están trabajando con el desarrollo del núcleo de Linux y se preocupan por las pruebas de los módulos del núcleo de Linux.

Probando un módulo antes de cargarlo en el kernel de Linux

Las pruebas de seguridad de un módulo del núcleo de Linux tienen que empezar en la etapa más temprana – el momento en que el núcleo se carga cuando el sistema arranca. La razón es que los hackers pueden encontrar vulnerabilidades en esta misma etapa y usarlas para alterar o comprometer el módulo.

Por eso es mejor comenzar las pruebas del módulo del núcleo de Linux con el examen del sistema de inicialización y la identificación de sus puntos débiles.

Cuando un sistema arranca, el propio núcleo carga la mayoría de los módulos del núcleo. Los módulos adicionales se cargan más tarde si se mencionan en etc/módulos. Los módulos se cargan junto con sus dependencias por el demonio modprobe. Además, los módulos del núcleo tienen opciones que pueden establecerse usando los archivos de configuración en /etc/modprobe.d/> o /lib/modprobe.d/.

Así, nuestra primera vulnerabilidad potencial se esconde en los archivos de configuración en modprobe.d. Usando los archivos de configuración, los ciberdelincuentes pueden impedir que un módulo se cargue cuando el sistema se inicia.

Hay varias maneras de evitar que un módulo se cargue. Exploremos en detalle los tres escenarios más comunes.

Servicios conexos

Pruebas de seguridad

Añadir un módulo a la lista negra

Una posible forma de detener la carga de un módulo del núcleo es ponerlo en una lista negra en un archivo de configuración. Hay dos maneras de hacerlo.

1. Crear un archivo *.conf.; en /etc/modprobe.d con el comando «blacklist <nombre del módulo

Aquí hay un ejemplo para Ubuntu y el módulo r8169 para un controlador de tarjeta de red:

                # echo "blacklist r8169"; /etc/modprobe.d/1.conf# update-initramfs -u# reboot

Cuando el sistema arranque, el módulo r8169 no se cargará.

Sin embargo, puedes cargarlo manualmente usando este comando:

                sudo modprobe r8169

Tenga en cuenta que puede agregar «lista negra <nombre del módulo»; a cualquier *.conf existente en la carpeta /etc/modprobe.d.

2. Crear un archivo *.conf en /lib/modprobe.d con «blacklist <nombre del módulo

Ejemplo para Ubuntu:

                # echo "blacklist r8169"; /lib/modprobe.d/1.conf# update-initramfs -u# reboot

El resultado es el mismo: el controlador de la tarjeta de red no se cargará cuando el sistema se inicie.

Desactivando un módulo del núcleo

Aparte de añadir módulos a una lista negra, hay otras formas de deshabilitarlos para que puedan&aposicionarlos.

Para desactivar un módulo, puede redirigirlo usando el comando de instalación. Si lo hace, modprobe intentará cargar el archivo conectado y definir el módulo de forma incorrecta (por ejemplo, como /bin/true o /bin/false), por lo que no se cargará.

Ejemplo:

1. Crear un archivo *.conf en /etc/modprobe.d con el comando "install <nombre del módulo> /bin/false" o "install <nombre del módulo> /bin/true":

…pre..;
# echo "install r8169 /bin/true" > /etc/modprobe.d/1.conf# update-initramfs -u# reboot
…y que no se puede hacer nada..;

Una vez más, el módulo r8169 no se cargará cuando el sistema arranque.

Tenga en cuenta que puede agregar "instalar <nombre de módulo> bin/true" a cualquier archivo *.conf existente en la carpeta /etc/modprobe.d.

2. 2. Crear un archivo *.conf en /lib/modprobe.d con "install <nombre del módulo> /bin/false" o "install <nombre del módulo> /bin/true", como se muestra en el ejemplo siguiente:

…pre..;
# echo "instalar r8169 /bin/true" > /lib/modprobe.d/1.conf# update-initramfs -u# reboot
…y que no se puede hacer nada..;

Cuando el sistema arranque, el módulo r8169 no se cargará.

Poner un módulo en la lista negra a través de los ajustes del cargador de arranque GRUB

Aparte de los archivos de configuración en /etc/modprobe.d/ y /lib/modprobe.d/, otra posible vulnerabilidad radica en la configuración del cargador de arranque GRUB, que también le permite poner en la lista negra los módulos del núcleo.

Por ejemplo, puedes añadir este código a /etc/default/grub:

…pre..;
GRUB_CMDLINE_LINUX_DEFAULT="modprobe.blacklist=<nombre_de_módulo>"
…y la lista de los módulos..;

En este caso, el módulo especificado no se cargará cuando el sistema se inicie.

Al realizar las pruebas de seguridad de los módulos del núcleo de Linux, es fundamental comprobar las principales vulnerabilidades que pueden permitir evitar que se cargue un módulo específico del núcleo. El sistema bajo prueba tiene que comprobar el módulo del kernel y protegerlo para que no se cargue.

Comprobación de la inicialización del módulo en el sistema de inicio

Además de cargar un módulo del núcleo mediante el modprobe, también puedes intentar cargarlo automáticamente en el sistema de inicio. Aunque esta es una forma inusual de cargar un módulo del núcleo, puede ser usado en un sistema bajo prueba.

Exploremos cómo funciona la carga automática del núcleo en el sistema de inicio.

Cuando un módulo del núcleo se carga, el sistema de inicio monta un sistema de archivos raíz. Entonces el programa init se lanza e inicializa el sistema bajo prueba.

Los ajustes de inicialización se describen en el archivo de configuración de etc/inittab. Uno de los ajustes esenciales es el nivel de ejecución, un estado operativo preestablecido para un sistema operativo de tipo Unix.

Hay siete niveles de ejecución para los sistemas de inicialización de Linux:

  • 0 – Parada del sistema
  • 1 – Modo monousuario
  • 2 – Modo multiusuario sin red
  • 3 – Modo multiusuario con red
  • 4 – No utilizado/definible por el usuario
  • 5 – Modo multiusuario con red, administrador de pantalla y soporte X11
  • 6 – Reinicio del sistema

Una vez que se determina el nivel de ejecución, el init ejecuta enlaces simbólicos del catálogo /etc/rc.d/rcN.d, donde N es el número de nivel de ejecución.

Los enlaces se nombran según el formato S<num><name>, de modo que se incluyen tanto el nombre como el número de orden del guión. Los guiones, a los que se hace referencia mediante los enlaces, se almacenan en la carpeta /etc/rc.d/init.d.

Si almacena un script de inicio del módulo del kernel en la carpeta /etc/rc.d/init.d y pone enlaces a este script en las carpetas etc/rc.d/rcN.d, el módulo del kernel se cargará después de que el sistema se inicie sin usar modprobe.

Esto significa que hay otras vulnerabilidades potenciales en el proceso de carga del módulo del núcleo. Si un módulo del núcleo se carga a través de un script en /etc/rc.d/init.d, los hackers pueden evitar que se cargue:

  • Cambiar o eliminar el script de inicio en /etc/rc.d/init.d
  • Cambiar o eliminar los enlaces simbólicos en las carpetas /etc/rc.d/rcN.d

Cuando se prueba un sistema, es crítico comprobar si puede detectar y reaccionar a cualquier cambio en la carga automática de scripts y en los enlaces simbólicos con los niveles de ejecución requeridos.

Servicios conexos

Desarrollo del núcleo y del controlador

Validar un módulo del núcleo antes de cargarlo en el núcleo de Linux

Los módulos del núcleo son responsables de varios procesos. Por lo tanto, tienes que asegurarte de que el módulo que vas a cargar en el núcleo de Linux es el que planeaste cargar.

Un error tan simple como cargar el módulo equivocado puede tener graves consecuencias:

  • Fuga de datos
  • Interrupción de datos
  • Cifrado forzado de datos
  • Corrupción de la funcionalidad del módulo del núcleo
  • Pánico en el núcleo de Linux

El pánico del núcleo es una medida de seguridad adoptada por el núcleo de un sistema operativo al detectar un error interno fatal del que el núcleo no puede recuperarse con seguridad o tras el cual el sistema no puede seguir funcionando sin un elevado riesgo de pérdida de datos importante.

Para evitar consecuencias negativas, siempre valide un módulo del núcleo antes de cargarlo en el núcleo de Linux. Esto se puede hacer de varias maneras.

Veamos más de cerca los dos métodos más populares para validar un módulo del núcleo.

Validación del hash de un módulo del núcleo

Cualquier modificación de un módulo lleva a un cambio en su hachís. Para validar la corrección de una modificación, se puede usar el Algoritmo de Hash Seguro 1 (SHA-1).

La verificación de los archivos con SHA-1 se hace produciendo una suma de comprobación antes de que el archivo se haya transmitido, y luego otra vez una vez que llega a su destino. El archivo transmitido puede considerarse genuino sólo si las sumas de verificación son idénticas.

Si aparece alguna modificación no autorizada o el módulo malicioso se disfraza como el suyo, la suma de comprobación descubrirá el intento de comprometer su hachís. Las posibilidades de que los hackers consigan la suma de comprobación correcta son más que bajas.

Verificación de la firma de un módulo del núcleo

Cuando se compila un módulo, se firma con un certificado único de la compañía que no puede ser fabricado o emulado. Si el módulo está firmado con cualquier otro certificado, éste será identificado al validar la firma del módulo.

Esto significa que aunque los ciberdelincuentes consigan sustituir el módulo del núcleo y mantener la suma de comprobación de un archivo original, no hay posibilidad de que puedan firmar el módulo con un certificado único de la empresa.

Si no hay firma o el certificado es incorrecto, este problema se identificará durante la etapa de validación antes de cargar el módulo en el núcleo de Linux.

Lee también:

Usando Kali Linux para la prueba de penetración

Protección de los módulos inicializados

Una vez que hayas terminado las pruebas de seguridad del módulo del núcleo durante la carga, es hora de pasar al siguiente paso: proteger el módulo inicializado. Después de que un módulo es inicializado, los hackers todavía tienen la oportunidad de quitarlo o reemplazarlo.

En primer lugar, hay que asegurar el archivo del módulo y controlar las manipulaciones con modprobe que afectan a la carga del módulo.

Además, preste atención a cualquier dispositivo PCI conectado al módulo. Compruebe las posibles manipulaciones del dispositivo, ya que pueden llevar a la pérdida de eficiencia del módulo del núcleo.

Por último, si el módulo se carga automáticamente en el sistema de inicio, debería comprobar y gestionar las posibles manipulaciones con scripts para la carga automática o con enlaces simbólicos en los niveles de ejecución requeridos.

Exploremos en detalle cada una de las posibles vulnerabilidades mencionadas anteriormente.

Control de las operaciones con el archivo del módulo

Es crucial asegurarse de que el módulo del núcleo funcione correctamente y no se descargue, modifique o elimine.

Por lo tanto, tienes que comprobar las operaciones del archivo primario con el archivo del módulo:

  • Renombrar
  • Quitar
  • Transferir a otra carpeta
  • Modificar el módulo
  • Modificar los atributos
  • Reemplazar el archivo del módulo

Además, compruebe estas operaciones para la descarga del módulo:

  • Rmmod
  • Modprobe -r

Todas estas operaciones tienen que ser bloqueadas tanto para los usuarios habituales como para las cuentas de raíz.

Control de operaciones con un dispositivo PCI

Dado que gran parte del núcleo en funcionamiento son controladores, y los controladores se instalan en los dispositivos, también es importante comprobar las operaciones relacionadas con los dispositivos PCI.

Las operaciones que tienes que comprobar son:

  • Desvincular el conductor del dispositivo
  • Dispositivo de desactivación
  • Quitar el dispositivo
  • Dispositivo de apagado

Todas estas operaciones pueden impedir la interoperabilidad entre un módulo (conductor) y un dispositivo PCI, que puede detener el módulo. Para ser más específicos, estas operaciones pueden conducir a un acceso alterado, fuga de datos o pérdida de datos.

Control de operaciones con el sistema init

Como hemos mencionado, hay dos posibles vulnerabilidades en el sistema de inicio que deberían preocuparle:

  • Guión de inicio en /etc/rc.d/init.d
  • Enlaces simbólicos en las carpetas /etc/rc.d/rcN.d.

Después de la inicialización del módulo, hay que comprobar las operaciones esenciales de los archivos para esos scripts y enlaces. De esta manera, puedes asegurarte de que los ajustes del módulo no pueden ser modificados para evitar que el módulo del núcleo se cargue.

Compruebe las siguientes operaciones con los scripts de inicio y los enlaces simbólicos:

  • Renombrar
  • Quitar
  • Modificar el contenido
  • Transferir a otra carpeta
  • Modificar los atributos

Todas estas operaciones deben bloquearse tanto para los usuarios habituales como para las cuentas de raíz.

Lee también:

Enganchando las funciones del kernel de Linux, Parte 1: Buscando la solución perfecta

Protegiendo la memoria del módulo del núcleo

Ya hemos demostrado lo importante que es el funcionamiento del módulo del núcleo y por qué hay que proteger su integridad. Ahora es el momento de pensar en la memoria del módulo del núcleo.

No basta con proteger el módulo validando su hachís y su firma. También hay que controlar la memoria del módulo.

El módulo del núcleo opera la sección de operaciones de lectura-escritura. Al crear un registro en esta sección, los hackers pueden causar problemas como:

  • El módulo o el sistema se cae
  • Código de ejecución grabado en la memoria del módulo del núcleo en el sistema protegido
  • Desglose o modificación de la funcionalidad del módulo

El proceso de comprobación y aseguramiento de la memoria del módulo del núcleo puede dividirse en dos pasos.

Paso 1. Revisa la lista de funciones importadas en el módulo justo después de ser cargado en el kernel de Linux

Preste atención a las funciones que utiliza el módulo. La utilidad de lectura puede ayudarte mostrando una lista de todas las funciones que utiliza un módulo. Tenga en cuenta que esta lista será larga – no menos de 100 funciones con seguridad.

Tienes que confirmar que la lista de funciones y todos los atributos que la acompañan son los que esperabas. Sólo verificando esta lista es posible asegurar que el rendimiento del módulo del kernel no se verá comprometido.

Paso 2. Asegurar la memoria del módulo del núcleo cargada en el núcleo de Linux en tiempo real

Este paso es un poco más complicado que el anterior.

Para controlar la memoria del sistema virtual (incluida la memoria del módulo del núcleo), hay que asegurar la posibilidad de gestionar la memoria a nivel de hardware o de hipervisor. Puedes hacer esto usando el hipervisor de QEMU, por ejemplo.

Puedes asegurar la seguridad de la memoria de la siguiente manera:

  • Establezca permisos de sólo lectura para todas las secciones de la memoria con permisos de lectura-escritura para que la memoria no pueda ser modificada.
  • Limitar el acceso a las secciones de memoria que deben tener permisos de lectura-escritura para que sólo el código del módulo del núcleo y las funciones importadas puedan escribir en ellas.

Lee también:

Cómo depurar el núcleo de Linux con QEMU y Libvirt

Conclusión

Los módulos del kernel son vulnerables durante todas las etapas de su funcionamiento, desde el lanzamiento del sistema hasta la ejecución de los procesos en el kernel de Linux.

No se pueden ignorar los riesgos relacionados con los módulos del núcleo, ya que pueden causar graves daños a cualquier proyecto y provocar pérdidas importantes.

Para garantizar la seguridad de los datos de los usuarios y la fiabilidad de su sistema, es necesario adoptar un enfoque complejo y coherente de la seguridad del módulo del núcleo.

En este artículo, describimos varios puntos débiles en la seguridad de los módulos del núcleo de Linux y sugerimos formas de abordar estos puntos débiles al ejecutar pruebas de seguridad.

En Apriorit, aprovechamos la experiencia de nuestro equipo profesional de control de calidad que nunca deja de explorar nuevas amenazas potenciales y formas de prevenirlas. Contáctenos si está buscando servicios de pruebas de seguridad robustos. Siempre estamos dispuestos a ayudar!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *