Хабрахабр

Как STACKLEAK улучшает безопасность ядра Linux

STACKLEAK — это функция безопасности ядра Linux, изначально разработанная создателями Grsecurity/PaX. Я решил довести STACKLEAK до официального ванильного ядра (Linux kernel mainline). В этой статье будет рассказано о внутреннем устройстве, свойствах данной функции безопасности и ее очень долгом непростом пути в mainline.

STACKLEAK защищает от нескольких классов уязвимостей в ядре Linux, а именно:

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

Данная функция безопасности отлично укладывается в концепцию проекта Kernel Self Protection Project (KSPP): безопасность — это больше, чем только исправление ошибок. Абсолютно все ошибки в коде исправить невозможно, и поэтому ядро Linux должно безопасно отрабатывать в ошибочных ситуациях, в том числе при попытках эксплуатации уязвимостей. Больше подробностей о KSPP доступно на wiki проекта.

Однако grsecurity/PaX патч перестал распространяться свободно с апреля 2017 года. STACKLEAK присутствует как PAX_MEMORY_STACKLEAK в grsecurity/PaX патче. Поэтому появление STACKLEAK в ванильном ядре было бы ценным для пользователей Linux с повышенными требованиями к информационной безопасности.

Порядок работы:

  • выделить STACKLEAK из grsecurity/PaX патча,
  • тщательно изучить код и сформировать патч,
  • отправить в LKML, получить обратную связь, улучшить, повторять заново до принятия в mainline.

На момент написания статьи (25.09.2018) была отправлена 15 версия серии патчей. Она содержит архитектурно независимую часть и код для x86_64 и x86_32. Поддержка STACKLEAK для arm64, разработанная Лорой Эббот (Laura Abbott) из Red Hat, уже успела попасть в ванильное ядро 4.19.

STACKLEAK: свойства безопасности

Очистка остаточной информации в стеке ядра

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

Пример утечки информации из стека ядра представлен на схеме 1.

Схема 1.

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

Схема 2.

Примеры таких уязвимостей: CVE-2017-17712, CVE-2010-2963. Как следствие, STACKLEAK блокирует некоторые атаки на неинициализированные переменные в стеке ядра. Описание методики эксплуатации уязвимости CVE-2010-2963 можем найти в статье Кейса Кука (Kees Cook).

Суть атаки на неинициализированную переменную в стеке ядра представлена на схеме 3.

Схема 3.

STACKLEAK блокирует атаки такого типа, так как значение, которым заполняется ядерный стек в конце системного вызова, указывает на неиспользованную область в виртуальном адресном пространстве (схема 4).

Схема 4.

При этом важным ограничением является то, что STACKLEAK не защищает от аналогичных атак, выполняемых за один системный вызов.

Обнаружение переполнения стека ядра «в глубину»

В ванильном ядре (Linux kernel mainline) STACKLEAK эффективен против переполнения стека «в глубину» (kernel stack depth overflow) только в сочетании с CONFIG_THREAD_INFO_IN_TASK и CONFIG_VMAP_STACK. Обе эти меры внедрены Энди Лутомирски (Andy Lutomirski).

Простейший вариант эксплуатации данного типа уязвимостей отражен на схеме 5.

Схема 5.

Однако при включении опции CONFIG_THREAD_INFO_IN_TASK данная структура выносится из ядерного стека, что устраняет описанный способ эксплуатации уязвимости. Перезапись определенных полей в структуре thread_info на дне ядерного стека позволяет повысить привилегии процесса.

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

Атака такого типа отражена на схеме 6.

Схема 6.

При включении данной опции рядом с ядерным стеком помещается специальная страница памяти (guard page), доступ к которой приводит к исключению (схема 7). Защитой в данном случае служит CONFIG_VMAP_STACK.

Схема 7.

Идею еще в 2005 году выдвинул Гаэль Дэлалю (Gael Delalleau). Наконец, самым интересным вариантом переполнения стека в глубину является атака типа Stack Clash.

Дело в том, что существует способ перепрыгнуть guard page и перезаписать данные из соседнего региона памяти (схема 8). В 2017 году ее переосмыслили исследователи из компании Qualys, назвав данную технику Stack Clash. Это делается с помощью массива переменной длинны (VLA, variable length array), размер которого контролирует атакующий.

Схема 8.

Больше информации о STACKLEAK и Stack Clash содержится в блоге grsecurity.

Перед каждым вызовом alloca() выполняется проверка на переполнение стека в глубину. Как STACKLEAK защищает от Stack Clash в ядерном стеке? Вот соответствующий код из 14 версии серии патчей:

void __used stackleak_check_alloca(unsigned long size)
; unsigned long visit_mask = 0; unsigned long stack_left; BUG_ON(get_stack_info(&sp, current, &stack_info, &visit_mask)); stack_left = sp - (unsigned long)stack_info.begin; if (size >= stack_left) { /* * Kernel stack depth overflow is detected, let's report that. * If CONFIG_VMAP_STACK is enabled, we can safely use BUG(). * If CONFIG_VMAP_STACK is disabled, BUG() handling can corrupt * the neighbour memory. CONFIG_SCHED_STACK_END_CHECK calls * panic() in a similar situation, so let's do the same if that * option is on. Otherwise just use BUG() and hope for the best. */
#if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK) panic("alloca() over the kernel stack boundary\n");
#else BUG();
#endif }
}

Однако данный функционал был исключен из 15 версии. Это было сделано в первую очередь из-за спорного запрета Линуса Торвальдса использовать BUG_ON() в патчах по безопасности ядра Linux.

В эту работу включилось около 15 разработчиков, и она скоро будет закончена. Кроме того, 9-я версия серии патчей привела к дискуссии, в результате которой было решено устранить все массивы переменной длинны из mainline-ядра.

Влияние STACKLEAK на производительность

Привожу результаты тестирования производительности на x86_64. Оборудование: Intel Core i7-4770, 16 GB RAM.

Тест №1, привлекательный: сборка ядра Linux на одном процессорном ядре

# time make Результат на 4.18: real 12m14.124s user 11m17.565s sys 1m6.943s Результат на 4.18+stackleak: real 12m20.335s (+0.85%) user 11m23.283s sys 1m8.221s

Тест №2, непривлекательный:

# hackbench -s 4096 -l 2000 -g 15 -f 25 -P Средний результат на 4.18: 9.08 сек Средний результат на 4.18+stackleak: 9.47 сек (+4.3%)

Таким образом влияние STACKLEAK на производительность системы зависит от типа нагрузки. В частности, большое количество коротких системных вызовов повышает накладные расходы. Т.о. необходимо оценивать производительность STACKLEAK для планируемой нагрузки перед промышленной эксплуатацией.

Внутреннее устройство STACKLEAK

STACKLEAK состоит из:

  • Кода, очищающего стек ядра в конце системного вызова (изначально был написан на ассемблере),
  • GCC плагина для инструментации кода ядра на этапе компиляции.

Очистка стека ядра выполняется в функции stackleak_erase(). Данная функция отрабатывает перед возвращением в пользовательское пространство после системного вызова. В использованную часть стека thread’а записывается STACKLEAK_POISON (-0xBEEF). На начальную точку очистки указывает переменная lowest_stack, постоянно обновляемая в stackleak_track_stack().

Стадии работы stackleak_erase() отражены на схемах 9 и 10.

Схема 9.

Схема 10.

stackleak_erase() очищает только использованную часть ядерного стека. Т.о. А если на x86_64 очищать все 16 кБ стека ядра в конце каждого системного вызова, hackbench показывает падение производительности 40%. Именно поэтому STACKLEAK такой быстрый.

Инструментация кода ядра на этапе компиляции выполняется в STACKLEAK GCC плагине.

Они регистрируют новые проходы с помощью GCC Pass Manager, предоставляя обратные вызовы (callbacks) для данных проходов. GCC плагины — это загружаемые модули для компилятора GCC, специфичные для проекта.

Также перед каждой alloca() вставляется вызов уже упомянутой stackleak_check_alloca(), а после — вызов stackleak_track_stack(). Итак, для полноценной работы STACKLEAK в код функций с большим стековым кадром (stack frame) вставляются вызовы stackleak_track_stack().

Как уже было сказано, в 15 версии серии патчей из GCC-плагина была исключена вставка вызовов stackleak_check_alloca().

Путь в Linux kernel mainline

Путь STACKLEAK в mainline очень долгий и непростой (схема 11).

Ход работ по внедрению STACKLEAK в Linux kernel mainline. Схема 11.

В мае 2017 года я принял решение взяться за задачу внедрения STACKLEAK в ванильное ядро. В апреле 2017 года создатели grsecurity закрыли свои патчи для сообщества, начав распространять их только на коммерческой основе. Компания Positive Technologies, в которой я работаю, дает мне возможность заниматься этой задачей некоторую часть моего рабочего времени. Так начался путь длиной более года. Но в основном я трачу на нее «свободное» время.

Мне хотелось оставить всю эту затею уже много раз. С прошлого мая моя серия патчей прошла многократное ревью, претерпела значительные изменения, дважды была раскритикована Линусом Торвальдсом. На момент написания статьи (25. Но в определенный момент появилось твердое желание все же дойти до конца. 2018) 15 версия серии патчей находится в ветке linux-next, соответствует всем озвученным требованиям Линуса и готова к merge-window ядра 4. 09. 0. 20 / 5.

Привожу ссылки на слайды и видео:
Месяц назад я сделал доклад о данной работе на Linux Security Summit.

Заключение

STACKLEAK — очень полезная функция безопасности ядра Linux, блокирующая эксплуатацию сразу несколько типов уязвимостей. Помимо этого изначальный автор PaX Team смог сделать ее быстрой и красивой в инженерном плане. Поэтому появление STACKLEAK в ванильном ядре было бы ценным для пользователей Linux с повышенными требованиями к информационной безопасности. Более того, работа в данном направлении привлекает внимание сообщества разработчиков Linux к средствам самозащиты ядра.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть