
Эксплойт ядра для CVE-2016-6187 (локальное повышение привилегий)
Эксплойт ядра для CVE-2016-6187 (локальное повышение привилегий) (затронутые версии: < 4.6.5)
Эксплойт разработан под кастомно собранное ядро 4.6 + buildroot (kconfig скоро будет) и обходит KASLR, SMEP, SMAP (KPTI недоступно для 4.6)
Эксплуатируемая уязвимость представляет собой единичное переполнение нулевым байтом в куче в одном из LSM-хуков AppArmor (setprocattr). См. https://nvd.nist.gov/vuln/detail/CVE-2016-6187
Первоначальные замеры показывают 94.44% вероятность получения root-оболочки. В 1 из 18 случаев эксплойт вызывает kernel oops. Требуется дополнительная работа для полной стабилизации ядра после получения root-доступа.
Мы используем переполнение нулевым байтом для повреждения LSB указателя freelist в kmalloc-128, что позволяет ему указывать на уже выделенный объект. После этого мы освобождаем этот выделенный объект, вызывая ситуацию двойного освобождения.
Теперь мы можем выделить две перекрывающиеся структуры из кэша kmalloc-128. Первая структура — это объект, содержимое которого может быть прочитано из пользовательского пространства. Для этого я выбрал ip6_sf_socklist. Вторая структура, которая будет выделена поверх ip6_sf_socklist, должна содержать указатели ядра — rfkill_data хорошо подходит. Она содержит указатель на кучу (пустой список rfkill_data.events) и глобальный указатель данных на rfkill_fds (rfkill_data.list.prev). Как только rfkill_data выделяется поверх ip6_sf_socklist, мы можем получить указатели ядра, вызвав getsockopt(sock, IPPROTO_IPV6, MCAST_MSFILTER, gsf, &optlen). Затем можно вычислить базовый адрес текста ядра из rfkill_fds.
Затем мы вызываем второй сценарий двойного освобождения, но на этот раз мы используем процесс связывания очереди сообщений, чтобы перезаписать указатель freelist и обмануть аллокатор, заставив его вернуть нам блок из kmalloc-96, когда мы на самом деле запрашиваем блок из kmalloc-128.
Затем мы можем использовать несоответствие размеров, чтобы переполнить указатель freelist блока kmalloc-96, что даёт нам примитив для выделения произвольных адресов. В нашем случае мы нацеливаемся на структуру ptmx_fops и перезаписываем обратный вызов ptmx_fops.unlocked_ioctl указателем на наш гаджет поворота стека (stack pivot gadget).
Вызов ioctl на /dev/ptmx запускает гаджет поворота, и мы перемещаем стек на поддельный стек в kmalloc-128, где мы уже подготовили нашу первую стадию ROP-цепочки.
Поскольку у нас ограниченное пространство на поддельном стеке kmalloc-128 (мы не знаем, будет ли следующий блок находиться в том же slab), задача первой стадии ROP-цепочки — скопировать вторую стадию ROP-цепочки из пользовательского пространства в буфер журнала dmesg ядра (__log_buf) и переместить стек в __log_buf.
Затем вторая стадия ROP-цепочки пытается восстановить ядро (восстановить ptmx_fops, ...) перед вызовом commit_creds(prepare_kernel_cred(NULL)) для повышения привилегий.
И наконец, она выходит из пространства ядра через iretq, и мы возвращаемся, с повышенными привилегиями, к нашей подпрограмме execve("/bin/sh", 0, 0).
Непривилегированные пользователи должны иметь возможность как минимум открывать /dev/rfkill. Некоторые дистрибутивы могут это не разрешать. Я проверил Ubuntu и Fedora — оба позволяют непривилегированным пользователям открывать /dev/rfkill (хотя Fedora использует SELinux вместо AppArmor)
В следующем разделе объясняется, как воспроизвести локальное повышение привилегий в qemu-x86_64.
Сначала вам нужно получить образ initrd и ядро bzImage. Есть два способа:
Самый простой и быстрый способ. Все необходимые компоненты можно найти в prebuilt/.
Или, альтернативно, вы можете самостоятельно собрать компоненты. Просто скачайте ядро 4.6 и используйте предоставленный файл x86_64_vuln_kern_defconfig в config/ для компиляции bzImage.
Для initrd вы можете использовать, например, buildroot. Просто убедитесь, что установлены правильные права доступа для /dev/rfkill в вашем init
chmod 664 /dev/rfkill
Это то же самое, что и в Ubuntu-16.04 и многих других дистрибутивах. Также не забудьте скомпилировать эксплойт и поместить его в ваш initrd
make
После получения компонентов загрузите уязвимую машину
./boot.sh <path/to/bzImage> <path/to/rootfs.cpio>
и выполните эксплойт
./exploit
Возможно, вам потребуется запустить эксплойт несколько раз, в зависимости от того, обнулили ли мы LSB, который уже был нулевым. Также обратите внимание, что всё ещё есть небольшая вероятность (возможно, 10-20%) падения после получения root-доступа — я работаю над этим.
Удачи.
ptmx_fops для повышения стабильностиkmalloc-128 для повышения стабильности