Защита памяти: StackGuard.
Ранее было рассказано, как с помощью простого переполнения буфера хакер может получить права пользователя root. Около 50% уязвимостей, обнаруженных в последние годы, были связаны именно с переполнением буфера. Поэтому к этой проблеме можно отнестись с большим вниманием.
Здесь будет рассказано о некоторых известных средствах защиты памяти, хотя ни одно из них не претендует на первенство, но они все же помогают решить проблему.
StackGuard.
StackGuard был разработан в 1998 г. как часть дистрибутива Immunix, но в то же время его надо было загружать отдельно. Во время его написания последней версией GCC была версия 2.96. В июне 2003 г. было объявлено, что новая версия StackGuard будет написана на GCC 3.x, но до сих пор данного не произошло. Поэтому не будем описывать инсталляцию и использование StackGuard, но потолкуем о том, как он работает, так как подобным образом работают несколько аналогичных проектов.
Ранее было показано, как хакер заставляет процессор реализовывать свой код. Он помещает инструкции в буфер и вызывает переполнение буфера. Адрес RET (адрес возврата) перезаписывается и устанавливается на начало буфера. В результате данного процессор начинает исполнять инструкции хакера. StackGuard помещает в стек специальное значение (canary value) непосредственно после RET, как показано на рисунке.
Это значение проверяется сразу после возврата функции. Любая попытка изменения адреса возврата (RET) приводит к изменению данного значения. StackGuard дает эффективный способ определения изменения значения RET
Конечно, хакер может перезаписать и это значение, подкорректировав его конкретным образом, чтобы StackGuard не заметил этого.
Но разработчики StackGuard предусмотрели и такой вариант развития событий: употребление произвольных значений и использования терминатора значений. В некоторых версиях StackGuard были доступными оба метода, но в последних версиях применяется только последний - терминатор.
При использовании временных проверочных значений используется 32-битный генератор случайных чисел, поэтому при каждом вызове функции проверочное значение изменялось. Шансы хакера корректно модифицировать это значение приравниваются к нулю.
Однако хакер может получить доступ к части системной памяти, содержащей это значение, и завладеть им. Правда, такой сценарий возможен, только если хакер получил права root. Но зачем тогда ему все это, если его цель (права root) достигнута.
Если знаете язык С, то должны знать, что символ \0 применяется для внутреннего представления конца строки. Создатели эксплоитов должны убедиться, что их shell-код не содержит данного символа, в противном случае выполнение кода будет преждевременно прервано. Терминаторы используют как раз эту технику: они заполняют проверочное значение четырьмя разными терминаторами строки: \0 (NULL), CR, LF и -1.
Если хакер хочет перезаписать RET, его shell-код должен повторно заполнить проверочное значение этими терминаторами. Так как он пытается переполнить массив символов, эти терминаторы будут обработаны как конец строки и оставшийся shell-код просто будет проигнорирован.
Режим защиты MemGuard.
StackGuard тоже дает еще один режим защиты - MemGuard. Вместо использования проверочных значений MemGuard просто помечает страницу памяти как read-only, это означает, что страница будет доступна только для чтения, и потом устанавливает собственный обработчик.
Этот обработчик перехватывает все попытки записи в эту область памяти и эмулирует запись, если она направлена на некритическую часть страницы (то есть не на адрес возврата). Такая техника более безопасна, но запись в память происходит в 1800 раз медленнее, что делает употребление MemGuard бесполезным.
