Операционная система Linux показала миру всю мощь проектов с открытым исходным кодом — благодаря ей сегодня у нас есть возможность изучить исходный код работающей операционной системы и на его основе составить собственную систему для решения тех или иных задач. Благодаря своей открытости Linux должна стать самой безопасной операционной системой в мире, поскольку открытый исходный код позволяет разрабатывать и улучшать защитные подсистемы от атак на операционную систему и улучшать саму операционную систему. Фактически, в настоящее время сообществом создано большое количество средств защиты: сегодня уже не так просто использовать уязвимости переполнения буфера, чтобы получить повышенные привилегии, как это было 20 лет назад. Тем не менее, сегодня вы можете найти эксплойты, являющиеся общественным достоянием, которые могут повысить права пользователей в последних версиях ядра. В данной татье рассмотрим, как это работает и почему это происходит. Мы рассмотрим основные компоненты эксплойтов и посмотрим, как работают некоторые из них.
Типы эксплойтов
Давайте выберем общий термин, который будет обозначать наличие эксплойта — алгоритма, нарушающего нормальное функционирование операционной системы, а именно механизмов контроля доступа. Мы также представим концепцию уязвимости — это недостаток программного обеспечения, который может использоваться алгоритмом эксплуатации. Без уязвимости эксплойт существовать не может.
Мы представим классификацию эксплойтов. Базовое разделение эксплойтов на подгруппы для любой операционной системы начинается на уровне архитектуры. Сегодня операционные системы включают как минимум 2 уровня привилегий, которые они используют для своей работы. Ниже приведен рисунок, иллюстрирующий разделение привилегий.
Изображение очень четко показывает, что операционная система имеет уровень пространства ядра (Kernel Space), обычно это наиболее привилегированный режим, именно здесь мы находим то, что мы называем операционной системой. И второй уровень — это пользовательское пространство (User Space): здесь запускаются обычные приложения и сервисы, которые мы используем каждый день.
Исторически уязвимости можно найти для каждого из вышеперечисленных уровней, для которых можно создать эксплойт, но у эксплойтов для каждого из уровней есть свои ограничения.
На уровне пользователя любой эксплойт, влияющий на приложение, имеет те же разрешения, что и пользователь, запустивший уязвимое приложение. В результате этот тип эксплойта позволяет полностью контролировать операционную систему только тогда, когда приложение запускается системным администратором. В отличие от пользовательского уровня, если уровень ядра содержит уязвимый код, он может сразу предоставить возможность управления операционной системой с максимальными разрешениями. Далее мы сосредоточимся на исследовании этих эксплойтов.
Эксплойты
Представим небольшую статистику по раскрытию уязвимостей для ядра операционной системы Linux дистрибутивов Debian, SUSE, Ubuntu, Arch Linux последних 4-х лет.
Данные взяты отсюда. Картина не претендует на полноту, но показывает, что уязвимостей достаточно много, и даже сегодня есть из чего выбирать для построения эксплойта. Давайте попробуем описать, что из себя представляет эксплойт.
Любой эксплойт для любого уровня операционной системы сегодня состоит из частей, которые должны быть имплементированы в его коде:
-
Подготовительные операции:
Выставление необходимого отображения памяти
Создание необходимых объектов в ОС
Обход механизмов защиты ОС для используемой уязвимости
-
Вызов уязвимой части ПО.
-
Выполняет полезную нагрузку:
Для открытия доступа к ОС
Изменение конфигурации ОС
Вывод ОС из строя
Следуя всем вышеперечисленным пунктам, вы можете написать работоспособный эксплойт. Давайте рассмотрим некоторые эксплойты прошлых лет для исследования и попытаемся выяснить, можем ли мы найти какие-либо шаблоны или заимствования, которые используются для нарушения контроля доступа в операционной системе Linux. В качестве объектов исследования рассмотрим эксплойты, использующие следующие уязвимости с учетными данными CVE:
Разбор эксплойтов
CVE-2020-8835 применяется к ядру Linux, начиная с версии 5.5.0. Уязвимость заключается в реализации технологии ebpf
. Технология была разработана таким образом, чтобы пользователь мог создавать собственные обработчики для фильтрации сетевого трафика. Основной компонент фильтрации — виртуальная машина с собственным набором элементов управления. Код, выполняемый виртуальной машиной, находится в ядре: ошибка в этом коде дает злоумышленнику возможность работать с памятью с максимальными привилегиями. В случае описанной уязвимости проблема заключалась в том, что операции обработки 32-битных инструкций обрабатывались некорректно, и виртуальная машина могла записывать и читать данные в ОЗУ ядра.
Как автор эксплойта использует данную уязвимость и какая выполняется полезная нагрузка, рассмотрим дальше.
Подготовительный этап
За этот этап отвечает следующая часть кода.
Строка 394 предназначена для создания объекта в памяти, который будет хранить данные команды для ebpf
. Строка 400 загружает в память код, который будет выполняться на виртуальной машине и нарушать условия обработки 32-битных команд. Подготовка памяти окончена, следующие строки создадут объект сокета, который будет передавать загруженные команды в bpf
. После этого начнется фаза вызова уязвимости.
Вызов уязвимого кода
Вызов уязвимого кода, а точнее работа с командами виртуальной машины, осуществляется из строк с 423 по 441. Основная задача этого кода — получить базовый адрес структуры, которая находится в памяти, в данном случае это куча (heap) процесса. Как только эти команды будут выполнены, эксплойт сможет обнаруживать данные в памяти, используемые операционной системой для управления контролем доступа. В операционной системе Linux эти данные хранятся в структуре taskstruct
.
Полезная нагрузка
Полезная нагрузка данного эксплойта заключается в том, что после его выполнения можно запустить процесс с правами пользователя root
. Для этого код эксплойта производит модификацию полей структуры ядра операционной системы Linux — cred
это структура, которая входит в структуру taskstruct
. Исходный код структуры cred
можно найти здесь.
Действия по модификации полей struct cred
можно увидеть на строках 472,473,474
. То есть этим действием обнуляется значение uid, gid, sgid
создаваемого процесса. С точки зрения ОС это установка значений идентификаторов, которые обычно использует root
. Метод очень похож на тот, который применяется для атак на операционную систему Windows.
Обезопасить себя без обновления ОС можно, если внести следующие изменения в конфиг: sudo sysctl kernel.unprivileged_bpf_disabled=1
CVE-2020-27194 — снова уязвимость ebpf
. Доступно для версий ядра 5.8. *. Создатели этой технологии шутят, что bpf
— это JavaScript для ядра. Действительно, это суждение недалеко от истины. На самом деле виртуальная машина управляет командами с помощью технологии JIT, которая сама по себе несет в себе все типичные для браузера уязвимости в ядре операционной системы, то есть сложно настроить подсистемы безопасности для защиты выполнения кода. Рассматриваемая уязвимость заключается в том, что любая область ОЗУ может быть изменена кодом виртуальной машины. Возможно, это связано с тем, что виртуальная машина не работает надежно с 64-битными операциями. Уязвимость очень похожа на рассмотренную выше.
Эксплойт, который создан для того, чтобы использовать описанную уязвимость, выполняет те же операции, что и эксплойт CVE-2020-8835. Алгоритм эксплойта следующий:
-
Загрузить код с обработкой 64 битных операций в память
-
Создать сокет и отправить данные для вызова команд
ebpf
-
Найти в памяти адрес структуры
taskstruct
за счет выполнения команд в виртуальной машине
-
-
Модифицировать значения
uid,gid,sgid
и запустить интерактивную оболочку.
Автор писал исходный код с новыми фишками и дополнительными функциями. Предлагаем читателю самостоятельно взглянуть на код. Перечисленные этапы работы эксплойта выше не дадут запутаться.
Защита от этой уязвимости без использования обновления такая же: sudo sysctl kernel.unprivileged_bpf_disabled=1
Итоги
Основываясь на двух эксплойтах, обсуждаемых в статье, можно предположить, что повышение привилегий в современной операционной системе Linux больше не является темной магией программирования, а представляет собой полностью отлаженный стандартный процесс, который включает повторное использование функций и объектов в ОЗУ. В этом случае вам даже не нужно писать базовый независимый (shellcode) код, чтобы выполнять большую часть работы. Изменить идентификаторы, используемые для назначения разрешений пользователям, довольно просто.