- PVSM.RU - https://www.pvsm.ru -
STACKLEAK — это функция безопасности ядра Linux, изначально разработанная создателями Grsecurity/PaX. Я решил довести STACKLEAK до официального ванильного ядра (Linux kernel mainline). В этой статье будет рассказано о внутреннем устройстве, свойствах данной функции безопасности и ее очень долгом непростом пути в mainline.
STACKLEAK защищает от нескольких классов уязвимостей в ядре Linux, а именно:
Данная функция безопасности отлично укладывается в концепцию проекта Kernel Self Protection Project (KSPP): безопасность — это больше, чем только исправление ошибок. Абсолютно все ошибки в коде исправить невозможно, и поэтому ядро Linux должно безопасно отрабатывать в ошибочных ситуациях, в том числе при попытках эксплуатации уязвимостей. Больше подробностей о KSPP доступно на wiki проекта [1].
STACKLEAK присутствует как PAX_MEMORY_STACKLEAK в grsecurity/PaX патче. Однако grsecurity/PaX патч перестал распространяться свободно с апреля 2017 года. Поэтому появление STACKLEAK в ванильном ядре было бы ценным для пользователей Linux с повышенными требованиями к информационной безопасности.
Порядок работы:
На момент написания статьи (25.09.2018) была отправлена 15 версия серии патчей [2]. Она содержит архитектурно независимую часть и код для x86_64 и x86_32. Поддержка STACKLEAK для arm64, разработанная Лорой Эббот (Laura Abbott) из Red Hat, уже успела попасть в ванильное ядро 4.19.
Данная мера сокращает полезную информацию, которую могут выдать некоторые утечки из ядерного стека в пользовательское пространство.
Пример утечки информации из стека ядра представлен на схеме 1.
Схема 1.
Однако утечки такого типа становятся бесполезны, если в конце системного вызова использованная часть стека ядра заполняется фиксированным значением (схема 2).
Схема 2.
Как следствие, STACKLEAK блокирует некоторые атаки на неинициализированные переменные в стеке ядра. Примеры таких уязвимостей: CVE-2017-17712, CVE-2010-2963. Описание методики эксплуатации уязвимости CVE-2010-2963 можем найти в статье Кейса Кука [3] (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.
Перезапись определенных полей в структуре thread_info на дне ядерного стека позволяет повысить привилегии процесса. Однако при включении опции CONFIG_THREAD_INFO_IN_TASK данная структура выносится из ядерного стека, что устраняет описанный способ эксплуатации уязвимости.
Более продвинутый вариант данной атаки состоит в том, чтобы с помощью выхода за границу стека переписать данные в соседнем регионе памяти. Подробнее о данном подходе:
Атака такого типа отражена на схеме 6.
Схема 6.
Защитой в данном случае служит CONFIG_VMAP_STACK. При включении данной опции рядом с ядерным стеком помещается специальная страница памяти (guard page), доступ к которой приводит к исключению (схема 7).
Схема 7.
Наконец, самым интересным вариантом переполнения стека в глубину является атака типа Stack Clash. Идею еще в 2005 году выдвинул [6] Гаэль Дэлалю (Gael Delalleau).
В 2017 году ее переосмыслили исследователи из компании Qualys, назвав [7] данную технику Stack Clash. Дело в том, что существует способ перепрыгнуть guard page и перезаписать данные из соседнего региона памяти (схема 8). Это делается с помощью массива переменной длинны (VLA, variable length array), размер которого контролирует атакующий.
Схема 8.
Больше информации о STACKLEAK и Stack Clash содержится в блоге grsecurity [8].
Как STACKLEAK защищает от Stack Clash в ядерном стеке? Перед каждым вызовом alloca() выполняется проверка на переполнение стека в глубину. Вот соответствующий код из 14 версии серии патчей:
void __used stackleak_check_alloca(unsigned long size)
{
unsigned long sp = (unsigned long)&sp;
struct stack_info stack_info = {0};
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 boundaryn");
#else
BUG();
#endif
}
}
Однако данный функционал был исключен из 15 версии. Это было сделано в первую очередь из-за спорного запрета [9] Линуса Торвальдса использовать BUG_ON() в патчах по безопасности ядра Linux.
Кроме того, 9-я версия серии патчей привела к дискуссии, в результате которой было решено устранить все массивы переменной длинны из mainline-ядра. В эту работу включилось около 15 разработчиков, и она скоро будет закончена [10].
Привожу результаты тестирования производительности на 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_erase(). Данная функция отрабатывает перед возвращением в пользовательское пространство после системного вызова. В использованную часть стека thread’а записывается STACKLEAK_POISON (-0xBEEF). На начальную точку очистки указывает переменная lowest_stack, постоянно обновляемая в stackleak_track_stack().
Стадии работы stackleak_erase() отражены на схемах 9 и 10.
Схема 9.
Схема 10.
Т.о. stackleak_erase() очищает только использованную часть ядерного стека. Именно поэтому STACKLEAK такой быстрый. А если на x86_64 очищать все 16 кБ стека ядра в конце каждого системного вызова, hackbench показывает падение производительности 40%.
Инструментация кода ядра на этапе компиляции выполняется в STACKLEAK GCC плагине.
GCC плагины — это загружаемые модули для компилятора GCC, специфичные для проекта. Они регистрируют новые проходы с помощью GCC Pass Manager, предоставляя обратные вызовы (callbacks) для данных проходов.
Итак, для полноценной работы STACKLEAK в код функций с большим стековым кадром (stack frame) вставляются вызовы stackleak_track_stack(). Также перед каждой alloca() вставляется вызов уже упомянутой stackleak_check_alloca(), а после — вызов stackleak_track_stack().
Как уже было сказано, в 15 версии серии патчей из GCC-плагина была исключена вставка вызовов stackleak_check_alloca().
Путь STACKLEAK в mainline очень долгий и непростой (схема 11).
Схема 11. Ход работ по внедрению STACKLEAK в Linux kernel mainline.
В апреле 2017 года создатели grsecurity закрыли свои патчи для сообщества, начав распространять их только на коммерческой основе. В мае 2017 года я принял решение взяться за задачу внедрения STACKLEAK в ванильное ядро. Так начался путь длиной более года. Компания Positive Technologies, в которой я работаю, дает мне возможность заниматься этой задачей некоторую часть моего рабочего времени. Но в основном я трачу на нее «свободное» время.
С прошлого мая моя серия патчей прошла многократное ревью, претерпела значительные изменения, дважды была раскритикована Линусом Торвальдсом. Мне хотелось оставить всю эту затею уже много раз. Но в определенный момент появилось твердое желание все же дойти до конца. На момент написания статьи (25.09.2018) 15 версия серии патчей находится в ветке linux-next, соответствует всем озвученным требованиям Линуса и готова к merge-window ядра 4.20 / 5.0.
Месяц назад я сделал доклад о данной работе на Linux Security Summit. Привожу ссылки на слайды [11] и видео [12]:
STACKLEAK — очень полезная функция безопасности ядра Linux, блокирующая эксплуатацию сразу несколько типов уязвимостей. Помимо этого изначальный автор PaX Team смог сделать ее быстрой и красивой в инженерном плане. Поэтому появление STACKLEAK в ванильном ядре было бы ценным для пользователей Linux с повышенными требованиями к информационной безопасности. Более того, работа в данном направлении привлекает внимание сообщества разработчиков Linux к средствам самозащиты ядра.
Автор: a13xp0p0v
Источник [13]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/informatsionnaya-bezopasnost/294153
Ссылки в тексте:
[1] доступно на wiki проекта: http://kernsec.org/wiki/index.php/Kernel_Self_Protection_Project
[2] 15 версия серии патчей: https://www.openwall.com/lists/kernel-hardening/2018/08/16/12
[3] в статье Кейса Кука: https://outflux.net/blog/archives/2010/10/19/cve-2010-2963-v4l-compat-exploit/
[4] The Stack is Back: https://jon.oberheide.org/files/infiltrate12-thestackisback.pdf
[5] Exploiting Recursion in the Linux Kernel: https://googleprojectzero.blogspot.ru/2016/06/exploiting-recursion-in-linux-kernel_20.html
[6] выдвинул: https://cansecwest.com/core05/memory_vulns_delalleau.pdf
[7] назвав: https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt
[8] блоге grsecurity: https://grsecurity.net/an_ancient_kernel_hole_is_not_closed.php
[9] запрета: https://lore.kernel.org/lkml/CA+55aFy6jNLsywVYdGp83AMrXBo_P-pkjkphPGrO=82SPKCpLQ@mail.gmail.com/
[10] будет закончена: https://lore.kernel.org/lkml/CAGXu5j+Yv9Bu4mJPOfkpxrom91kSKaUESHR-Tn1CAJCTTZy_8w@mail.gmail.com/
[11] слайды: https://schd.ws/hosted_files/lssna18/b7/stackleak_LSS_NA_2018.pdf
[12] видео: https://www.youtube.com/watch?v=5wIniiWSgUc
[13] Источник: https://habr.com/post/424633/?utm_campaign=424633
Нажмите здесь для печати.