» ); wnd.document.close(); wnd.focus(); }
С приходом нового поколения процессоров Intel на базе архитектуры Ivy Bridge было представлено новое аппаратное средство безопасности. Оно называется Intel SMEP.
Авторы: Артём Шишкин и Илья Смит
С приходом нового поколения процессоров Intel на базе архитектуры Ivy Bridge было представлено новое аппаратное средство безопасности. Оно называется Intel SMEP. Как и бит NX, предотвращающий исполнение кода на странице памяти, оно добавляет головной боли при эксплуатации уязвимостей режима ядра.
В свою очередь Microsoft реализовала поддержку SMEP в Windows 8, таким образом сделав эту ОС ещё безопасней. Однако, первая реализация «в лоб» поддержки SMEP получилась с небольшим изъяном, благодаря которому у атакующего всё ещё есть возможность относительно безболезненной для него эксплуатации уязвимостей.
Что такое SMEP?
SMEP расшифровывается как “Supervisor Mode Execution Prevention” — предотвращение исполнения кода в режиме супервизора. Режим супервизора – это привилегированный режим работы процессора, в котором исполняется ядро ОС Windows 8. В понятиях ОС этот режим называется также режимом ядра. Противоположным ему является режим пользователя – в этом режиме исполняются пользовательские приложения.
Защита ОС строится на том, что пользовательские приложения не могут выполнять привилегированные операции, например, получить доступ к портам ввода-вывода, управляющим регистрам процессора и т.п. Кроме того, память, используемая в режиме ядра, защищена от доступа из пользовательского режима. Пользовательское приложение не может ни прочитать, ни изменить, ни выполнить код в памяти ядра напрямую. Взаимодействие с ядром ОС происходит опосредованно через интерфейс системных вызовов.
Привилегированный режим в свою очередь не имеет никаких ограничений, если бы не SMEP. Если он включен, любая попытка выполнить код, находящийся в памяти пользовательского приложения, приведет к ошибке страницы (page fault). В частности, в обработчике ошибок страниц на Windows 8 данная ситуация вызовет bugcheck.
Проще говоря, если какой-нибудь драйвер или системный модуль ядра попробует выполнить код, расположенный в памяти пользовательского приложения, закончится всё это синим экраном смерти с грустным смайликом.
А смысл?
Уязвимости режима ядра – самые «вкусные» для атакующего, поскольку при успешной эксплуатации он получает полный контроль над целевой системой. Смысл в том, что при эксплуатации уязвимостей режима ядра атакующий, как правило, выделяет для хранения шелл-кода память в пользовательском режиме.
«Но в режиме ядра теперь нельзя исполнять код со страниц памяти пользователя! А где теперь-то хранить шелл-код? А в чём смысл атаки без собственного шелл-кода? А может, и не надо тогда атаковать?»
С расчетом на подобный ход мыслей атакующего и была создана данная технология. Она способна защитить конечного пользователя от целого класса атак, если довести её реализацию до ума и прикрыть «тылы» другими механизмами защиты.
Первый блин комом
Давайте размышлять. В пользовательском пространстве хранить шелл-код нельзя. Значит, он должен располагаться в памяти ядра (выбор не велик – всего два режима), содержимое этого участка памяти должно быть нам подконтрольно (нельзя же писать из пользовательского режима напрямую), и должна быть возможность узнать адрес этого участка памяти из пользовательского режима (нельзя и читать из пользовательского режима напрямую). Что можно использовать для доставки шелл-кода в ядро?
Самое очевидное решение – использовать объекты Windows. Те самые, с которыми приложения работают через описатели (handle). Это всевозможные таймеры, события, мьютексы, порты завершения ввода-вывода, графические объекты и т.д. Тело такого объекта располагается в памяти ядра, а содержимое может быть изменено из пользовательского режима, и мы можем узнать, по какому адресу в пространстве ядра они расположены!
К примеру, когда мы открываем файл функцией CreateFile(), мы получаем описатель открытого файла. Если приложению нужно считать пару байт этого файла в буфер, то мы вызываем функцию ReadFile(), которая при помощи системного вызова передает управление в ядро ОС, где по таблице описателей находит нужный объект файла. В данном случае объект имеет тип _FILE_OBJECT, и изменяемым полем в нём является имя файла. Т.е. теоретически можно создать файл с именем «