Пожалуйста, обратите внимание, что пользователь заблокирован
Часть первая
Эксплойты повреждения памяти исторически были одним из самых сильных дополнений в инструментарии хорошей Red Team'ы. Они представляют собой легкую победу для атакующих инженеров безопасности, а также для противников, поскольку позволяют злоумышленнику выполнять полезные данные, не полагаясь на какое-либо взаимодействие с пользователем.
К счастью для защитников, но и к сожалению для исследователей и злоумышленников, эти типы эксплойтов становится все труднее выполнять, во многом благодаря широкому спектру средств защиты операционных систем, которые были реализованы непосредственно в системах, которые мы используем каждый день. Этот обширный аппарат смягчения последствий делает ранее тривиальную эксплуатацию дорогостоящей и сложной для более современного оборудования и программного обеспечения.
Эта статья состоит из двух частей, и она посвящена эволюции разработки эксплойтов и исследованию уязвимостей в системах Windows. Будут рассматриваться такие вопросы, как «Как это повлияет на картину будущих нарушений?» и «Стоит ли по-прежнему платить за разработку надежного, портативного и эффективного бинарного эксплойта?»
Как ты сюда попал?
С самого начала вычисления вызывали любопытство, что в конечном итоге привело к обнаружению «компьютерной ошибки» или непреднамеренного поведения систем в результате взаимодействия с пользователем. Это в свою очередь привело к использованию этих ошибок злоумышленниками со злым умыслом и положило начало эре бинарной эксплуатации. С тех пор ни исследователи безопасности, ни Red Team, ни противники ни разу не оглядывались назад.
Начало бинарной эксплуатации привело к тому, что производители, в первую очередь Microsoft и Apple (с особым упоминанием grsecurity для Linux, возглавившая обязанности более двух десятилетий назад), препятствовали этим эксплойтам с помощью различных средств защиты. Эти средства защиты от эксплуатации, многие из которых включены по умолчанию, снизили воздействие современных средств эксплуатации.
Подобно массовому использованию Active Directory в корпоративных средах, которое заставило исследователей Red Team уделять большое внимание продуктам Microsoft, злоумышленники и исследователи сделали Windows центром внимания из-за ее широкого использования как в корпоративных, так и в не корпоративных средах. В результате эта статья будет ориентирована на Windows и будет сосредоточена как на пользовательском режиме, так и на смягчении последствий в режиме ядра.
Классы уязвимости: тогда и сейчас
Когда дело доходит до бинарной эксплуатации, исследователям и противникам всегда приходилось отвечать на извечный вопрос: «Как можно выполнить код на цели без какого-либо взаимодействия с пользователем?» Ответ пришел в виде различных классов уязвимостей. Хотя это не исчерпывающий список, некоторые общие уязвимости включают:
Также стоит отметить, что каждый из этих классов уязвимостей может находиться в пользовательском режиме, режиме ядра, а в наши дни и в гипервизоре. Исторически уязвимости пользовательского режима эксплуатировались удаленно или через обычные настольные приложения, такие как браузеры, офисные пакеты для повышения производительности и программы чтения PDF-файлов. Однако уязвимости режима ядра в основном используются локально - после получения доступа к системе для повышения привилегий. Часто такие уязвимости сочетаются с уязвимостью пользовательского режима, достигая того, что часто называют локальным удаленным доступом . Кроме того, существуют такие случаи, как MS17-010 (обычно называемый EternalBlue) , CVE-2019-0708 (обычно называемый BlueKeep) и CVE-2020-0796 (обычно называемый SMBGhost), где возможно удаленное выполнение кода в ядре.
Из-за роста количества эксплойтов поставщики должны были предоставить какой-то способ предотвратить выполнение этих эксплойтов. Так родилась защита от эксплойтов.
Использование средств смягчения последствий: тогда и сейчас
В то время как исследователи безопасности и злоумышленники исторически имели преимущество в использовании различных методов доставки полезной нагрузки через уязвимости, поставщики постепенно начали выравнивать игровое поле, внедряя различные меры защиты, в надежде полностью исключить классы ошибок или нарушить общие методы эксплуатации. По крайней мере, есть надежда, что смягчение последствий сделает эту технику слишком дорогостоящей или ненадежной для массового использования, например для drive-by-download атак (эксплойтов-китов).
Как определено здесь, средства защиты от эксплойтов прошли долгий путь с момента появления Windows. В первую очередь будут рассмотрены устаревшие меры по снижению рисков - начальные меры по снижению рисков, выпущенные в операционных системах Microsoft. Современные средства защиты, которые включают более распространенные и задокументированные инструменты предотвращения эксплойтов, будут вторым столпом, описанным в этой серии. И, наконец, меры по снижению рисков, которые менее документированы и не так широко применяются, - называемые здесь «современными» или передовыми средствами защиты - завершат серию.
Устаревшее решение №1: DEP, также известный как No-eXecute (NX)
Предотвращение выполнения данных (DEP), известное как No-eXecute (NX), было одним из первых средств защиты, которое вынудило исследователей и противников принять дополнительные методы эксплуатации. DEP предотвращает выполнение произвольного кода в неисполняемых частях памяти. Он был представлен как в пользовательском режиме, так и в режиме ядра с Windows XP SP2, но только для кучи и стека пользовательского режима, а также для стека режима ядра плюс выгружаемая память ядра ( выгружаемый пул ). Потребовалось гораздо больше выпусков, вплоть до Windows 8, чтобы большая часть памяти кучи режима ядра, включая резидентную память ( невыгружаемый пул ) стала неисполняемой. Хотя это и считается «старым» средством защиты, оно остается тем, что должны учитывать все исследователи уязвимостей и злоумышленники.
Реализация DEP в режиме ядра и в пользовательском режиме очень похожа в том смысле, что DEP применяется для каждой страницы памяти через запись в таблице страниц. Запись таблицы страниц, или PTE, относится к записи самого низкого уровня в структурах подкачки, используемых для трансляции виртуальной памяти. Элементы PTE на очень высоком уровне содержат биты, которые отвечают за обеспечение различных разрешений и свойств для заданного диапазона виртуальных адресов. Каждый фрагмент виртуальной памяти, называемый страницей (обычно 4 КБ), помечается как исполняемый или доступный для записи - но не как то и другое одновременно - через запись в таблице страниц в ядре.
Используя !address и !pte команды в WinDbg может обеспечить более полное представление о реализации DEP.
Рисунок 1: Стек пользовательского режима имеет разрешения только на чтение\запись с включенным DEP
Рисунок 2: При включенном DEP адрес пользовательского режима 00007ffe`54a40000 не имеет установленного управляющего бита исполняемого PTE.
До того, как DEP в режиме ядра был расширен для покрытия резидентной кучи ядра в операционных системах Windows, PTE для таких распределений были помечены как RWX, а тип памяти NonPagedPool режима ядра был исполняемым и доступным для записи. Резидентная память относится к тому факту, что память, принадлежащая этому типу распределения, никогда не будет «выгружаться» из памяти, что означает, что этот тип виртуальной памяти всегда будет отображаться на действительный физический адрес.
С выпуском Windows 8 NonPagedPoolNx пул стал кучей режима ядра по умолчанию для распределения резидентной памяти. Это захватывает все свойства NonPagedPool, но делает его неисполняемым. Так же, как и для адресов пользовательского режима, исполняемый бит обеспечивается записью в таблице страниц виртуального адреса режима ядра.
Рисунок 3: При включенном DEP в режиме ядра в статической структуре режима ядра KUSER_SHARED_DATA не установлен бит управления исполняемым PTE
DEP в пользовательском режиме можно обойти с помощью обычных методов эксплуатации, таких как, программирование ориентированное на возврат (ROP), программирование ориентированное на вызов (COP), и программирование ориентированное на переход (JOP). Эти методы «кода повторного использования» используется для динамического вызова функций Windows API , таких как VirtualProtect() или WriteProcessMemory() к разрешениям изменения страниц памяти до RWX или записи шеллкода к уже существующей исполняемой области памяти с использованием указателей из разных модулей, загруженных во время выполнения. Помимо изменения прав доступа к памяти, также можно использоватьVirtualAlloc() или аналогичные процедуры для выделения исполняемой памяти.
DEP в режиме ядра можно обойти с помощью произвольного примитива чтения/записи для извлечения управляющих битов записи в таблице страниц для конкретной страницы в памяти и изменения их, чтобы разрешить доступ как для записи, так и для выполнения. Его также можно обойти, перенаправив поток выполнения в память пользовательского режима, которая уже была помечена как RWX, поскольку по умолчанию код режима ядра может вызывать код пользовательского режима по желанию.
Устаревшее средство смягчения последствий # 2: ASLR / kASLR
С добавлением DEP исследователи уязвимостей и злоумышленники быстро внедрили методы повторного использования кода. Реализация рандомизации адресного пространства (ASLR) и рандомизации адресного пространства ядра (KASLR) сделала эксплуатацию менее простой.
ASLR и его реализация режима ядра KASLR рандомизируют базовые адреса различных библиотек DLL, модулей и структур. Например, эта конкретная версия Windows 10 загружает ядро перед перезагрузкой по адресу виртуальной памяти fffff800`0fe00000.
Рисунок 4: Базовый адрес ntoskrnl.exe до перезагрузки
После перезагрузки ядро загружается по другому виртуальному адресуfffff800`07000000.
Рисунок 5: Базовый адрес ntoskrnl.exe после перезагрузки
Исторически до реализации ASLR победить DEP было столь же тривиально, как дизассемблирование приложения или DLL сырых ассемблерных инструкций и использование указателей на эти инструкции, которые были статическими до ASLR, для обхода DEP. Однако при реализации ASLR обычно требуется одно из трех действий:
Поскольку Windows выполняет ASLR только для каждой загрузки, все процессы используют одну и ту же структуру адресного пространства после запуска системы. Следовательно, ASLR неэффективен против локального злоумышленника, который уже выполнил код. Точно так же, поскольку ядро предоставляет непривилегированным пользователям API-интерфейсы самоанализа, которые предоставляют адреса памяти ядра, KASLR также не эффективен против этого класса атак. По этой причине ASLR и KASLR в Windows являются только эффективными средствами защиты от векторов удаленной эксплуатации.
Однако с появлением локального удаленного доступа было признано, что KASLR был неэффективен против удаленных злоумышленников, которые сначала достигли пользовательского RCE, потому что, как уже упоминалось, определенные функции Windows API, такие как EnumDeviceDrivers() или NtQuerySystemInformation(), могут использоваться для перечисления базовых адресов всех загруженных модулей ядра.
Поскольку локальный удаленный злоумышленник сначала начал бы с RCE пользовательского режима, нацеленного на браузер и т. д., Microsoft начала усиленно обеспечивать выполнение таких приложений в изолированной среде и ввела обязательный контроль целостности (MIC), а затем AppContainer, как способ снизить привилегии этих приложений, в том числе благодаря их запуску с низким уровнем целостности. Затем в Windows 8.1 был заблокировал доступ к таким интроспективным функциям API для процессов среднего уровня целостности и выше.
Следовательно, процесс с низким уровнем целостности, такой как песочница браузера, потребует уязвимости утечки информации, чтобы обойти KASLR.
Рисунок 6: Вызов EnumDeviceDrivers() заблокирован из-за низкой целостности
Несколько других примитивов утечки базового адреса ядра были смягчены в различных сборках Windows 10. Примечательно, что куча уровня аппаратной абстракции (HAL), которая содержит несколько указателей на ядро, также была расположена в фиксированном месте. Это было связано с тем, что куча HAL необходима на очень ранней стадии процесса загрузки, даже до того, как будет инициализирован фактический менеджер памяти Windows. В то время лучшим решением было зарезервировать память для кучи HAL в идеально фиксированном месте. Это было смягчено с помощью сборки Windows 10 Creators Update (RS2).
Несмотря на то, что ASLR почти так же стар, как DEP, и оба они являются одними из первых реализованных средств защиты, их необходимо учитывать при современной эксплуатации.
Современное смягчение последствий # 1: CFG / kCFG
Control Flow Guard (CFG) и его реализация в ядре, известная как kCFG, является версией Microsoft Control Flow Integrity (CFI). CFG работает, выполняя проверки косвенных вызовов функций, сделанных внутри модулей и приложений, скомпилированных с помощью CFG. Кроме того, ядро Windows было скомпилировано с помощью kCFG, начиная с выпуска Windows 10 1703 (RS2). Обратите внимание, однако, что для включения kCFG необходимо включить VBS (безопасность на основе виртуализации). VBS будет обсуждаться более подробно во второй части этой статьи.
С учетом эффективности для пользователей косвенные вызовы, защищенные CFG, проверяются с использованием битовой карты с набором битов, указывающих, является ли цель «действительной» или если цель «недействительная». Цель считается «действительной», если она представляет собой начальное положение функции в модуле, загруженном в процесс. Это означает, что битовая карта представляет все адресное пространство процесса. Каждый модуль, скомпилированный с помощью CFG, имеет свой собственный набор битов в битовой карте, в зависимости от того, где он был загружен в память. Как описано в разделе ASLR, Windows рандомизирует адресное пространство только при загрузке, поэтому этот битовый массив обычно используется всеми процессами, что позволяет сэкономить значительный объем памяти.
Как правило, на очень высоком уровне косвенные вызовы функций пользовательского режима передаются guard_check_icall функции (или guard_dispatch_icall в других случаях). Затем эта функция разыменовывает функцию _guard_check_icall_fptr и выполняет переход к указателю, который является указателем на функцию LdrpValidateUserCallTargetES (или LdrpValidateUserCallTarget в других случаях).
Рисунок 7: Реализация CFG в пользовательском режиме
Выполняется серия побитовых операций и ассемблерных функций, что приводит к проверке растрового изображения, чтобы определить, является ли функция в косвенном вызове функции допустимой функцией внутри растрового изображения. Неверная функция приведет к завершению процесса.
kCFG имеет очень похожую реализацию, в которой косвенные вызовы функций проверяются kCFG. В частности, это «ломает» [nt!HalDispatchTable+0x8] примитив, который злоумышленники и исследователи использовали для выполнения кода в контексте ядра путем вызова nt!KeQueryIntervalProfile, который выполняет косвенный вызов функции в [nt!HalDispatchTable+0x8] 64-битных системах.
Рисунок 8: [nt!HalDispatchTable+0x8] теперь охраняется kCFG через nt!KeQueryIntervalProfile косвенный вызов
kCFG использует немного другую модификацию CFG, в которой битовая карта хранится внутри переменной nt!guard_icall_bitmap. Кроме того, nt!_guard_dispatch_icall запускает процедуру проверки цели, и никаких других вызовов функций не требуется.
Рисунок 9: Реализация kCFG
CFG смягчает тот факт, что в какой-то момент жизненного цикла разработки эксплойта может потребоваться перезапись указателя функции, чтобы он указывал на другой указатель на функцию, который может быть полезен противнику (например, VirtualProtect ).
CFG - это первое преимущество по снижения CFI. Это означает, что он не принимает во внимание инструкции ret , что является случаем обратного преимущества. Поскольку CFG не проверяет адреса возврата, CFG можно обойти, используя утечку информации, что может позволить действие, такое как синтаксический анализ блока среды потока (TEB), для утечки стека. Используя это знание, можно будет перезаписать адрес возврата функции в стеке со злым умыслом.
Со временем было обнаружено, что CFG имеет несколько недостатков. Например, обратите внимание, что модули используют таблицу адресов импорта (IAT) для импорта, например, функции Windows API. Эти таблицы IAT по сути являются виртуальными адресами в определенном модуле, которые указывают на функции Windows API.
Рисунок 10: IAT edgehtml.dll
IAT по умолчанию доступен только для чтения и, как правило, не может быть изменен. Microsoft считает эти функции «безопасными» из-за их состояния только для чтения, что означает, что CFG / kCFG не защищает эти функции. Если бы злоумышленник мог изменить или добавить вредоносную запись в IAT, можно было бы вызвать определяемый пользователем указатель.
Кроме того, злоумышленники могут использовать дополнительные функции ОС для выполнения кода. В соответствии с проектом, CFG / kCFG проверяет только если функция начинается в месте , обозначенном растрового изображения - не то, что функция является то , что она претендует быть. Если злоумышленник или исследователь смог найти дополнительные функции, отмеченные как действительные в битовой карте CFG / kCFG, то можно будет перезаписать указатель функции указателем другой функции на выполнение «прокси» кода. Это может привести например к атаке с использованием путаницы типов данных, когда другая, неожиданная функция теперь выполняется с параметрами/объектами исходной ожидаемой функции.
Как упоминалось ранее, kCFG включается только тогда, когда включен VBS. Одна интересная характеристика kCFG заключается в том, что даже когда VBS не включен, функции и процедуры диспетчеризации kCFG все еще присутствуют, и вызовы функций по-прежнему проходят через них. С включенным VBS или без него kCFG выполняет поразрядную проверку «старших» битов виртуального адреса, чтобы определить, является ли адрес расширенным по знаку (также известный как адрес режима ядра). Если адрес пользовательского режима обнаружен, независимо от того, включен ли HVCI, kCFG вызовет проверку на наличие ошибок KERNEL_SECURITY_CHECK_FAILURE. Это одно из средств защиты от принуждения кода режима ядра к вызову кода пользовательского режима, который, как мы видели, является потенциальным методом обхода DEP. В следующем разделе мы поговорим о предотвращении выполнения в режиме супервизора (SMEP), которое также является современным средством защиты от этой атаки.
Также стоит отметить, что растровое изображение kCFG защищено HVCI или целостностью кода, защищенной гипервизором. О HVCI будет упоминаться во второй части этой статьи.
Современное смягчение последствий # 2: SMEP
Предотвращение выполнения в режиме супервизора - это аппаратное средство защиты ЦП, которое было реализовано специально против эксплойтов ядра.
Когда он NonPagedPoolNx был представлен, исследователи и злоумышленники больше не могли писать шелл-код непосредственно в режим ядра и выполнять его. Это привело к мысли, что такую функцию Windows API VirtualAlloc() можно использовать для выделения шелл-кода в пользовательском режиме, а затем передать возвращенный указатель на шелл-код обратно в режим ядра. Затем ядро будет выполнять код пользовательского режима «в контексте» ядра, что означает, что шелл-код будет работать с полными привилегиями ядра.
SMEP работает для смягчения этой атаки, запрещая выполнение кода пользовательского режима из ядра. В частности, процессоры на базе x86 имеют внутреннее состояние, известное как уровень привилегий кода (CPL). Эти процессоры имеют четыре разных CPL, известных как кольца. Windows использует только два из этих колец: кольцо 3, относящееся ко всему, что находится в пользовательском режиме, и кольцо 0, которое относится ко всему, что находится в режиме ядра. SMEP запрещает выполнение кода, принадлежащего CPL 3, в контексте CPL 0.
SMEP включается через 20-й бит регистра управления CR4. Регистр управления - это регистр, используемый для изменения или включения определенных функций ЦП, таких как реализация виртуальной памяти посредством подкачки страниц и т. д. Хотя SMEP включается через 20-й бит регистра CR4, он затем принудительно применяется через PTE адрес памяти. Принуждение SMEP работает путем проверки бита User vs. Supervisor(U/S) PTE на наличие любой структуры подкачки. Если бит установлен в значение U(ser), страница рассматривается как страница пользовательского режима. Если бит очищен, то есть бит представлен как S(upervisor), страница обрабатывается как страница супервизора (режим ядра).
Как объяснил Алекс Ионеску на Infiltrate 2015 , если только одна из записей подкачки установлена на «S» - SMEP не вызовет сбоя. Эта реализация важна, поскольку SMEP можно обойти, «обманом» заставив ЦП выполнить шелл-код из пользовательского режима с помощью произвольной записи.
Во-первых, найти PTE для распределения в пользовательском режиме, а затем снимите U/S немного , чтобы заставить его быть установлен в положение S . Когда это происходит, ЦП будет рассматривать этот адрес пользовательского режима как страницу режима ядра, позволяя выполнить выполнение.
Рисунок 11: Использование произвольной уязвимости записи для сброса U/S бита, в результате чего страница пользовательского режима становится страницей режима ядра
Более старый метод обхода SMEP заключается в его отключении в масштабе всей системы за счет использования ROP. Злоумышленник может использовать гаджет ROP в режиме ядра, найдя тот, который позволяет переопределить значение регистра CR4 на значение с очищенным 20-м битом, что включает SMEP, обратно в регистр CR4.
Обратной стороной этого метода является то, что вы должны использовать гаджеты ROP в режиме ядра, чтобы поддерживать выполнение в ядре, чтобы соответствовать правилам SMEP. Кроме того, как и во всех атаках с повторным использованием кода, смещение между гаджетами может меняться в зависимости от версии Windows, и для работы ROP необходимо управление стеком . Защита под названием HyperGuard, которая выходит за рамки этоой статьи, также защищает от модификации CR4 в современных системах.
Рисунок 12: Полное отключение SMEP в масштабе всей системы с помощью манипуляции с CR4
kd> bp nt! часть_2
В этом статье были пересмотрены устаревшие средства защиты, а также современные средства защиты, такие как CFG и SMEP, которые направлены на то, чтобы бросить вызов исследователям уязвимостей и поднять планку и качество эксплойтов. Эти темы создают основу для более современных средств защиты, таких как ACG, XFG, CET и VBS, которые добавляют сложности - увеличивают воздействие эксплуатации и побуждают читателей стать более любознательными в отношении окупаемости инвестиций в разработку современных эксплойтов. В следующей части мы обсуждим более современные способы эксплуатации и множество средств защиты, разработанных Microsoft для борьбы с ними.
Источник: https://www.crowdstrike.com/blog/state-of-exploit-development-part-1/
Эксплойты повреждения памяти исторически были одним из самых сильных дополнений в инструментарии хорошей Red Team'ы. Они представляют собой легкую победу для атакующих инженеров безопасности, а также для противников, поскольку позволяют злоумышленнику выполнять полезные данные, не полагаясь на какое-либо взаимодействие с пользователем.
К счастью для защитников, но и к сожалению для исследователей и злоумышленников, эти типы эксплойтов становится все труднее выполнять, во многом благодаря широкому спектру средств защиты операционных систем, которые были реализованы непосредственно в системах, которые мы используем каждый день. Этот обширный аппарат смягчения последствий делает ранее тривиальную эксплуатацию дорогостоящей и сложной для более современного оборудования и программного обеспечения.
Эта статья состоит из двух частей, и она посвящена эволюции разработки эксплойтов и исследованию уязвимостей в системах Windows. Будут рассматриваться такие вопросы, как «Как это повлияет на картину будущих нарушений?» и «Стоит ли по-прежнему платить за разработку надежного, портативного и эффективного бинарного эксплойта?»
Как ты сюда попал?
С самого начала вычисления вызывали любопытство, что в конечном итоге привело к обнаружению «компьютерной ошибки» или непреднамеренного поведения систем в результате взаимодействия с пользователем. Это в свою очередь привело к использованию этих ошибок злоумышленниками со злым умыслом и положило начало эре бинарной эксплуатации. С тех пор ни исследователи безопасности, ни Red Team, ни противники ни разу не оглядывались назад.
Начало бинарной эксплуатации привело к тому, что производители, в первую очередь Microsoft и Apple (с особым упоминанием grsecurity для Linux, возглавившая обязанности более двух десятилетий назад), препятствовали этим эксплойтам с помощью различных средств защиты. Эти средства защиты от эксплуатации, многие из которых включены по умолчанию, снизили воздействие современных средств эксплуатации.
Подобно массовому использованию Active Directory в корпоративных средах, которое заставило исследователей Red Team уделять большое внимание продуктам Microsoft, злоумышленники и исследователи сделали Windows центром внимания из-за ее широкого использования как в корпоративных, так и в не корпоративных средах. В результате эта статья будет ориентирована на Windows и будет сосредоточена как на пользовательском режиме, так и на смягчении последствий в режиме ядра.
Классы уязвимости: тогда и сейчас
Когда дело доходит до бинарной эксплуатации, исследователям и противникам всегда приходилось отвечать на извечный вопрос: «Как можно выполнить код на цели без какого-либо взаимодействия с пользователем?» Ответ пришел в виде различных классов уязвимостей. Хотя это не исчерпывающий список, некоторые общие уязвимости включают:
- Классическое переполнение стека (да, даже в 2020 году): это возможность перезаписать существующий контент в стеке и использовать контролируемую запись для определения местоположения и повреждения адреса возврата функции для перехода в произвольное место.
- Использование памяти после освобождения: объект размещается в памяти в куче в пользовательском режиме (или в памяти пула режима ядра). Когда происходит преждевременное «освобождение» этого объекта, ссылка / дескриптор этого освобожденного объекта остается. Используя другой примитив, новый объект создается на месте освобожденного объекта, и ссылка на старый объект используется для выполнения или иного изменения нового объекта, который действует вместо старого объекта. Ожидается, что эти неожиданные изменения в новом объекте каким-то образом приведут к повышению привилегий или другой вредоносной возможности.
- Произвольная запись: это возможность произвольно записывать данные, такие как один или несколько указателей, в произвольное место. Это также могло быть результатом другого класса уязвимостей. Примитивы произвольной записи также могут использоваться, как примитивы произвольного чтения в зависимости от точности, имеющейся над примитивом записи.
- Путаница типов данных: объект относится к одному типу, но позже на этот тип ссылаются как на другой тип. Из-за расположения в памяти различных типов данных это может привести к неожиданному поведению.
Также стоит отметить, что каждый из этих классов уязвимостей может находиться в пользовательском режиме, режиме ядра, а в наши дни и в гипервизоре. Исторически уязвимости пользовательского режима эксплуатировались удаленно или через обычные настольные приложения, такие как браузеры, офисные пакеты для повышения производительности и программы чтения PDF-файлов. Однако уязвимости режима ядра в основном используются локально - после получения доступа к системе для повышения привилегий. Часто такие уязвимости сочетаются с уязвимостью пользовательского режима, достигая того, что часто называют локальным удаленным доступом . Кроме того, существуют такие случаи, как MS17-010 (обычно называемый EternalBlue) , CVE-2019-0708 (обычно называемый BlueKeep) и CVE-2020-0796 (обычно называемый SMBGhost), где возможно удаленное выполнение кода в ядре.
Из-за роста количества эксплойтов поставщики должны были предоставить какой-то способ предотвратить выполнение этих эксплойтов. Так родилась защита от эксплойтов.
Использование средств смягчения последствий: тогда и сейчас
В то время как исследователи безопасности и злоумышленники исторически имели преимущество в использовании различных методов доставки полезной нагрузки через уязвимости, поставщики постепенно начали выравнивать игровое поле, внедряя различные меры защиты, в надежде полностью исключить классы ошибок или нарушить общие методы эксплуатации. По крайней мере, есть надежда, что смягчение последствий сделает эту технику слишком дорогостоящей или ненадежной для массового использования, например для drive-by-download атак (эксплойтов-китов).
Как определено здесь, средства защиты от эксплойтов прошли долгий путь с момента появления Windows. В первую очередь будут рассмотрены устаревшие меры по снижению рисков - начальные меры по снижению рисков, выпущенные в операционных системах Microsoft. Современные средства защиты, которые включают более распространенные и задокументированные инструменты предотвращения эксплойтов, будут вторым столпом, описанным в этой серии. И, наконец, меры по снижению рисков, которые менее документированы и не так широко применяются, - называемые здесь «современными» или передовыми средствами защиты - завершат серию.
Устаревшее решение №1: DEP, также известный как No-eXecute (NX)
Предотвращение выполнения данных (DEP), известное как No-eXecute (NX), было одним из первых средств защиты, которое вынудило исследователей и противников принять дополнительные методы эксплуатации. DEP предотвращает выполнение произвольного кода в неисполняемых частях памяти. Он был представлен как в пользовательском режиме, так и в режиме ядра с Windows XP SP2, но только для кучи и стека пользовательского режима, а также для стека режима ядра плюс выгружаемая память ядра ( выгружаемый пул ). Потребовалось гораздо больше выпусков, вплоть до Windows 8, чтобы большая часть памяти кучи режима ядра, включая резидентную память ( невыгружаемый пул ) стала неисполняемой. Хотя это и считается «старым» средством защиты, оно остается тем, что должны учитывать все исследователи уязвимостей и злоумышленники.
Реализация DEP в режиме ядра и в пользовательском режиме очень похожа в том смысле, что DEP применяется для каждой страницы памяти через запись в таблице страниц. Запись таблицы страниц, или PTE, относится к записи самого низкого уровня в структурах подкачки, используемых для трансляции виртуальной памяти. Элементы PTE на очень высоком уровне содержат биты, которые отвечают за обеспечение различных разрешений и свойств для заданного диапазона виртуальных адресов. Каждый фрагмент виртуальной памяти, называемый страницей (обычно 4 КБ), помечается как исполняемый или доступный для записи - но не как то и другое одновременно - через запись в таблице страниц в ядре.
Используя !address и !pte команды в WinDbg может обеспечить более полное представление о реализации DEP.
Рисунок 1: Стек пользовательского режима имеет разрешения только на чтение\запись с включенным DEP
Рисунок 2: При включенном DEP адрес пользовательского режима 00007ffe`54a40000 не имеет установленного управляющего бита исполняемого PTE.
До того, как DEP в режиме ядра был расширен для покрытия резидентной кучи ядра в операционных системах Windows, PTE для таких распределений были помечены как RWX, а тип памяти NonPagedPool режима ядра был исполняемым и доступным для записи. Резидентная память относится к тому факту, что память, принадлежащая этому типу распределения, никогда не будет «выгружаться» из памяти, что означает, что этот тип виртуальной памяти всегда будет отображаться на действительный физический адрес.
С выпуском Windows 8 NonPagedPoolNx пул стал кучей режима ядра по умолчанию для распределения резидентной памяти. Это захватывает все свойства NonPagedPool, но делает его неисполняемым. Так же, как и для адресов пользовательского режима, исполняемый бит обеспечивается записью в таблице страниц виртуального адреса режима ядра.
Рисунок 3: При включенном DEP в режиме ядра в статической структуре режима ядра KUSER_SHARED_DATA не установлен бит управления исполняемым PTE
DEP в пользовательском режиме можно обойти с помощью обычных методов эксплуатации, таких как, программирование ориентированное на возврат (ROP), программирование ориентированное на вызов (COP), и программирование ориентированное на переход (JOP). Эти методы «кода повторного использования» используется для динамического вызова функций Windows API , таких как VirtualProtect() или WriteProcessMemory() к разрешениям изменения страниц памяти до RWX или записи шеллкода к уже существующей исполняемой области памяти с использованием указателей из разных модулей, загруженных во время выполнения. Помимо изменения прав доступа к памяти, также можно использоватьVirtualAlloc() или аналогичные процедуры для выделения исполняемой памяти.
DEP в режиме ядра можно обойти с помощью произвольного примитива чтения/записи для извлечения управляющих битов записи в таблице страниц для конкретной страницы в памяти и изменения их, чтобы разрешить доступ как для записи, так и для выполнения. Его также можно обойти, перенаправив поток выполнения в память пользовательского режима, которая уже была помечена как RWX, поскольку по умолчанию код режима ядра может вызывать код пользовательского режима по желанию.
Устаревшее средство смягчения последствий # 2: ASLR / kASLR
С добавлением DEP исследователи уязвимостей и злоумышленники быстро внедрили методы повторного использования кода. Реализация рандомизации адресного пространства (ASLR) и рандомизации адресного пространства ядра (KASLR) сделала эксплуатацию менее простой.
ASLR и его реализация режима ядра KASLR рандомизируют базовые адреса различных библиотек DLL, модулей и структур. Например, эта конкретная версия Windows 10 загружает ядро перед перезагрузкой по адресу виртуальной памяти fffff800`0fe00000.
Рисунок 4: Базовый адрес ntoskrnl.exe до перезагрузки
После перезагрузки ядро загружается по другому виртуальному адресуfffff800`07000000.
Рисунок 5: Базовый адрес ntoskrnl.exe после перезагрузки
Исторически до реализации ASLR победить DEP было столь же тривиально, как дизассемблирование приложения или DLL сырых ассемблерных инструкций и использование указателей на эти инструкции, которые были статическими до ASLR, для обхода DEP. Однако при реализации ASLR обычно требуется одно из трех действий:
- Используйте библиотеки DLL и приложения, которые не скомпилированы с ASLR.
- Использовать уязвимость за пределами границ или какой-либо другой тип утечки информации / памяти
- Брутфорс адресного пространства (невозможно в 64-битных системах)
Поскольку Windows выполняет ASLR только для каждой загрузки, все процессы используют одну и ту же структуру адресного пространства после запуска системы. Следовательно, ASLR неэффективен против локального злоумышленника, который уже выполнил код. Точно так же, поскольку ядро предоставляет непривилегированным пользователям API-интерфейсы самоанализа, которые предоставляют адреса памяти ядра, KASLR также не эффективен против этого класса атак. По этой причине ASLR и KASLR в Windows являются только эффективными средствами защиты от векторов удаленной эксплуатации.
Однако с появлением локального удаленного доступа было признано, что KASLR был неэффективен против удаленных злоумышленников, которые сначала достигли пользовательского RCE, потому что, как уже упоминалось, определенные функции Windows API, такие как EnumDeviceDrivers() или NtQuerySystemInformation(), могут использоваться для перечисления базовых адресов всех загруженных модулей ядра.
Поскольку локальный удаленный злоумышленник сначала начал бы с RCE пользовательского режима, нацеленного на браузер и т. д., Microsoft начала усиленно обеспечивать выполнение таких приложений в изолированной среде и ввела обязательный контроль целостности (MIC), а затем AppContainer, как способ снизить привилегии этих приложений, в том числе благодаря их запуску с низким уровнем целостности. Затем в Windows 8.1 был заблокировал доступ к таким интроспективным функциям API для процессов среднего уровня целостности и выше.
Следовательно, процесс с низким уровнем целостности, такой как песочница браузера, потребует уязвимости утечки информации, чтобы обойти KASLR.
Рисунок 6: Вызов EnumDeviceDrivers() заблокирован из-за низкой целостности
Несколько других примитивов утечки базового адреса ядра были смягчены в различных сборках Windows 10. Примечательно, что куча уровня аппаратной абстракции (HAL), которая содержит несколько указателей на ядро, также была расположена в фиксированном месте. Это было связано с тем, что куча HAL необходима на очень ранней стадии процесса загрузки, даже до того, как будет инициализирован фактический менеджер памяти Windows. В то время лучшим решением было зарезервировать память для кучи HAL в идеально фиксированном месте. Это было смягчено с помощью сборки Windows 10 Creators Update (RS2).
Несмотря на то, что ASLR почти так же стар, как DEP, и оба они являются одними из первых реализованных средств защиты, их необходимо учитывать при современной эксплуатации.
Современное смягчение последствий # 1: CFG / kCFG
Control Flow Guard (CFG) и его реализация в ядре, известная как kCFG, является версией Microsoft Control Flow Integrity (CFI). CFG работает, выполняя проверки косвенных вызовов функций, сделанных внутри модулей и приложений, скомпилированных с помощью CFG. Кроме того, ядро Windows было скомпилировано с помощью kCFG, начиная с выпуска Windows 10 1703 (RS2). Обратите внимание, однако, что для включения kCFG необходимо включить VBS (безопасность на основе виртуализации). VBS будет обсуждаться более подробно во второй части этой статьи.
С учетом эффективности для пользователей косвенные вызовы, защищенные CFG, проверяются с использованием битовой карты с набором битов, указывающих, является ли цель «действительной» или если цель «недействительная». Цель считается «действительной», если она представляет собой начальное положение функции в модуле, загруженном в процесс. Это означает, что битовая карта представляет все адресное пространство процесса. Каждый модуль, скомпилированный с помощью CFG, имеет свой собственный набор битов в битовой карте, в зависимости от того, где он был загружен в память. Как описано в разделе ASLR, Windows рандомизирует адресное пространство только при загрузке, поэтому этот битовый массив обычно используется всеми процессами, что позволяет сэкономить значительный объем памяти.
Как правило, на очень высоком уровне косвенные вызовы функций пользовательского режима передаются guard_check_icall функции (или guard_dispatch_icall в других случаях). Затем эта функция разыменовывает функцию _guard_check_icall_fptr и выполняет переход к указателю, который является указателем на функцию LdrpValidateUserCallTargetES (или LdrpValidateUserCallTarget в других случаях).
Рисунок 7: Реализация CFG в пользовательском режиме
Выполняется серия побитовых операций и ассемблерных функций, что приводит к проверке растрового изображения, чтобы определить, является ли функция в косвенном вызове функции допустимой функцией внутри растрового изображения. Неверная функция приведет к завершению процесса.
kCFG имеет очень похожую реализацию, в которой косвенные вызовы функций проверяются kCFG. В частности, это «ломает» [nt!HalDispatchTable+0x8] примитив, который злоумышленники и исследователи использовали для выполнения кода в контексте ядра путем вызова nt!KeQueryIntervalProfile, который выполняет косвенный вызов функции в [nt!HalDispatchTable+0x8] 64-битных системах.
Рисунок 8: [nt!HalDispatchTable+0x8] теперь охраняется kCFG через nt!KeQueryIntervalProfile косвенный вызов
kCFG использует немного другую модификацию CFG, в которой битовая карта хранится внутри переменной nt!guard_icall_bitmap. Кроме того, nt!_guard_dispatch_icall запускает процедуру проверки цели, и никаких других вызовов функций не требуется.
Рисунок 9: Реализация kCFG
CFG смягчает тот факт, что в какой-то момент жизненного цикла разработки эксплойта может потребоваться перезапись указателя функции, чтобы он указывал на другой указатель на функцию, который может быть полезен противнику (например, VirtualProtect ).
CFG - это первое преимущество по снижения CFI. Это означает, что он не принимает во внимание инструкции ret , что является случаем обратного преимущества. Поскольку CFG не проверяет адреса возврата, CFG можно обойти, используя утечку информации, что может позволить действие, такое как синтаксический анализ блока среды потока (TEB), для утечки стека. Используя это знание, можно будет перезаписать адрес возврата функции в стеке со злым умыслом.
Со временем было обнаружено, что CFG имеет несколько недостатков. Например, обратите внимание, что модули используют таблицу адресов импорта (IAT) для импорта, например, функции Windows API. Эти таблицы IAT по сути являются виртуальными адресами в определенном модуле, которые указывают на функции Windows API.
Рисунок 10: IAT edgehtml.dll
IAT по умолчанию доступен только для чтения и, как правило, не может быть изменен. Microsoft считает эти функции «безопасными» из-за их состояния только для чтения, что означает, что CFG / kCFG не защищает эти функции. Если бы злоумышленник мог изменить или добавить вредоносную запись в IAT, можно было бы вызвать определяемый пользователем указатель.
Кроме того, злоумышленники могут использовать дополнительные функции ОС для выполнения кода. В соответствии с проектом, CFG / kCFG проверяет только если функция начинается в месте , обозначенном растрового изображения - не то, что функция является то , что она претендует быть. Если злоумышленник или исследователь смог найти дополнительные функции, отмеченные как действительные в битовой карте CFG / kCFG, то можно будет перезаписать указатель функции указателем другой функции на выполнение «прокси» кода. Это может привести например к атаке с использованием путаницы типов данных, когда другая, неожиданная функция теперь выполняется с параметрами/объектами исходной ожидаемой функции.
Как упоминалось ранее, kCFG включается только тогда, когда включен VBS. Одна интересная характеристика kCFG заключается в том, что даже когда VBS не включен, функции и процедуры диспетчеризации kCFG все еще присутствуют, и вызовы функций по-прежнему проходят через них. С включенным VBS или без него kCFG выполняет поразрядную проверку «старших» битов виртуального адреса, чтобы определить, является ли адрес расширенным по знаку (также известный как адрес режима ядра). Если адрес пользовательского режима обнаружен, независимо от того, включен ли HVCI, kCFG вызовет проверку на наличие ошибок KERNEL_SECURITY_CHECK_FAILURE. Это одно из средств защиты от принуждения кода режима ядра к вызову кода пользовательского режима, который, как мы видели, является потенциальным методом обхода DEP. В следующем разделе мы поговорим о предотвращении выполнения в режиме супервизора (SMEP), которое также является современным средством защиты от этой атаки.
Также стоит отметить, что растровое изображение kCFG защищено HVCI или целостностью кода, защищенной гипервизором. О HVCI будет упоминаться во второй части этой статьи.
Современное смягчение последствий # 2: SMEP
Предотвращение выполнения в режиме супервизора - это аппаратное средство защиты ЦП, которое было реализовано специально против эксплойтов ядра.
Когда он NonPagedPoolNx был представлен, исследователи и злоумышленники больше не могли писать шелл-код непосредственно в режим ядра и выполнять его. Это привело к мысли, что такую функцию Windows API VirtualAlloc() можно использовать для выделения шелл-кода в пользовательском режиме, а затем передать возвращенный указатель на шелл-код обратно в режим ядра. Затем ядро будет выполнять код пользовательского режима «в контексте» ядра, что означает, что шелл-код будет работать с полными привилегиями ядра.
SMEP работает для смягчения этой атаки, запрещая выполнение кода пользовательского режима из ядра. В частности, процессоры на базе x86 имеют внутреннее состояние, известное как уровень привилегий кода (CPL). Эти процессоры имеют четыре разных CPL, известных как кольца. Windows использует только два из этих колец: кольцо 3, относящееся ко всему, что находится в пользовательском режиме, и кольцо 0, которое относится ко всему, что находится в режиме ядра. SMEP запрещает выполнение кода, принадлежащего CPL 3, в контексте CPL 0.
SMEP включается через 20-й бит регистра управления CR4. Регистр управления - это регистр, используемый для изменения или включения определенных функций ЦП, таких как реализация виртуальной памяти посредством подкачки страниц и т. д. Хотя SMEP включается через 20-й бит регистра CR4, он затем принудительно применяется через PTE адрес памяти. Принуждение SMEP работает путем проверки бита User vs. Supervisor(U/S) PTE на наличие любой структуры подкачки. Если бит установлен в значение U(ser), страница рассматривается как страница пользовательского режима. Если бит очищен, то есть бит представлен как S(upervisor), страница обрабатывается как страница супервизора (режим ядра).
Как объяснил Алекс Ионеску на Infiltrate 2015 , если только одна из записей подкачки установлена на «S» - SMEP не вызовет сбоя. Эта реализация важна, поскольку SMEP можно обойти, «обманом» заставив ЦП выполнить шелл-код из пользовательского режима с помощью произвольной записи.
Во-первых, найти PTE для распределения в пользовательском режиме, а затем снимите U/S немного , чтобы заставить его быть установлен в положение S . Когда это происходит, ЦП будет рассматривать этот адрес пользовательского режима как страницу режима ядра, позволяя выполнить выполнение.
Рисунок 11: Использование произвольной уязвимости записи для сброса U/S бита, в результате чего страница пользовательского режима становится страницей режима ядра
Более старый метод обхода SMEP заключается в его отключении в масштабе всей системы за счет использования ROP. Злоумышленник может использовать гаджет ROP в режиме ядра, найдя тот, который позволяет переопределить значение регистра CR4 на значение с очищенным 20-м битом, что включает SMEP, обратно в регистр CR4.
Обратной стороной этого метода является то, что вы должны использовать гаджеты ROP в режиме ядра, чтобы поддерживать выполнение в ядре, чтобы соответствовать правилам SMEP. Кроме того, как и во всех атаках с повторным использованием кода, смещение между гаджетами может меняться в зависимости от версии Windows, и для работы ROP необходимо управление стеком . Защита под названием HyperGuard, которая выходит за рамки этоой статьи, также защищает от модификации CR4 в современных системах.
Рисунок 12: Полное отключение SMEP в масштабе всей системы с помощью манипуляции с CR4
kd> bp nt! часть_2
В этом статье были пересмотрены устаревшие средства защиты, а также современные средства защиты, такие как CFG и SMEP, которые направлены на то, чтобы бросить вызов исследователям уязвимостей и поднять планку и качество эксплойтов. Эти темы создают основу для более современных средств защиты, таких как ACG, XFG, CET и VBS, которые добавляют сложности - увеличивают воздействие эксплуатации и побуждают читателей стать более любознательными в отношении окупаемости инвестиций в разработку современных эксплойтов. В следующей части мы обсуждим более современные способы эксплуатации и множество средств защиты, разработанных Microsoft для борьбы с ними.
Источник: https://www.crowdstrike.com/blog/state-of-exploit-development-part-1/