• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Текущее состояние кодинга сплойтов

weaver

31 c0 bb ea 1b e6 77 66 b8 88 13 50 ff d3
Забанен
Регистрация
19.12.2018
Сообщения
3 301
Решения
11
Реакции
4 622
Депозит
0.0001
Пожалуйста, обратите внимание, что пользователь заблокирован
Часть первая

1598022526526.png



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

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

Эта статья состоит из двух частей, и она посвящена эволюции разработки эксплойтов и исследованию уязвимостей в системах Windows. Будут рассматриваться такие вопросы, как «Как это повлияет на картину будущих нарушений?» и «Стоит ли по-прежнему платить за разработку надежного, портативного и эффективного бинарного эксплойта?»

Как ты сюда попал?
С самого начала вычисления вызывали любопытство, что в конечном итоге привело к обнаружению «компьютерной ошибки» или непреднамеренного поведения систем в результате взаимодействия с пользователем. Это в свою очередь привело к использованию этих ошибок злоумышленниками со злым умыслом и положило начало эре бинарной эксплуатации. С тех пор ни исследователи безопасности, ни Red Team, ни противники ни разу не оглядывались назад.

Начало бинарной эксплуатации привело к тому, что производители, в первую очередь Microsoft и Apple (с особым упоминанием grsecurity для Linux, возглавившая обязанности более двух десятилетий назад), препятствовали этим эксплойтам с помощью различных средств защиты. Эти средства защиты от эксплуатации, многие из которых включены по умолчанию, снизили воздействие современных средств эксплуатации.

Подобно массовому использованию Active Directory в корпоративных средах, которое заставило исследователей Red Team уделять большое внимание продуктам Microsoft, злоумышленники и исследователи сделали Windows центром внимания из-за ее широкого использования как в корпоративных, так и в не корпоративных средах. В результате эта статья будет ориентирована на Windows и будет сосредоточена как на пользовательском режиме, так и на смягчении последствий в режиме ядра.

Классы уязвимости: тогда и сейчас
Когда дело доходит до бинарной эксплуатации, исследователям и противникам всегда приходилось отвечать на извечный вопрос: «Как можно выполнить код на цели без какого-либо взаимодействия с пользователем?» Ответ пришел в виде различных классов уязвимостей. Хотя это не исчерпывающий список, некоторые общие уязвимости включают:
  1. Классическое переполнение стека (да, даже в 2020 году): это возможность перезаписать существующий контент в стеке и использовать контролируемую запись для определения местоположения и повреждения адреса возврата функции для перехода в произвольное место.
  2. Использование памяти после освобождения: объект размещается в памяти в куче в пользовательском режиме (или в памяти пула режима ядра). Когда происходит преждевременное «освобождение» этого объекта, ссылка / дескриптор этого освобожденного объекта остается. Используя другой примитив, новый объект создается на месте освобожденного объекта, и ссылка на старый объект используется для выполнения или иного изменения нового объекта, который действует вместо старого объекта. Ожидается, что эти неожиданные изменения в новом объекте каким-то образом приведут к повышению привилегий или другой вредоносной возможности.
  3. Произвольная запись: это возможность произвольно записывать данные, такие как один или несколько указателей, в произвольное место. Это также могло быть результатом другого класса уязвимостей. Примитивы произвольной записи также могут использоваться, как примитивы произвольного чтения в зависимости от точности, имеющейся над примитивом записи.
  4. Путаница типов данных: объект относится к одному типу, но позже на этот тип ссылаются как на другой тип. Из-за расположения в памяти различных типов данных это может привести к неожиданному поведению.
Мэтт Миллер из Microsoft выступил с докладом на BlueHat IL в 2019 году, в котором рассказал об основных классах уязвимостей с 2016 года: чтение за пределами диапазона, использование памяти после освобождения, путаница с типами данных и неинициализированное использование. Эти классы ошибок использовались и используются злоумышленниками и исследователями безопасности по сей день.

Также стоит отметить, что каждый из этих классов уязвимостей может находиться в пользовательском режиме, режиме ядра, а в наши дни и в гипервизоре. Исторически уязвимости пользовательского режима эксплуатировались удаленно или через обычные настольные приложения, такие как браузеры, офисные пакеты для повышения производительности и программы чтения 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.

1598028097700.png

Рисунок 1: Стек пользовательского режима имеет разрешения только на чтение\запись с включенным DEP

1598028148274.png

Рисунок 2: При включенном DEP адрес пользовательского режима 00007ffe`54a40000 не имеет установленного управляющего бита исполняемого PTE.


До того, как DEP в режиме ядра был расширен для покрытия резидентной кучи ядра в операционных системах Windows, PTE для таких распределений были помечены как RWX, а тип памяти NonPagedPool режима ядра был исполняемым и доступным для записи. Резидентная память относится к тому факту, что память, принадлежащая этому типу распределения, никогда не будет «выгружаться» из памяти, что означает, что этот тип виртуальной памяти всегда будет отображаться на действительный физический адрес.

С выпуском Windows 8 NonPagedPoolNx пул стал кучей режима ядра по умолчанию для распределения резидентной памяти. Это захватывает все свойства NonPagedPool, но делает его неисполняемым. Так же, как и для адресов пользовательского режима, исполняемый бит обеспечивается записью в таблице страниц виртуального адреса режима ядра.

1598029346986.png

Рисунок 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.

1598032202440.png

Рисунок 4: Базовый адрес ntoskrnl.exe до перезагрузки

После перезагрузки ядро загружается по другому виртуальному адресуfffff800`07000000.

1598032255749.png

Рисунок 5: Базовый адрес ntoskrnl.exe после перезагрузки

Исторически до реализации ASLR победить DEP было столь же тривиально, как дизассемблирование приложения или DLL сырых ассемблерных инструкций и использование указателей на эти инструкции, которые были статическими до ASLR, для обхода DEP. Однако при реализации ASLR обычно требуется одно из трех действий:
  1. Используйте библиотеки DLL и приложения, которые не скомпилированы с ASLR.
  2. Использовать уязвимость за пределами границ или какой-либо другой тип утечки информации / памяти
  3. Брутфорс адресного пространства (невозможно в 64-битных системах)
В современной среде эксплуатации уязвимости утечки информации являются стандартом для обхода ASLR. В зависимости от различных обстоятельств утечка информации может быть классифицирована как еще одна уязвимость нулевого дня в дополнение к примитиву повреждения памяти. Это означает, что для современных эксплойтов может потребоваться две уязвимости нулевого дня.

Поскольку Windows выполняет ASLR только для каждой загрузки, все процессы используют одну и ту же структуру адресного пространства после запуска системы. Следовательно, ASLR неэффективен против локального злоумышленника, который уже выполнил код. Точно так же, поскольку ядро предоставляет непривилегированным пользователям API-интерфейсы самоанализа, которые предоставляют адреса памяти ядра, KASLR также не эффективен против этого класса атак. По этой причине ASLR и KASLR в Windows являются только эффективными средствами защиты от векторов удаленной эксплуатации.

Однако с появлением локального удаленного доступа было признано, что KASLR был неэффективен против удаленных злоумышленников, которые сначала достигли пользовательского RCE, потому что, как уже упоминалось, определенные функции Windows API, такие как EnumDeviceDrivers() или NtQuerySystemInformation(), могут использоваться для перечисления базовых адресов всех загруженных модулей ядра.

Поскольку локальный удаленный злоумышленник сначала начал бы с RCE пользовательского режима, нацеленного на браузер и т. д., Microsoft начала усиленно обеспечивать выполнение таких приложений в изолированной среде и ввела обязательный контроль целостности (MIC), а затем AppContainer, как способ снизить привилегии этих приложений, в том числе благодаря их запуску с низким уровнем целостности. Затем в Windows 8.1 был заблокировал доступ к таким интроспективным функциям API для процессов среднего уровня целостности и выше.

Следовательно, процесс с низким уровнем целостности, такой как песочница браузера, потребует уязвимости утечки информации, чтобы обойти KASLR.

1598033648939.png

Рисунок 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 в других случаях).

1598034289495.png

Рисунок 7: Реализация CFG в пользовательском режиме


Выполняется серия побитовых операций и ассемблерных функций, что приводит к проверке растрового изображения, чтобы определить, является ли функция в косвенном вызове функции допустимой функцией внутри растрового изображения. Неверная функция приведет к завершению процесса.

kCFG имеет очень похожую реализацию, в которой косвенные вызовы функций проверяются kCFG. В частности, это «ломает» [nt!HalDispatchTable+0x8] примитив, который злоумышленники и исследователи использовали для выполнения кода в контексте ядра путем вызова nt!KeQueryIntervalProfile, который выполняет косвенный вызов функции в [nt!HalDispatchTable+0x8] 64-битных системах.

1598034676578.png

Рисунок 8: [nt!HalDispatchTable+0x8] теперь охраняется kCFG через nt!KeQueryIntervalProfile косвенный вызов

kCFG использует немного другую модификацию CFG, в которой битовая карта хранится внутри переменной nt!guard_icall_bitmap. Кроме того, nt!_guard_dispatch_icall запускает процедуру проверки цели, и никаких других вызовов функций не требуется.

1598034820209.png

Рисунок 9: Реализация kCFG


CFG смягчает тот факт, что в какой-то момент жизненного цикла разработки эксплойта может потребоваться перезапись указателя функции, чтобы он указывал на другой указатель на функцию, который может быть полезен противнику (например, VirtualProtect ).

CFG - это первое преимущество по снижения CFI. Это означает, что он не принимает во внимание инструкции ret , что является случаем обратного преимущества. Поскольку CFG не проверяет адреса возврата, CFG можно обойти, используя утечку информации, что может позволить действие, такое как синтаксический анализ блока среды потока (TEB), для утечки стека. Используя это знание, можно будет перезаписать адрес возврата функции в стеке со злым умыслом.

Со временем было обнаружено, что CFG имеет несколько недостатков. Например, обратите внимание, что модули используют таблицу адресов импорта (IAT) для импорта, например, функции Windows API. Эти таблицы IAT по сути являются виртуальными адресами в определенном модуле, которые указывают на функции Windows API.

1598035489298.png

Рисунок 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 . Когда это происходит, ЦП будет рассматривать этот адрес пользовательского режима как страницу режима ядра, позволяя выполнить выполнение.

1598036028512.png

Рисунок 11: Использование произвольной уязвимости записи для сброса U/S бита, в результате чего страница пользовательского режима становится страницей режима ядра

Более старый метод обхода SMEP заключается в его отключении в масштабе всей системы за счет использования ROP. Злоумышленник может использовать гаджет ROP в режиме ядра, найдя тот, который позволяет переопределить значение регистра CR4 на значение с очищенным 20-м битом, что включает SMEP, обратно в регистр CR4.

Обратной стороной этого метода является то, что вы должны использовать гаджеты ROP в режиме ядра, чтобы поддерживать выполнение в ядре, чтобы соответствовать правилам SMEP. Кроме того, как и во всех атаках с повторным использованием кода, смещение между гаджетами может меняться в зависимости от версии Windows, и для работы ROP необходимо управление стеком . Защита под названием HyperGuard, которая выходит за рамки этоой статьи, также защищает от модификации CR4 в современных системах.

1598036102449.png

Рисунок 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/
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Часть вторая

1598078350952.png



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

Современное решение №1: Рандомизация таблицы страниц
Как объяснялось в части 1, записи в таблице страниц (или PTE) очень важны, когда речь идет о современной эксплуатации. Вы можете вспомнить, что PTE несут ответственность за обеспечение различных разрешений и свойств памяти. Исторически вычисление PTE для виртуального адреса было тривиальным делом, так как база PTE в течение некоторого времени была статичной. Процесс получения PTE для виртуального адреса:
  1. Преобразуйте виртуальный адрес в виртуальный номер страницы (VPN), разделив его на размер страницы (обычно 4 КБ)
  2. Умножьте VPN на размер PTE (8 байтов в 64-битных системах)
  3. Добавить базу PTE к результату предыдущей операции
В терминологии программирования это по сути означает ссылку на массив по индексу, например PteBase[VirtualPageNumber].

В предыдущих версиях Windows база PTE располагалась по статическому виртуальному адресу fffff680`00000000. Однако после Windows 10 1607 (RS1) база PTE была рандомизирована, а это значит, что теперь этот процесс не так тривиален.

Один из способов вернуть «тривиальный» метод вычисления PTE для данного виртуального адреса - дерандомизировать базу PTE. Windows API предоставляет функцию с именем nt!MiGetPteAddress, которая использовалась в предыдущем исследовании эксплуатации, проведенном Мортеном Шенком в его выступлении на BlackHat в 2017 году.

Эта функция выполняет ту же процедуру, которая описана выше, для доступа к PTE виртуального адреса. Однако он динамически заполняет основу PTE со смещением 0x13 внутри функции.

1598078467520.png

Рисунок 1. Дерандомизация таблицы страниц с помощью nt!MiGetPteAddress+0x13


Используя произвольный примитив чтения, можно извлечь основу записей таблицы страниц, используя этот метод. При наличии базы PTE вышеупомянутый тривиальный вычислительный примитив остается в силе.

Обратите внимание, что Windows 10 1607 (RS1) рандомизировала не только базовый адрес PTE, но и базовый адрес 14 других областей памяти ядра. Хотя база PTE была наиболее значительным изменением, эти другие рандомизации также помогли обуздать определенные виды эксплойтов ядра, которые выходят за рамки этой публикации.

Современное смягчение # 2: ACG
Arbitrary Code Guard (ACG), представленная в Windows 10, - это дополнительное средство защиты от повреждения памяти, предназначенное для остановки выполнения произвольного кода. Хотя ACG был разработан с учетом Microsoft Edge, его можно применить к большинству процессов.

ROP, хорошо документированный метод обхода DEP, чаще всего используется для возврата к функциям Windows API, таким как VirtualProtect(). Используя эту функцию и предоставленные пользователем аргументы, злоумышленники и исследователи могут динамически изменять права доступа к памяти, в которой находится вредоносный шелл-код, на RWX. С ACG это невозможно.

1598078645664.png

Рисунок 2: Процесс, защищенный ACG


ACG предотвращает изменение существующего кода, например вредоносного шеллкода, ожидающего преобразования в RWX. Если у индивидуума есть примитивы чтения и записи и он обошел CFG и ASLR, ACG снижает возможность использования ROP для обхода DEP посредством динамического управления разрешениями памяти.

Кроме того, ACG предотвращает возможность выделения новой исполняемой памяти. VirtualAlloc(), еще один популярный API для ROP, не может выделять исполняемую память для злонамеренных целей. По сути, память не может быть изменена динамически PAGE_EXECUTE_READWRITE.

ACG, хотя и является смягчением в пользовательском режиме, реализован он в ядре с помощью функции Windows API, называемой nt!MiArbitraryCodeBlocked. Эта функция по существу проверяет процесс, чтобы увидеть, включен ли ACG.

1598078682634.png

Рисунок 3: nt!MiArbitraryCodeBlocked процессы проверки для смягчения последствий ACG

Объект EPROCESS для процесса, который является представлением процесса в ядре, имеет член типа данных union, известный как MitigationFlags, который отслеживает различные меры защиты, включенные для процесса. EPROCESS также содержит другой член, известный как MitigationFlagsValues, который предоставляет удобочитаемый вариант MitigationFlags.

Давайте рассмотрим процесс содержимого Edge ( MicrosoftEdgeCP.exe), где включен ACG.

1598078717959.png

Рисунок 4: MicrosoftEdgeCP.exe

Обращаясь к EPROCESS члену MitigationFlagsValues, мы видим, что DisableDynamicCode, который является ACG, установлен в 0x1, что означает, что ACG включен для этого процесса.

1598078757342.png

Рисунок 5: DisableDynamicCode устанавливается в процессе содержимого Edge

На этом этапе, если для процесса создается динамически созданный исполняемый код и этот флаг установлен, из проверки функции возвращается ошибка STATUS_DYNAMIC_CODE_BLOCKED, что приводит к сбою.

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

1598078782478.png


Рисунок 6: Список процессов с включенной ACG

Хотя обходов для ACG не так много, логика побудила исследователей и противников атаковать JIT-компиляторы (just-in-time). JavaScript - это интерпретируемый язык, то есть он не компилируется в прямой машинный код. Вместо этого JavaScript использует «байт-код». Однако в некоторых случаях JIT-компиляторы используются браузерами для динамической компиляции байт-кода JavaScript в реальный машинный код для повышения производительности. Это означает, что по замыслу JIT-компиляторы всегда создают динамически исполняемый код. Из-за этой функциональности ACG несовместима с JIT и имела ограниченную мощность внутри Edge до Windows 10 1703 (RS2).

Алекс Ионеску объяснил в своем выступлении на Ekoparty, что до обновления 1703 (RS2) у Edge был один поток, отвечающий за JIT из-за ACG. Поскольку JIT несовместим с ACG, этот «поток JIT» не включал ACG - это означает, что, если компрометация этого потока была возможна, тогда можно было бы обойти ACG. Чтобы решить эту проблему, Microsoft создала отдельный процесс для компиляции Edge JIT полностью в Windows 1703 (RS2). Чтобы процесс Edge Content (процесс без JIT) использовал JIT-компиляцию, процесс JIT использует дескриптор процесса Edge Content, чтобы выполнять JIT-работу внутри каждого процесса, отличного от JIT.

У ACG есть «универсальный обходной путь», заключающийся в том, что исследователи и злоумышленники могут полностью избегать выполнения кода. Используя методы повторного использования кода, можно записать всю полезную нагрузку в ROP, JOP или COP, которые будут «придерживаться» правил ACG. Вместо использования методов повторного использования кода для возврата в API можно было бы просто использовать его для создания всей полезной нагрузки. Кроме того, скомпрометированные браузеры должны будут использовать выход из песочницы с полным повторным использованием кода. Это не идеально, так как написание полезных данных в ROP, JOP или COP занимает очень много времени.

ACG также был обойден с использованием JIT-структуры Edge. Иван Фратич из Google Project Zero выступил с докладом на Infiltrate 2018, объяснив, что способ получения обработчиков контента в Edge процессами JIT рискован.

Процесс Edge Content использует функцию Windows API DuplicateHandle() для создания дескриптора самого себя, который может использовать процесс JIT. Проблема в том, что для DuplicateHandle() функции требуется уже установленный дескриптор целевого процесса с PROCESS_DUP_HANDLE разрешениями. Процессы Content Edge используют эти разрешения для получения дескриптора JIT-процесса с большим объемом доступа, что PROCESS_DUP_HANDLE позволяет процессу с дескриптором другого процесса дублировать псевдодескриптор (например, -1), имеющий максимальный доступ. Это позволит получить доступ к процессу JIT из процесса Content Edge, где ACG отключен. Это может привести к компрометации системы из-за использования Content Process, чтобы затем перейти к процессу JIT, не защищенному ACG, для использования.

В конечном итоге эти проблемы были исправлены в Windows 10 RS4, и, очевидно, Edge теперь использует Chromium Engine, который также использует ACG и JIT-компилятор вне процесса.

Современное смягчение последствий № 3: CET
Из-за того, что CFG не принимает во внимание случаи возврата, Microsoft потребовалось быстро разработать решение для защиты обратных адресов. Как упоминал Джо Биалек из Microsoft Security Response Center в своем выступлении на OffensiveCon 2018 , Microsoft изначально решила эту проблему с помощью программной защиты, известной как RFG или Return Flow Guard.

RFG стремился решить эту проблему путем использования дополнительного кода в прологах функций, чтобы поместить адрес возврата функции в нечто, известное как «теневой стек», который содержит только копии законных указателей возврата для функций и не содержит никаких параметров. Этот теневой стек был недоступен из пользовательского режима и, следовательно, «защищен ядром». В эпилоге функции копия адреса возврата из теневого стека сравнивалась с адресом возврата в области видимости. Если бы они были другими, произошла бы авария. RFG, хотя и неплохая концепция, в конечном итоге потерпел поражение от внутренней Red Team Microsoft, которая нашла универсальный обходной путь, который сводился к реализации любого решение с теневым стеком реализовано программно. Из-за ограничений любой программной реализации перехвата управляющего потока требовалось аппаратное решение.

Используйте Intel CET или технологию Control-Flow Enforcement. CET - это аппаратное смягчение последствий, которое реализует теневой стек для защиты адресов возврата в стеке, а также для случаев прямого перехода, таких как calls/jumps через косвенное отслеживание переходов (IBT). Однако, по словам Алекса Ионеску и Ярдена Шафира , Microsoft решила использовать CFG (и XFG, на которые будет ссылаться далее в этом посте) для защиты передовых граничных случаев вместо возможностей CET IBT, которые работают аналогично реализации CFI Clang .

Главный тезис CET - это защита обратных адресов, по сути предотвращающая ROP. CET имеет аналогичный подход к RFG в том, что используется теневой стек.

Когда CET определяет, что целевой адрес возврата не совпадает с соответствующим сохраненным адресом возврата в теневом стеке, генерируется ошибка.

1598078817951.png

Рисунок 7: Взгляд на «псевдо» проверку обратного адреса через CET


Хотя CET, который является частью семейства процессоров Intel Tiger Lake, не поразил массовое потребительское оборудование, некоторые возможные обходные пути были концептуализированы.

Современное смягчение # 4: XFG
Xtended Control Flow Guard, известный как XFG, представляет собой «улучшенную» реализацию CFG от Microsoft. По дизайну CFG проверяет только наличие функций в битовой карте CFG - это означает, что технически, если указатель функции был перезаписан другой функцией, которая существовала в битовой карте CFG, это было бы допустимой целью. На рисунке 8 ниже показано [nt!HalDispatchTable+0x8], что обычно указывает на hal!HaliQuerySystemInformation, был перезаписан с помощью nt!RtlGetVersion.

1598078844904.png

Рисунок 8: [nt!HalDispatchTable+0x8] был перезаписан nt!RtlGetVersion

Непосредственно перед выполнением растровое изображение kCFG принимает значение RAX, которое будет nt!RtlGetVersion вместо [nt! HalDispatchTable + 0x8], чтобы определить, действительна функция или нет.

1598078870875.png

Рисунок 9: Указатель на nt!HalDispatchTable+0x8 загружается в RAX при подготовке к вызову nt!guard_dispatch_icall

1598078891970.png

Рисунок 10: Фактическое значение RAX nt!RtlGetVersion, а не предполагаемое значение hal!HaliQuerySystemInformation

Поразрядные проверки происходят, и вызов функции по-прежнему разрешен, даже если [nt!HalDispatchTable + 0x8] был перезаписан другой функцией.

1598078914723.png


Рисунок 11: Возникает jmp в RAX, который содержит сохраненное значение nt!RtlGetVersion

1598078939280.png


Рисунок 12: Вызов nt!RtlGetVersion

Хотя CFG действительно препятствует некоторым косвенным вызовам перезаписываемых функций, с помощью созданных вызовов функций по-прежнему можно выполнять вызовы со злонамеренными намерениями.


XFG устраняет этот недостаток надежности, как отметил Дэвид Уэстон из Microsoft. В своем выступлении на BlueHat Shanghai 2019 Дэвид объясняет, что XFG реализует «хеш-функцию на основе типа» защищенной функции, которая размещается на 0x8 байтов над вызовом одной из функций диспетчеризации XFG.

XFG по существу берет прототип функции функции, состоящий из возвращаемого значения и аргументов функции, и создает ~ 55-битный хеш прототипа. Когда вызывается функция диспетчеризации, хэш функции помещается на 8 байтов выше самой функции. Этот хэш будет использоваться как дополнительная проверка перед передачей потока управления.

1598078966968.png

Рисунок 13: Хэш XFG загружается R10 перед передачей потока управления в диспетчерскую функцию XFG

Если хэш функции XFG, которые генерируется компилятором, не является полными, хеши могут быть не уникальными. Это означает, что если последовательность байтов, составляющая хэш, не уникальна, коды операций, которые находятся в 8 байтах под хешем, могут содержать те же байты, например, при вызове в середине функции. Хотя это маловероятно, это может привести к тому, что XFG объявит перезаписанную функцию «действительной», потому что сравнение между хешем и функцией при дизассемблировании на коды операций может быть истинным, что приведет к обходу XFG. Однако команда компилятора специально реализовала код, чтобы этого не произошло. Точно так же, поскольку хеширование для функций Cи использует примитивные типы, такие как void *, функции потенциально могут быть перезаписаны функциями, имеющими идентичные/похожие прототипы.

Современное смягчение последствий # 5: VBS и HVCI
Чтобы обеспечить дополнительные границы безопасности для ОС Windows, Microsoft решила использовать существующие возможности виртуализации современного оборудования. Среди этих средств защиты - целостность кода, защищенная гипервизором (HVCI), и безопасность на основе виртуализации (VBS).

VBS отвечает за включение HVCI и включен по умолчанию на совместимом оборудовании после Windows 10 1903 (19H1) в системах с защищенным ядром. Его также можно включить по умолчанию в системах Windows 10 2003 (20H1) для поставщиков, которые соглашаются через конфигурацию системы, и если оборудование достаточно современное, чтобы соответствовать базовому уровню Microsoft « Security Level 3 ». VBS стремится изолировать код режима пользователя и режима ядра, выполняя его поверх гипервизора Hyper-V.

На следующем изображении из Windows Internals, Part 1, 7th Edition (Ionescu, et al.) Показаны общие визуальные элементы реализации VBS.

1598078998131.png


Рисунок 14: Реализация VBS (внутреннее устройство Windows, часть 1, 7-е издание)


VTL, или виртуальные уровни доверия, предотвращают доступ процессов, выполняемых в одном VTL, к ресурсам другого VTL. Это связано с тем, что ресурсы, расположенные в обычном ядре, фактически управляются более «надежной» границей - VTL 1.

Одним из основных компонентов VBS, упомянутых в этой статье, является HVCI. HVCI - это, по сути, ACG в ядре. HVCI препятствует динамически создаваемому исполняемому коду в ядре. Кроме того, HVCI предотвращает выделение памяти пула ядра, которая является RWX , аналогично защите пользовательского режима ACG от страниц RWX через VirtualAlloc().

HVCI использует преобразование адресов второго уровня, известное как SLAT, для обеспечения соблюдения расширенных таблиц страниц или EPT, которые являются дополнительными неизменяемыми битами (в контексте VTL 0), которые устанавливают разрешения VTL 1 для страниц VTL 0. Это означает, что даже если злоумышленник или исследователь может манипулировать битом управления PTE в режиме ядра VTL 0, биты EPT VTL 1 по-прежнему не разрешают выполнение управляемых страниц в режиме ядра VTL 0.

Обход HVCI может включать в себя методы, аналогичные ACG в атаках только на данные. Воздержание от выполнения кода, но вместо этого использование методов повторного использования кода, которые не приводят к манипуляциям с PTE или другим запрещенным действиям, по-прежнему является жизнеспособным вариантом. Кроме того, если злоумышленник/исследователь может использовать уязвимость в гипервизоре или в защищенном ядре, которое работает в VTL1, это может быть возможно нарушить целостность VTL 1.

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

Многие злоумышленники обычно выбирают «путь наименьшего сопротивления», то есть отправляют вредоносный документ или вредоносный HTA в ничего не подозревающий список целевых пользователей. Как правило, этого достаточно для выполнения работы. Однако в противовес этому может быть что-то большее, чем эксплойт без взаимодействия с пользователем, без аутентификации и удаленного выполнения кода ядра в общей службе, такой как SMB, RDP или DNS? Использование методов социальной инженерии зависит от других неконтролируемых факторов, таких как осведомленные о безопасности конечные пользователи, которые получают такие фишинговые электронные письма. Бинарная эксплуатация исключает человеческий фактор из процесса выполнения кода, оставляя меньше забот.

Исследователь или злоумышленник могут потратить недели или месяцы на разработку надежного портативного эксплойта, который обойдет все существующие средства защиты. Эксплойт, такой как эксплойт браузера, может потребовать в пользовательском режиме произвольное чтение нулевого дня для обхода ASLR; произвольная запись нулевого дня для обхода DEP, CFG, ACG и других средств снижения риска; произвольное чтение ядра с нулевым днем для обхода случайной выборки kASLR / таблицы страниц от ограниченного вызывающего объекта для подготовки эксплойта ядра к выходу из изолированной программной среды браузера; и ядро произвольной записи нулевого дня для эксплойта ядра. Это всего четыре нулевых дня. Стоит ли окупаемость вложений? Это вопросы, которые должны принимать во внимание исследовательские фирмы и противники-нации.

Ссылки:

Источник: https://www.crowdstrike.com/blog/state-of-exploit-development-part-2/
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх