
Exploit de escalada de privilegios local para CVE-2023-0386 dirigido al kernel de Linux overlayfs. Incluye análisis detallado de la vulnerabilidad, código PoC y guía de explotación paso a paso usando FUSE y espacios de nombres de usuario.
gcc -Wall exp.c `pkg-config fuse --cflags --libs` -o exp
./exp /tmp

El conocimiento teórico de este artículo (namespaces, sistema de archivos overlay, sistema de archivos fuse, etc.) proviene de chatGPT.
ID de vulnerabilidad: CVE-2023-0386
Producto afectado: kernel de Linux - sistema de archivos overlay
Versiones afectadas: 5.11 ~ 5.19
Requisitos: poder ejecutar unshare o poder crear un sistema de archivos overlay
Impacto: escalada de privilegios local
Compilar el kernel uno mismo:
Prepara el kernel dentro del rango de versiones vulnerables, excluyendo la 5.15 (la 5.15 parece tener problemas), y habilita los dos sistemas de archivos overlay y fuse:
CONFIG_SLUB_DEBUGOVERLAY_FS
CONFIG_FUSE_FS
Se ha probado que Ubuntu 21.10 con kernel 5.13.0-16-generic funciona:

Antes del análisis de la vulnerabilidad, hagamos que chatGPT interprete a un experto en el kernel de Linux:
(Pregunta a chatGPT: a continuación, interpreta a un experto en el kernel de Linux y ayúdame a resolver algunas preguntas)
La información pública sobre la vulnerabilidad es escasa; lo más directo es la información del parche. El enlace al parche es el siguiente:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4f11ada10d0a

Se puede ver que se añadió una comprobación en la función ovl_copy_up_one. Primero preguntemos a chatGPT qué hace esta función:

Esta función se ejecuta durante la acción de copiar un archivo de la capa inferior a la capa superior en el sistema de archivos overlay. A continuación, veamos la comprobación añadida por el parche en su contexto:
static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
int flags)
{
int err;
DEFINE_DELAYED_CALL(done);
struct path parentpath;
struct ovl_copy_up_ctx ctx = {
.parent = parent,
.dentry = dentry,
.workdir = ovl_workdir(dentry),
};
if (WARN_ON(!ctx.workdir))
return -EROFS;
ovl_path_lower(dentry, &ctx.lowerpath);
err = vfs_getattr(&ctx.lowerpath, &ctx.stat,//[1] 获取底层文件系统的stat
STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
if (err)
return err;
//[2]补丁新加判断文件的stat属性中的用户id和用户组id是否在当前命名空间有映射
if (!kuid_has_mapping(current_user_ns(), ctx.stat.uid) ||
!kgid_has_mapping(current_user_ns(), ctx.stat.gid))
return -EOVERFLOW;
[1] Primero, se obtienen los atributos del archivo de destino en el sistema de archivos inferior mediante la función vfs_getattr. La función vfs_getattr recibe una estructura struct path de un archivo y devuelve la estructura struct stat correspondiente a ese archivo.
[1.1] ctx.lowerpath es la ruta de un archivo en el sistema de archivos inferior dentro del sistema de archivos overlay; el sistema de archivos overlay se presentará más adelante.
[1.2] La estructura struct stat almacena la información de metadatos del archivo, incluidos el propietario y el grupo. La información del propietario obtenida se evaluará en la comprobación que añade el parche.
[2] A continuación, se llama a la función kuid_has_mapping para evaluar la información de propietario y grupo del archivo recién obtenida, determinando si el propietario y el grupo del archivo de destino tienen un mapeo en el espacio de nombres de usuario actual.
[2.1] La función kuid_has_mapping recibe dos parámetros: una estructura struct user_namespace (espacio de nombres de usuario) y una estructura struct kuid (usuario del kernel). Esta función determina si la información de usuario dada tiene un mapeo en el espacio de nombres de usuario dado. El mapeo de usuarios en los espacios de nombres se explicará en detalle más adelante.
Por lo tanto, sabemos que al realizar la operación de esta función vulnerable (ovl_copy_up_one), si el usuario propietario o el grupo propietario del archivo de la capa inferior no tiene mapeo en el espacio de nombres actual, la operación falla.
El principio del parche queda claro, pero aun así necesitamos resolver las siguientes cuestiones para poder reproducir esta vulnerabilidad:
ovl_copy_up_one, es decir, la copia de un archivo de la capa inferior a la capa superior en el sistema de archivos overlay?lowerpath, cuyo propietario se comprueba si está mapeado, en la cadena lógica anterior?Antes de resolver estas dos dudas, necesitamos aclarar algunos conceptos básicos:
(Pregunta a chatGPT: por favor, presenta los espacios de nombres del kernel de Linux)
En Linux, los espacios de nombres (namespaces) son una característica del kernel que se utiliza para lograr el aislamiento de recursos. Mediante los espacios de nombres, un grupo de procesos puede parecer que se ejecuta en un entorno de sistema independiente, mejorando así la seguridad y la manejabilidad del sistema. Los espacios de nombres desempeñan un papel clave en tecnologías de contenedores (como Docker), ya que permiten que los contenedores se ejecuten en un entorno aislado sin afectar a otros contenedores ni al sistema principal.
El kernel de Linux admite 7 tipos de espacios de nombres (mount, pid, net, ipc, user, time, cgroup), y cada uno aísla una categoría específica de recursos del sistema. Los espacios de nombres se crean, modifican y gestionan mediante una serie de llamadas al sistema (como clone, unshare y setns). Los runtimes de contenedores (como Docker) y otras herramientas de virtualización utilizan estas características de los espacios de nombres para proporcionar a los contenedores un entorno de ejecución independiente y aislado.
La función de comprobación kuid_has_mapping añadida por el parche de la vulnerabilidad está relacionada con el espacio de nombres de usuario (user namespace), uno de los 7 espacios de nombres mencionados.
(Pregunta a chatGPT: por favor, presenta el espacio de nombres de usuario entre estos)
El espacio de nombres de usuario (User Namespace) se utiliza para aislar los ID de usuario (UID) y los ID de grupo (GID). Gracias a él, se pueden usar conjuntos independientes de ID de usuario y grupo en diferentes espacios de nombres. Esto significa que los usuarios y grupos de un espacio de nombres pueden tener ID o permisos distintos en otro espacio de nombres. El espacio de nombres de usuario mejora la seguridad y la manejabilidad del sistema, especialmente en entornos de contenedores.
La característica clave del espacio de nombres de usuario es el mapeo de ID: permite mapear UID y GID de un espacio de nombres a UID y GID de otro. Esto significa que, en diferentes espacios de nombres de usuario, los mismos UID y GID pueden representar a usuarios y grupos distintos. Por ejemplo, el usuario root (UID 0) de un contenedor puede estar mapeado a un usuario no privilegiado en el sistema principal.
Solo necesitamos recordar los siguientes puntos: