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

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

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

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

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

Категории

Все категории
Loading categories
Инструменты/GitHubGitHub/c3r34lk1ll3r/cve-2017-5123
Повышение привилегийЭксплуатацияОбучение и ОбразованиеЭксплуатация Бинарных ФайловЛаборатории и Практика
GitHubc3r34lk1ll3r/cve-2017-5123

CVE-2017-5123

PoC CVE-2017-5123 - LPE - Обход SMEP/SMAP. Без KASLR

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

Популярное

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

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

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

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

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

CVE-2017-5123

PoC CVE-2017-5123 - LPE - Обход SMEP/SMAP. Без KASLR

Реализация waitid в ванильных ядрах не ограничивала целевое назначение для копирования информации о результатах. Это может позволить локальным пользователям записывать в защищённую память ядра, что может привести к повышению привилегий.

Введение

В этом небольшом руководстве я проанализирую уязвимость ядра, которая позволяет получить привилегии root.

Данный файл разделён на четыре части:

  1. Настройка VM;
  2. Анализ уязвимости;
  3. Эксплуатация;
  4. PoC.

Хочу отметить, что существуют гораздо лучшие способы эксплуатации этой CVE (действительно, это всего лишь PoC для изучения ядра, его нельзя использовать в реальных условиях), но я считаю, что данная методология может быть полезна как введение в эксплуатацию ядра.

Настройка VM

Сборка ядра

Эта уязвимость была внедрена в коммите 4c48abe91be0, поэтому нам нужно собрать эту версию ядра.

Это может быть немного сложно, так как это старая версия, и код должен быть пропатчен. Я сделал репозиторий с уже пропатченным кодом ядра и файлом .config, так что вы можете клонировать и собрать.

git clone https://github.com/c3r34lk1ll3r/kernel_mirror.git
cd kernel_mirror
git checkout origin/modified_v4.14
wget https://gist.githubusercontent.com/c3r34lk1ll3r/c9c34ae86140cc7a24d0d90141686ee8/raw/52431b577a71e3fe8f89d6ce355ce9c1c54c53b6/.config
make -j 8 --output-sync=recurse

Обратите внимание, что это ядро будет собрано с драйверами virtio, так что вы сможете использовать virtio disk для обмена файлами между хостом и VM.

Настройка Rootfs

Теперь создадим начальную rootfs:

qemu-img create -f raw hda.raw 10G
# Форматируем диск в ext4
mkfs.ext4 ./hda.raw 
# Создаём точку монтирования для образа
mkdir /tmp/mount1
# Монтируем диск
sudo mount -o loop ./hda.raw /tmp/mount1

Затем следует установить базовый дистрибутив Linux, например, с помощью pacstrap или debootstrap.

sudo pacstrap /tmp/mount1 base base-devel vim

Наконец, можем модифицировать систему:

# Добавляем пользователя 'test'
echo 'test:x:1000:1000::/home/test:/bin/bash' | sudo tee -a /tmp/mount1/etc/passwd
# без пароля
echo 'test::14871::::::' | sudo tee -a /tmp/mount1/etc/shadow 
# мы можем смонтировать virtio-диск для обмена файлами между хостом и гостем
echo '/transient /home/test/shared 9p trans=virtio,version=9p2000.L,rw,user,exec 0 0' | sudo tee -a /tmp/mount1/etc/fstab
sudo mkdir -p /tmp/mount1/home/test/shared 
# Полезно иметь права sudo
echo '%wheel ALL=(ALL) NOPASSWD: ALL' | sudo tee -a /tmp/mount1/etc/sudoers
echo 'wheel:x:998:test' | sudo tee -a /tmp/mount1/etc/group

sudo chown -R 1000:1000 /tmp/mount1/home/test
sudo umount /tmp/mount1

Если всё в порядке, теперь можно протестировать нашу систему с qemu:

qemu-system-x86_64 \
    -kernel ./kernel_mirror/arch/x86_64/boot/bzImage \
    -hda ./hda.raw \
    -m 4G \
    -cpu "Skylake-Client-IBRS,ss=on,vmx=on,hypervisor=on,tsc-adjust=on,clflushopt=on,umip=on,md-clear=on,stibp=on,arch-capabilities=on,ssbd=on,xsaves=on,pdpe1gb=on,ibpb=on,amd-ssbd=on,skip-l1dfl-vmentry=on,hle=off,rtm=off" \
    -smp 4 \
    -vga virtio \
    -enable-kvm \
    -nographic \
    -machine type=q35,accel=kvm \
    -virtfs "fsdriver=local,id=fs.1,path=./trans_fs,security_model=mapped,writeout=immediate,mount_tag=/transient" \
    -append "root=/dev/sda rw noquiet nokaslr console=ttyS0 loglevel=5" \
    -chardev "vc,id=vc.0,cols=1920,rows=1080" \
    -net "user,hostfwd=tcp::10022-:22" \
    -net "nic" \
    -s

Уязвимость

В описании CVE говорится о неограниченной операции записи во время системного вызова waitid.

Откроем 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();
    unsafe_put_user(signo, &infop->si_signo, Efault);
    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();
    return err;
Efault:
    user_access_end();
    return -EFAULT;
}

Эта функция довольно проста: после нескольких проверок следуют вызовы unsafe_put_user(...) и функция возвращается.

Основная часть функции состоит из вызовов unsafe_put_user(...), поэтому перейдём к ним (arch/x86/include/asm/uaccess.h):

/*
 * The "unsafe" user accesses aren't really "unsafe", but the naming
 * is a big fat warning: you have to not only do the access_ok()
 * checking before using them, but you have to surround them with the
 * user_access_begin/end() pair.
 */
#define user_access_begin()	__uaccess_begin()
#define user_access_end()	__uaccess_end()

#define unsafe_put_user(x, ptr, err_label)					\
do {										\
    int __pu_err;								\
    __typeof__(*(ptr)) __pu_val = (x);					\
    __put_user_size(__pu_val, (ptr), sizeof(*(ptr)), __pu_err, -EFAULT);	\
    if (unlikely(__pu_err)) goto err_label;					\
} while (0)

#define unsafe_get_user(x, ptr, err_label)					\
do {										\
    int __gu_err;								\  
    __inttype(*(ptr)) __gu_val;						\
    __get_user_size(__gu_val, (ptr), sizeof(*(ptr)), __gu_err, -EFAULT);	\
    (x) = (__force __typeof__(*(ptr)))__gu_val;				\
    if (unlikely(__gu_err)) goto err_label;					\
} while (0)

В комментарии есть жирное предупреждение: если вы хотите использовать unsafe_put/get_user, вы должны сначала вызвать access_ok() и окружить их парой user_access_begin/end().

Если посмотреть на предыдущий код (waitid), можно заметить, что access_ok() никогда не вызывается, поэтому системный вызов нарушает это предупреждение.

Но что это за макросы?

SMAP/SMEP

SMAP и SMEP — это две функции безопасности, внедрённые в ядро для усложнения написания эксплойтов. Следует отметить, что эти функции реализуются процессором.

SMEP предотвращает исполнение пользовательского кода, когда процессор находится в режиме супервизора; SMAP, напротив, блокирует чтение/запись в пользовательскую память.

Ядру необходимо читать/записывать данные в/из пользовательской памяти, и это можно сделать двумя способами:

  1. существуют функции (например, copy_from_user), которые позволяют копировать память в пространство ядра;
  2. временно отключить SMAP.

Как видно из определения unsafe_put_user, эта функция просто копирует значение x в память, на которую указывает ptr (и переходит к err_label в случае ошибки). Мы только что сказали, что ядро не может обращаться к пользовательскому пространству из-за SMAP, и поэтому эти функции следует оборачивать между user_access_begin/end().

#define __uaccess_begin() stac()
#define __uaccess_end()   clac()

Как видим, user_access_begin/end — это просто ASM-инструкции stac и clac.

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