Windows 8 – да будет 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, и изменяемым полем в нём является имя файла. Т.е. теоретически можно создать файл с именем "\0xBAADC0DE", которое будет содержать наш шелл-код. Затем мы эксплуатируем некую уязвимость режима ядра и передаем управление на наш шелл-код.

    0: kd> dt nt!_FILE_OBJECT
    +0x000 Type: Int2B
    +0x002 Size: Int2B

    +0x050 Flags: Uint4B
    +0x058 FileName: _UNICODE_STRING
    +0x068 CurrentByteOffset: _LARGE_INTEGER

    +0x0c0 IrpList: _LIST_ENTRY
    +0x0d0 FileObjectExtension: Ptr64 Void

    Успех? Пока ещё не совсем. Дело в том, что SMEP на Windows 8 дополняется другим механизмом защиты. Несмотря на то, что код расположен на странице режима ядра, эта страница имеет пометку (бит NX) о том, что она неисполняемая, т.е. на этой странице не может исполняться код! Получается, что объекты Windows защищены от исполнения, следовательно, тоже не подходят для хранения шелл-кода. Это утверждение верно для х64 версии Windows 8. Однако в x86 версии тела графических объектов располагаются в исполняемой памяти!

    Самым подходящим объектом для доставки шелл-кода оказалась палитра. Создается она при помощи функции CreatePalette() и структуры LOGPALETTE, содержимое которой и наполняется шелл-кодом. Ещё бы, как валидировать цвета в палитре? Ведь в нашей палитре будут именно те цвета, которые мы захотим! А мы захотим много байт NOP (0x90), и много байт шелл-кода. Вот такая получается «злая» палитра…



    Итого имеем схему обхода SMEP на Windows 8 x86:
    1. Создаем «злую» структуру LOGPALETTE, наполняя её шелл-кодом.
    2. Создаем палитру через CreatePalette().
    3. Находим адрес объекта палитры в ядре через разделяемую таблицу GDI.
    4. Передаем управление по некоторому смещению от объекта палитры.
    5. ???????
    6. PROFIT!!!

    А что делать с х64?


    Хотите обход SMEP на Windows 8 x64? Их есть у меня! Возможно способ не такой интересный, но зато рабочий. SMEP на x64 обходится при помощи возвратно-ориентированного программирования (ROP). Если кратко, то ROP использует уже присутствующие в памяти участки кода из других модулей. Таким образом, пробрасывать свой шелл-код в ядро не нужно.

    Конечно, возможности атакующего при составлении полезной нагрузки в данном случае сильно ограничены. Но всё, что нужно атакующему – отключить SMEP, а для этого в модуле «ntoskrnl» есть «подарочки» в виде функций HvlEndSystemInterrupt() и KiConfigureDynamicProcessor(). Последние байты этих функций позволяют отключить SMEP на заданном процессоре.

    HvlEndSystemInterrupt():
    …
    pop     rax
    pop     rcx
    retn
    


    KiConfigureDynamicProcessor():
    …
    mov     cr4, rax
    add     rsp, 28h
    retn
    


    // ROP chain to refresh cr4 value
    
    //                  vTrash			vROPChain
    DWORD_PTR dwRopStack[7      +       10] = {0};
    
    // HvlEndSystemInterrupt gadget
    dwRopStack[7 + 0] = dwKernelBase + HvlGadgetOffset;
    // New CR4 value
    dwRopStack[7 + 1] = 0x00000000000506F8;
    // KiConfigureDynamicProcessor
    dwRopStack[7 + 3] = dwKernelBase + Cr4GadgetOffset;
    // Out address (shellcode)
    dwRopStack[7 + 9] = (DWORD_PTR)pTestBuf;
    




    Долой ограничения


    После того как SMEP отключен, атакующий получает возможность исполнять код из пользовательского буфера, т.е. ограничений на размер шелл-кода больше нет. Можно использовать уязвимость ядра, которая была применена для отключения SMEP, для передачи уже на «злой» шелл-код.

    Заключение


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

    Также важно понимать, что это не эксплуатабельная уязвимость сама по себе. Это всего лишь метод обхода одного из механизмов защиты, и использоваться он может только в паре с какой-то другой эксплуатабельной уязвимостью.

    P.S. SMEP отключается 13-ю байтами кода. За подробностями прошу по ссылкам ниже.
    Как победить SMEP на Windows 8 x86
    Как победить SMEP на Windows 8 x64

    Авторы: Артём Шишкин и Илья Смит
    Метки:
    Positive Technologies 442,00
    Компания
    Поделиться публикацией
    Комментарии 18
    • 0
      с небольшим изъяном
      — скромности не занимать, Тёма?

      А что, неужели роп для x64 не универсален (если знать как достать dwKernelBase)?
      Как искал гаджеты: «mov cr4, *» по всему ядру?
      • +4
        dwKernelBase узнать можно через ZwQuerySystemInformation().
        Гаджеты именно так, ага. Есть ещё две занимательные функции — KiSaveProcessorControlState() и KiRestoreProcessorControlState(). «Чувак, это ропчик!»
        • 0
          Да, ROP не всегда юзабелен. Да и в любой уязвимости есть свои тонкости эксплуатации, я думаю…
      • +3
        Я вот что не понимаю: такая вещь, как сегменты, забыта окончательно? Достаточно было задать сегмент кода ядра начинающимся не с нуля, а с правильной точки, и выполнить код из пространства пользователя будет невозможно. А вместо этого придумана какая-то новая технология, которую к тому же можно отключить.

        Да, это разрушит единство адресного пространства кода и данных. Но неужели в ядре Windows до сих пор применяется самомодифицирующийся код?
        • +3
          Забыта, давно и надолго. Неэффективно, неудобно. В х64 вообще осталась пара значимых бит в дескрипторах сегментов. Защита на уровне страниц хорошо работает, а обход — недочет реализации.
        • +2
          Мельком взглянул на заголовок — показалось, что написано «да будет смерть» ))
          • +1
            Ох, необычные у вас иллюстрации…
            • +2
              Зато вы зашли и прокрутили статью до конца. Текст-то обычный.
            • 0
              Ах какой умный аффтор =). У меня только один вопрос ). Я таких суперсхем по 10 штук на дню могу рисовать с оговорками типа: это обязательно заработает… если только солнце светит не очень сильно; или… в майкрософт работают не полные дауны. Вами лично это супергениальная идея проверялась опытным путем?
              • +5
                Думаете люди в пэйперах байки травят? Конечно проверялась. А в мс даунов в кернел тим не берут — есть инсайдерская информация.
              • 0
                > закончится всё это синим экраном смерти с грустным смайликом.

                что характерно.

                Кстати, в Linux поддержка SMEP есть с весны прошлого года.
                • +15
                  Да, хорошо, что наконец вышел процессор, который его поддерживает!
                  • +2
                    да, у нас линуксоидов и с поддержкой USB 3.0 также было. появилась за полгода до выхода технологии на рынок, за год до появления в новом Windows и за полтора до патча на XP.
                    • +2
                      ага… а заработало оно номально на линухе еще через год после патча под ХР — если вообще кто проверял…
                      Понмиться мышкино колесико в линуксе лет через 10 после винды нормально стало подерживаться.
                      Нормально — это чтобы руками не лазить в конфиги иксов.
                      • 0
                        Ядро Linux разрабатывают Intel, AMD и прочие монстры, а также те, кто спецификации на USB 3.0 собственно пишут. Есть Linux с платной поддержкой, в т.ч. на матплатах с USB 3.0. Так по какой причине плохо работать? Не путайте Linux для энтузиастов и Linux для предприятий.

                        > Нормально — это чтобы руками не лазить в конфиги иксов.
                        Спасибо, за это последнее уточнение, а то я не сразу понял, что не так с колесом.
                    • 0
                      И что-то не видно поддержки SMAP для Windows 8? SMAP — это фича безопасности, которая появится в процессорах Intel Haswell в следующем году. (Появится на рынке, а не в лабораториях разработчиков ядра Linux.)

                      А в Linux вот как раз добавили поддержку:

                      www.phoronix.com/scan.php?page=news_item&px=MTE5NzI
                  • 0
                    Хоть бы ссылку дали на нормального автора, первооткрыватели нашлись видетели ). j00ru.vexillium.org/?p=783 к примеру и прочие его посты. Ну и какбы немного сторонней критики kaskadsecurity.blogspot.com/2012/09/intel-windows-8.html. Такое отношение типа на хабре всёравно дураки сидят, типа схавают чо, конечно «достойно» уважения, угу…
                    • 0
                      Ну, во-первых, ссылки есть. Не знаю, как вы их не заметили. Видать, тоже не читали пэйперы — там все подробно описано.
                      Во-вторых, метод j00ru нерабочий, поскольку reservation objects поNXены на 8ке. На хабре один стиль изложения, в пейперах — другой, очевидно же. А новости как бы не я писал, и за них не в ответе…

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое