Cabecera blog ciberseguridad

Vulnerabilidad Dirty Pipe CVE-2022-0847

La vulnerabilidad Dirty Pipe afecta al kernel de Linux

Desde el 7 de marzo, se ha hecho público el bug con código CVE-2022-0847, también apodado Dirty Pipe. Esta vulnerabilidad afecta inicialmente al kernel de Linux a partir de la versión 5.8 y permite escalar privilegios escribiendo en archivos bloqueados para solo lectura. Muchos sistemas, entre ellos las últimas versiones de Android y algunas distribuciones como Ubuntu, Debian o Fedora están afectadas.

Antes de explicar los detalles de la vulnerabilidad, conviene repasar algunos conceptos teóricos de sistemas operativos y concretamente Linux:

  • Páginas de memoria. Unidad de datos más pequeña para la administración de memoria en un sistema operativo basado en memoria virtual. Su tamaño mínimo son 4Kb.
  • Páginas cacheadas. Son páginas de memoria a las que se ha accedido recientemente y que se almacenan en una memoria intermedia más rápida con el fin de agilizar posteriores posibles accesos.
  • Pipes (tuberías). Para las comunicaciones entre procesos suele hacerse uso páginas de memoria compartidas, en las que un proceso lee y otro escribe. Normalmente, una pipe abarca múltiples páginas de memoria. Son las mismas que se usan para concatenación de comandos en la terminal, usando el carácter “|”.
  • Pipes anónimas. Cuando la información compartida entre procesos no llega a ocupar una página de memoria entera, puede reutilizarse para otra pipe, desembocando en que en una misma página de memoria convivan datos de pipes distintas.
  • Flags/atributos de las pipes. Las flags de las pipes especifican características como el estado y los permisos. Es en uno de estos atributos: PIPE_BUF_FLAG_CAN_MERGE, de donde surge el problema.

El flag PIPE_BUF_FLAG_CAN_MERGE ha sido introducido en el kernel 5.8, en el commit (f6dd975583bd8ce088400648fd9819e4691c8958) publicado en mayo de 2020. Su función es indicar los datos de un pipe de una página puedan agruparse sin necesidad de rescribir los datos en memoria.

El problema real vino dado por el commit (241699cd72a8489c9446ae3910ddd243e9b9061b) publicado en octubre de 2016. En ese momento se definieron las funciones para reservar la memoria de las pipes, pero sin inicializar la variable donde se almacenan las flags. Por aquel entonces, las flags disponibles no eran muy interesantes, por lo que no supuso un riesgo.

Pero desde que se definió esta nueva flag, la falta de inicialización, sí resultó en un problema. Como el kernel siempre tiene bajo su control las páginas cacheadas, no realiza comprobaciones sobre sus permisos cuando usa una página.

Exploit de la vulnerabilidad Dirty Pipe CVE-2022-0847

El descubridor del fallo, Max Kellermann, desarrolló también un exploit para este problema que está disponible en su página dedicada a la vulnerabilidad Dirty Pipe. Básicamente, funciona de la siguiente manera:

  1. Empieza abriendo un archivo en modo lectura, en el que posteriormente se podrá escribir aunque el programa no tenga permisos.
  2. Crea una pipe con la llamada al sistema pipe(). Esta función le da acceso al mismo proceso a los descriptores que permiten escribir y leer.
  3. Escribe cualquier tipo de información en la pipe para llenarla completamente y que las páginas de memoria se marquen con la flag PIPE_BUF_FLAG_CAN_MERGE.
  4. Una vez se han marcado todas las páginas, permite que el kernel las libere leyendo todos los datos de la pipe que había escrito.
  5. A partir de este punto, cuando el kernel reserve páginas de memoria haciendo uso de las funciones introducidas en 2016, no inicializará sus flags y estarán marcadas con el atributo PIPE_BUF_FLAG_CAN_MERGE.
  6. Usa la función splice() para cargar el archivo que se había abierto al principio. La página de memoria asignada a este archivo será la misma que la de nuestra pipe vacía, gracias a que estaba marcada con la flag.
  7. Sobrescribe directamente los datos en la pipe.

Se ha demostrado que este exploit sirve para elevar privilegios en un sistema, sobrescribiendo el contenido de /etc/passwd, a continuación, se detalla un ejemplo de explotación:

  1. Comprobamos la primera línea del archivo /etc/passwd, confirmando que el usuario root está protegido por contraseña.
  2. Confirmamos que, al ejecutar el comando su, se pide autenticación.
  3. Compilamos el exploit programado por Max Kellermann y lo guardamos con el nombre “exploit”.
  4. Ejecutamos el exploit sobrescribiendo a partir del byte 4 y quitando la x que protege el usuario root con la contraseña.
  5. Al ejecutar el comando su, como el archivo /etc/passwd está modificado, no pide la contraseña.
  6. Modificamos el archivo para devolverlo a su estado original.

La vulnerabilidad Dirty Pipe afecta al kernel de Linux y permite obtener root

La única mitigación posible es hacer uso de un kernel que inicialice los valores de las flags o no disponga de la flag afectada, por lo que es imprescindible modificar el kernel los sistemas afectados para corregir la vulnerabilidad Dirty Pipe.

La mayoría de las distribuciones de Linux que hacen uso de kernels afectados a esta elevación de privilegios han publicado ya actualizaciones para solventar el problema.

En el caso de Android, a 17 de marzo, todavía no hay referencias a este bug en sus boletines de seguridad, por lo que los sistemas afectados no tienen mitigación posible por el momento. Solamente algunas versiones de Android 12 usan el kernel 5.10, por lo que no todos los dispositivos con esta versión de Android están afectados.

Otras distribuciones, como RedHat o SuSE, han publicado parches incluso para kernels que no estarían afectados por este exploit, como es el de RedHat 8, que usa el kernel 4.18. Lo que corrigen en este caso es el bug de 2016, para asegurar que las páginas de memoria se inicializan correctamente, fijando los valores de las flags. El bug corregido por este parche afectaría a las versiones de Linux posteriores a 4.9:

En el caso de Android, a 17 de marzo, todavía no hay referencias a este bug CVE-2022-0847 en sus boletines de seguridad que afecta a muchos kernels

De esta forma, pese a que no exista la flag PIPE_BUF_FLAG_CAN_MERGE en estas versiones, el resto de flags también se inicializarán de manera correcta. Las flags existentes para las páginas de memoria se definen en el archivo include/linux/pipe_fs_i.h.

flags pipe_fs_i.h CVE-2022-0847 de dirty pipe

El parche liberado por RedHat y SuSE solventa la vulnerabilidad corrigiendo el fallo del commit del 2016. Este parche soluciona la raíz del problema inicializando las flags y haciendo imposible el funcionamiento de futuros exploits que usen las flags que ya había definidas en versiones anteriores.

Referencias sobre la vulnerabilidad Dirty Pipe CVE-2022-0847

Advisories de distintas distribuciones Linux

Android

Descubre nuestro trabajo y nuestros servicios de ciberseguridad.