Введение
За последние несколько лет эксплуатация ядра Windows становилась все более сложной, особенно с выпуском системы Windows 10 и её последующими обновлениями ядра. Методы рождались и умирали в течение нескольких месяцев, новые быстро заменяли старые, которые не работали. Сами техники стали хрупкими, подходящими только к определенному шаблону или пространству.
Эксплуатация часто опиралась на получение какой-либо формы выполнения произвольного кода. Получение примитива для чтения/записи, стратегий для работы со стеком, позволяющих обойти защиту, и в конечном итоге получить выполнение привилегированного действия. Недавно мы наблюдали тенденцию к базовым логическим действиям, таким как кража токенов, включение битов режима бога, обнуление дескрипторов безопасности токена. (https://blogs.technet.microsoft.com/mmpc/2017/01/13/hardening-windows-10-with-zero-day-exploit-mitigations/) Эти действия делегируют кражу привилегий в пользовательском пространстве, освобождая разработку эксплоита от адовых ограничений ядра.
Шеллкод для кражи токенов, популярен среди многих разработчиков эксплоитов и авторов вредоносных программ на протяжении многих лет, и это не случайность. Это был чрезвычайно надежный метод, предлагающий стабильный, простой шеллкод и, до недавнего времени, приемлемое поведение. Microsoft реализовала эвристическое обнаружение в рамках платформы Advanced Threat Protection (https://blogs.technet.microsoft.com/mmpc/2016/11/01/our-commitment-to-our-customers-security/), но пока еще ничего не реализовала в самом ядре Windows. Для авторов вредоносных программ и скрипт-киддисов это может быть приемлемо, но для серьёзных противников или "Red Team" конечно нет.
Цель этой статьи состоит в том, чтобы обсудить один из таких логических приемов, которые мы усовершенствовали за последние несколько месяцев и применяем в действии. Хотя сами методы не новы (https://blogs.technet.microsoft.com/mmpc/2016/11/01/our-commitment-to-our-customers-security/), мы надеемся представить новые подходы и идеи, которые могут помочь в дальнейшем совершенствовании этого метода и других.
В дополнение к эксплуатации ядра, злоупотребления привилегиями токена могут быть использованы другими, менее экзотическими способами. В ситуациях, когда служебная учетная запись скомпрометирована, у которой включены нестандартные привилегии, их часто можно использовать для повышения привилегий (EoP - elevation of privilege). Методы для этого специфичны для каждой привилегии, часто недокументированны и во многих случаях нетривиальны. В разделе 3.3 этого документа мы показываем, сколько из этих привилегий может быть использовано для EoP в обычных тестах на проникновение и в сценариях "Red Team".
Мы стремимся объединить разрозненные источники и предоставить справочную информацию для будущей работы. Мы признаем время и усилия других исследователей в том же области работы и надеемся дать что-то значимое сообществу в целом.
1 - Обзор Токена
Основа нашей стратегии опять-таки основана на самой модели доступа к объектам в Windows. Windows использует объекты токенов для описания контекста безопасности определенного потока или процесса. Эти объекты токенов, представленные структурой nt!_TOKEN, которая содержит обширную информацию о безопасности и справочную информацию, включая уровень целостности, привилегии, группы и многое другое. Наше внимание сосредоточено на привилегиях, содержащихся в этих токенах.
1.1 - Модель Привилегий Windows
Мы кратко опишем модель привилегий Windows, поскольку она относится к токенам процессов и потоков. Если вам нужно подробное объяснение, авторы рекомендуют прочитать книгу «Внутреннее устройство Windows» или провести некоторое время в отладчике Windbg.
Каждый процесс в системе содержит ссылку на объект токена в своей структуре EPROCESS, которая используется во время согласования доступа к объекту или привилегированных системных задач. Этот токен предоставляется через службу LSASS во время процесса входа в систему, и, таким образом, изначально все процессы в сеансе выполняются под одним и тем же токеном.
Процесс содержит основной токен, и потоки, выполняющиеся внутри процесса, наследуют этот же токен. Когда потоку необходим доступ к объекту с использованием другого набора учетных данных, он может использовать токен исполнения роли(impersonation token). Использование токена исполнения роли не влияет на основной токен или другие потоки, а только на выполнение в контексте потока исполнения роли. Эти токены исполнения роли могут быть получены с помощью ряда различных API, предоставляемых ядром.
Токен служит билетом доступа к процессам, который должен быть предоставлен различным компонентам (gatekeepers) в Windows; токен предоставляется через функцию SeAccessCheck при доступе к объекту и функцию SeSinglePrivilegeCheck во время привилегированных операций. Например, когда процесс запрашивает доступ для записи в файл, функция SeAccessCheck оценивает уровень целостности токенов, а затем оценивает его Дискреционный Список Контроля Доступа (DACL). Когда процесс пытается завершить работу системы через функцию NtShutdownSystem, ядро проверяет, активирован ли токен у запрашивающего процесса SeShutdownPrivilege или нет.
1.2 - Структура Токенов и Привилегии
Как уже упоминалось, структура _TOKEN в основном содержит контекстную информацию о безопасности процесса или потока. Соответствующая запись для наших целей это _SEP_TOKEN_PRIVILEGES, расположенная по смещению 0x40, содержащая информацию о привилегиях токена:
Запись Present представляет переменную типа long long без знака, содержащую текущие привилегии в токене. Это не означает, что они включены или отключены, но только то, что они существуют на токене. После создания токена, вы не можете добавить к нему привилегии; Вы можете только включить или отключить существующие, найденные в этом поле. Второе поле, Enabled, представляет переменную типа long long без знака, содержащую все включенные привилегии на токене. В этой битовой маске должны быть включены привилегии для передачи функции SeSinglePrivilegeCheck. Последнее поле, EnabledByDefault, представляет начальное состояние токена на момент создания.
Привилегии могут быть включены или отключены путем изменения определенных битов в этих полях. Например, чтобы задействовать SeCreateTokenPrivilege, нужно просто выполнить: _SEP_TOKEN_PRIVILEGES + 0x44 | = 1 << 0x000000002. Отключение привилегии будет обратным: _SEP_TOKEN_PRIVILEGES + 0x44 & = ~ (1 << 0x000000002). Вспомогательный скрипт на Pykd можно найти здесь (https://github.com/hatRiot/token-priv).
До недавнего времени, нужно было только устанавливать биты в поле "Enabled", чтобы фактически переключать привилегии в токене. Это означает, что для включения привилегий достаточно одной записи - частичной или другой. Однако с выпуском Windows 10 v1607 ядро теперь проверяет, что разрешающие биты также отображаются в поле Present (http://www.anti-reversing.com/2251/).
Хотя на первый взгляд модель безопасности токенов для определения конкретных привилегий для различных задач, по-видимому, позволяет реализовать детализированные средства управления доступом, специфичные для службы, при более внимательном рассмотрении выявляется более сложная ситуация. Многие из привилегий, если они включены, позволяют пользователю выполнять привилегированные действия, которые могут привести к повышению привилегий. Это эффективно разрушает "мелкозернистую" структуру управления доступом и может дать ложное чувство безопасности.
1.3 — Токен Исполнения Роли
Прежде чем углубляться в конкретные привилегии, будет полезно описать механизм Windows для определения, может ли конкретный поток использовать данный токен. Любой пользователь может получить дескриптор привилегированного токена, но возможность использовать его - другое дело.
В Windows "Токен Исполнения Роли" - это когда новый токен назначается потоку, который отличается от токена родительского процесса. Хотя слова Исполнения Роли подразумевают, что один пользователь использует токен, принадлежащий другому пользователю, это не всегда так. Пользователь может выдавать себя за принадлежащий ему токен, но просто имеет другой набор привилегий или некоторые другие модификации.
Одним из полей, указанных в каждом токене, является уровень исполнения роли токенов. Это поле определяет, можно ли использовать этот токен в целях исполнения роли и в какой степени. Существует четыре уровня исполнения роли:
SecurityAnonymous - Сервер не может идентифицировать клиента.
SecurityIdentification - Сервер может получить идентификационные данные и привилегии клиента, но не может выдать себя за клиента.
SecurityImpersonation - Сервер может исполнить роль контекста безопасности клиента в локальной системе.
SecurityDelegation - Сервер может исполнить роль контекст безопасности клиента в удаленных системах.
SecurityImpersonation and SecurityDelegation это самые интересные случаи для нас. Идентификационные токены и ниже не могут быть использованы для запуска кода.
Разрешено ли данному пользователю выдавать себя за конкретный токен или нет, можно определить следующим образом:
ЕСЛИ уровень токенов МЕНЬШЕ Impersonate, ТО разрешить (такие токены называются уровнем "Идентификация" и не могут использоваться для привилегированных действий).
ЕСЛИ процесс имеет привилегию "Impersonate" ТОГДА разрешать.
ЕСЛИ уровень целостности процесса МЕНЬШЕ ИЛИ РАВЕН уровню целостности токена И пользователь процесса РАВЕН пользователю токена, ТОГДА разрешить ИНАЧЕ ограничить токен уровнем "Идентификация" (привилегированные действия невозможны).
2 - Современные средства безопасности и их методы
Windows 10 значительно повышает безопасность ядра Windows, как в плане общего уменьшения поверхности атаки, так и улучшения существующих средств защиты. Microsoft продолжает перебирать защитные механизмы: KASLR продолжает получать столь необходимые улучшения (все еще много утечек), усложнение часто эксплуатируемых структур для примитивов чтения/записи (tagWND) и улучшение защитного механизм против техники NULL SecurityDescriptor (https://labs.nettitude.com/blog/ana...n-in-the-latest-windows-10-v1607-build-14393/). На протяжении многих лет злоумышленники демонстрировали, что подавление отдельных стратегий порождает только новые, и цикл продолжается.
Чтобы обеспечить некоторый контекст по обсуждаемым темам, далее следует краткое описание современных методов защиты ядра и методов эксплуатации. Если у тебя есть вопросы, просто напиши Ionescu.
2.1 - Современные средства защиты ядра Windows
Windows 10 и последующие обновления (Anniversary/Creators) включили в себя ряд улучшений по защите от эксплойтов, подробно описанных на Blackhat 2016 (https://www.blackhat.com/docs/us-16/materials/us-16-Weston-Windows-10-Mitigation-Improvements.pdf). Мы рекомендуем ознакомиться со ссылками на слайды для получения дополнительной информации и статистики по общим вопросам связанных с безопасностью Windows. Мы сосредоточимся на мерах по безопасности, относящихся к данной теме.
Ядро ASLR улучшилось за счет включения ASLR в различных регионах и структурах ядра. Хотя это сильная стратегия зашиты для удаленных эксплойтов ядра, существует несколько открытых и закрытых стратегий для утечки адресов объектов в ядре (https://github.com/sam-b/windows_kernel_address_leaks), и, таким образом, это не представляет большой угрозы для описанной здесь техники. Основные структуры данных и области памяти, когда-то использовавшиеся для обходов KASLR, такие, как таблица диспетчеризации HAL, теперь полностью рандомизированы.
AppContainer, впервые представленный в Windows 8, предоставляет возможности песочницы для пользовательских приложений. Этот элемент управления был расширен в Windows 10 для включения фильтрации системных вызовов win32k, который ограничивает процесс от злоупотребления различными системными вызовами win32k. Эта защита в настоящее время только (официально) включено для браузера Edge, и, следовательно, она не очень интересна.
Распространенным примитивом чтения/записи в ядре является структура tagWND, которая при повреждении допускает произвольное чтение/запись через функции InternalGetWindowText/NtUserDefSetText. Было обнаружено, что эксплойты для MS15-061 и совсем недавно MS16-135 используют эту технику. Anniversary Update теперь предоставляет дополнительные проверки границ для этого конкретного объекта, делая эксплоит бесполезным. Хотя эта форма атаки является элементарной, очень раздражает необходимость находить другие примитивы чтения/записи, она может быть эффективной для взлома уже развернутых/разработанных цепочек.
Внедрение SMEP в Windows 8 означает, что мы больше не можем просить ядро выполнить шелл-код из пользовательского режима для нас. Многие обходы использовались на протяжении многих лет, некоторые все еще эффективны, но большинство нет. Уклонение от данной атаки обычно включает в себя отключение некоторого элемента безопасности или получения кода в ядре (через регионы RWX или Kernel ROP). Поскольку мы не стремимся выполнять какой-либо шеллкод в ядре или даже в пользовательском пространстве, эта защита не применяется.
Еще одна интересная защита - мифический Patchguard, ранее известный как Kernel Patch Protection, давно представленная ядру x64 NT. Если вы не знакомы с этой защитой, знайте, что это бугимен ядра. Защита отслеживает случайное подмножество проверок в случайные моменты времени по случайным причинам. Его инициализация и поведение во время выполнения обфусцированы. Его исходный код и поведение скрыты даже от внутренних разработчиков Windows. Skywing и Skape ранее проделали довольно много работы, реверся и документируя его части, и на них следует ссылаться для дальнейшего рассказа (http://uninformed.org/index.cgi?v=8&a=5&p=2)
Начиная с Windows 10, в Patchguard имеется 44 различных проверки (видимые через !analyse -show 109), и хотя авторы с подозрением относятся к полноте списков, защита токенов процесса не предусмотрена.
2.2 - Соответствующие Cтратегии Эксплуатации
Предыдущая, связанная работа, которая обеспечила влияние и косвенное руководство для стратегии этой статьи, представлена здесь. Эти сопутствующие методы кратко изложены, чтобы предоставить справочную информацию и воздать должное тем, кто был до нас.
Статья Cesar Cerrudos Easy Local Windows Kernel Exploration, опубликованная на Blackhat 2012 (https://media.blackhat.com/bh-us-12/Briefings/Cerrudo/BH_US_12_Cerrudo_Windows_Kernel_WP.pdf), представил три различных стратегии повышения привилегий и указала многим разработчикам эксплойтов на силу злоупотребления токенами процесса. Первый метод, продемонстрированный в статье, рассказывает подробно о стратегии NULL ACL, в которой произвольная запись может использоваться для NULL привилегированного объекта ACL. Это была и остается очень распространенной стратегией для эффективного перехода к более привилегированным процессам.
Вторая стратегия Cerrudos это наша версия с бомбардировкой, в которой произвольная запись может включить все привилегии в токене процесса. С этими привилегиями можно использовать атрибут SeDebugPrivilege и переходить к более привилегированному процессу, создавать токены с привилегиями SeCreateTokenPrivilege или загружать драйверы ядра с привилегиями SeLoadDriverPrivilege.
Третья и последняя стратегия Cerrudos это еще один метод, очень популярный в современных эксплойтах EoP, который предусматривает замену токена процесса на токен SYSTEM. Это было подробно описано с разных точек зрения в других местах и не будет повторяться здесь.
Yin Liang и Zhou Li из Tencent провели и опубликовали аналогичное исследование на Blackhat Europe 2016, в котором они продемонстрировали злоупотребление частичной записью с объектами Windows для получения примитивов для чтения/записи (https://www.blackhat.com/docs/eu-16/materials/eu-16-Liang-Attacking-Windows-By-Windows.pdf). Как и наша работа, они были сосредоточены на частично контролируемых, ограниченных ошибках записи, таких как MS16-135 и CVE-2016-0174, в которых можно контролировать инструкции OR или DEC. В этих случаях, особенно с участием системы win32k, наша стратегия устраняет необходимость в получении примитива.
Moritz Jodeit опубликовал отличную статью о CVE-2014-4113, в которой он изменил структуру _SEP_TOKEN_PRIVILEGES с помощью неконтролируемой записи, чтобы включить дополнительные привилегии токена (https://labs.bluefrostsecurity.de/publications/2016/01/07/exploiting-cve-2014-4113-on-windows-8.1/). Его метод представляет интерес, поскольку в то время он продемонстрировал современное уклонение от защиты без перезаписи указателя или выполнения шеллкода, с дополнительным преимуществом значительно упрощенного процесса эксплуатации.
3 - Злоупотребление привилегиями токена
3.1 - Эксплуатируемые привилегии
Для целей этой статьи, мы определим "эксплуатируемую" привилегию как любую привилегию токена, которую можно использовать отдельно для получения доступа уровня "NT AUTHORITY\SYSTEM» на целевой системе.
Как упоминалось в разделе 1.2, структура nt!_SEP_TOKEN_PRIVILEGES - это двоичное поле в токене, где каждый бит определяет, присутствует ли данная привилегия в токене. Точную структуру этой битовой маски, какие биты соответствуют каким привилегиям, можно найти в коде, выпущенном вместе с этим проектом, в частности в скрипте pykd — "tokenum.py".
В оставшейся части этого раздела рассматриваются детали каждой привилегии, которую мы смогли успешно использовать для получения повышенных привилегий. Образцы кода для использования каждой из этих привилегий включены в этот проект.
3.1.1 – Привилегия SeImpersonatePrivilege
SeImpersonatePrivilege описывается в MSDN как "User Right: Impersonate a client after authentication." Это привилегия, упомянутая в разделе 1.4 на втором этапе, при проверке того, может ли конкретный процесс выдавать себя за данный токен. Любой процесс, обладающий этой привилегией, может выдавать себя за любой токен, для которого он может получить дескриптор. Следует отметить, что эта привилегия не позволяет создавать новые токены.
Эта особая привилегия довольно интересна, поскольку она требуется для ряда общих учетных записей служб Windows, таких, как LocalService и для MSSQL и IIS. "Эксплойт" для этой привилегии также может привести к повышению привилегий, если какие-либо из этих учетных записей будут скомпрометированы. Это было предметом предыдущей работы авторов (https://foxglovesecurity.com/2016/0...e-escalation-from-service-accounts-to-system/).
В (https://bugs.chromium.org/p/project-zero/issues/detail?id=325) описан способ получения дескриптора токена для учетной записи "NT AUTHORITY\SYSTEM". В основном службе Windows (DCOM) передается специально созданный объект, который содержит ссылку на контролируемый злоумышленником TCP-слушатель на локальном компьютере. Когда Windows пытается разрешить эту ссылку, злоумышленник запрашивает NTLM аутентификацию, а затем передает аутентификацию NTLM, отправленную слушателю, для создания нового токена на локальном компьютере. Этот токен будет для пользователя "NT AUTHORITY\SYSTEM" и будет иметь все права администратора.
Любой пользователь может выполнить ранее описанную процедуру, чтобы получить дескриптор токена для пользователя "NT AUTHORITY\SYSTEM", однако для использования этого дескриптора требуется возможность исполнять роль; SeImpersonatePrivilege позволяет нам сделать это. Все, что требуется для порождения нового процесса с повышенным токеном, - это вызвать функцию CreateProcessWithTokenw, передав новый токен в качестве первого аргумента.
3.1.2 — Привилегия SeAssignPrimaryPrivilege
SeAssignPrimaryPrivilege очень похожа на ранее обсуждавшуюся SeImpersonatePrivilege. Необходимо только "назначить основной токен процесса". Наша стратегия заключается в создании нового процесса с использованием, более привилегированного токена.
Чтобы создать новый процесс с привилегированным токеном, нам сначала нужно получить дескриптор такого токена. Для этого мы следуем процедуре, описанной в (https://bugs.chromium.org/p/project-zero/issues/detail?id=325) и кратко изложенной в разделе 3.1.1.
Как следует из названия этой привилегии, она позволяет нам назначить основной токен новому или приостановленному процессу. Используя стратегию, описанную в 3.1.1, для получения токена, мы находимся с привилегированным токеном исполнения роли и, таким образом, сначала должны извлечь из него первичный токен. Это может быть выполнено через функцию DuplicateTokenEx:
DuplicateTokenEx(hClientToken, TOKEN_ALL_ACCESS, NULL, SecurityAnonymous, TokenPrimary, &hDupedToken);
С привилегированным первичным токеном у нас теперь есть несколько вариантов. К сожалению, мы не можем просто поменять токен нашего текущего запущенного процесса на привилегированный, так как изменение основных токенов в запущенных процессах не поддерживается. Это контролируется полем PrimaryTokenFrozen в структуре EPROCESS.
Самый простой вариант - вызвать функцию "CreateProcessAsUser", используя новый токен в качестве аргумента для создания нового процесса с высокими привилегиями.
В качестве альтернативы, мы можем запустить новый процесс в приостановленном состоянии и выполнить ту же операцию, что и выше. Когда создаются новые процессы с вызовом функции CreateProcess (..., CREATE_SUSPENDED, ...), значение для PrimaryTokenFrozen еще не установлено, что позволяет менять токен.
Следствием предыдущего пункта является то, что в некоторых сценариях частичной эксплуатации записи мы можем фактически заменить токен запущенного в данный момент процесса на привилегированный. Если мы можем заставить поле PrimaryTokenFrozen быть сброшенным посредством частичной записи, предполагая, что мы обладаем, или можем получить SeAssignPrimaryTokenPrivilege, мы можем затем вызвать функцию NtSetInformationProcess, чтобы заменить старый токен на новый. Поскольку PrimaryTokenFrozen является однобитовым полем, окружающие поля являются релевантными, потому что они, вероятно, будут уничтожены при любой частичной записи:
Если, например, у вас есть произвольный декремент, как в уязвимости MS15-061, вы можете сбросить бит TokenPrimaryFrozen и обменять токен процесса:
NtSetInformationProcess(hCurrentProcess,(PROCESS_INFORMATION_CLASS)0x09, &hElevatedPrimaryToken, 8);
3.1.3 — Привилегия SeTcbPrivilege
SeTcbPrivilege довольно интересна. MSDN описывает её как "Эта привилегия идентифицирует своего владельца как часть базы доверенных компьютеров. Некоторым доверенным защищенным подсистемам предоставляется эта привилегия". В дополнение к этому, в ряде книг, статей и сообщений на форумах описывается привилегия TCB как эквивалент полностью привилегированного доступа к машине. Однако, несмотря на все это, ни один из общедоступных ресурсов, кажется, не указывает, как SeTcbPrivilege может использоваться сам по себе для выполнения привилегированных операций.
Мы начали с анализа документации MSDN, пытаясь выяснить, какие вызовы Windows API были разрешены для учетных записей с помощью SeTcbPrivilge. В документации по
функции LsaLogonUser в качестве первого параметра мы находим следующее:
LsaHandle [in] это дескриптор, полученный из предыдущего вызова функции LsaRegisterLogonProcess. Вызывающая сторона должна иметь SeTcbPrivilege, только если выполняется одно или несколько из следующих условий:
+ Используется пакет Subauthentication.
+ KERB_S4U_LOGON используется, и вызывающая сторона запрашивает токен имперсонализации.
+ Параметр LocalGroups не равен NULL.
Если SeTcbPrivilege не требуется, вызовите функцию LsaConnectUntrusted для получения дескриптора.
Обычно вызов функции LsaLogonUser используется для аутентификации пользователя с использованием некоторой формы учетных данных, однако мы предполагаем, что нам неизвестно о целевой системе и ее пользователях. Кроме того, какой пользователь будет пытаться войти? Наконец, как мы будем исполнять роль полученного токен, поскольку у нас нет SeImpersonatePrivilege?
К счастью, James Forshaw включил очень полезное сообщение <140 символов:
"Вы можете использовать функцию LsaLogonUser, чтобы добавить группу администраторов к токену вашего собственного пользователя, а затем выдать себя за другого".
В Windows есть интересный тип входа, известный как вход в систему S4U (и упоминаемый выше как KERB_S4U_LOGON). Это эффективно описано в блоге MSDN (https://blogs.msdn.microsoft.com/winsdk/2015/08/28/logon-as-a-user-without-a-password/) следующим образом:
"В Windows можно войти в систему как пользователь другого домена без каких-либо учетных данных. Это известно как S4U или Сервис для входа пользователя. Это расширение Microsoft для Kerberos, представленное в Windows Server 2003."
Кажется, это соответствует тому, что мы пытаемся сделать идеально; используя тип входа S4U, мы можем получить токен для любого пользователя. Возвращаясь к документации по параметру LsaHandle выше, если у нас есть SeTcbPrivilege, очевидно, что полученный токен может быть токеном "имперсонализации", то есть мы можем назначить его потоку.
Снова обращаясь к параметру LsaHandle, последний пункт означает, что мы можем вызвать функцию LsaLogonUser с помощью SeTcbPrivilege и добавить произвольные группы к результирующему токену, возвращенному этим вызовом. Мы добавим SID группы "S-1-5-18" к токену, это SID для учетной записи Локальной Системы, и если мы используем токен, которым он принадлежит, мы получим полную привилегию в системе. Добавить системный SID довольно просто:
Единственная часть, оставшаяся в этой головоломке, - это то, как мы будем использовать полученный маркер имперсонализации, поскольку мы предполагаем, что у нас есть привилегия SeTcbPrivilege, но нет никаких других привилегий. Возвращаясь к разделу 1.4, посвященному правилам, связанным с токеном имперсонализаци, мы видим, что мы должны иметь возможность выдавать себя за токен без каких-либо специальных привилегий, если токен предназначен для нашего текущего пользователя, а уровень целостности токена равен “Medium”. Таким образом, используя токен, возвращенный функцией LsaLogonUser, мы просто устанавливаем уровень целостности “Medium”, а затем вызываем функцию SetThreadToken, чтобы заменить токен нашего текущего потока новым.
3.1.4 — Привилегия SeBackupPrivilege
Привилегия SeBackupPrivilege описывается в MSDN следующим образом:
«Требуется для выполнения операций резервного копирования. Эта привилегия заставляет систему предоставлять все права доступа для чтения любому файлу, независимо от списка управления доступом (ACL), указанного для файла. Любой запрос доступа, кроме чтения, все еще оценивается с помощью ACL. Эта привилегия требуется для функций RegSaveKey и RegSaveKeyExfunctions. Следующие права доступа предоставляются, если эта привилегия удерживается: -READ_CONTROL -ACCESS_SYSTEM_SECURITY -FILE_GENERIC_READ -FILE_TRAVERSE ”
Чтобы использовать эту привилегию для EoP, мы читаем хэши паролей учетных записей локальных Администраторов из реестра и затем передаем их локальной службе, из которой мы можем получить выполнение кода, самым популярным методом здесь будет просто передать хеш с помощью “psexec” или “wmiexec”.
К сожалению, для нас есть два предостережения в этом сценарии. Во-первых, многие организации за последние несколько лет начали отключать локальные учетные записи администраторов. Во-вторых, даже в тех случаях, когда некоторые учетные записи локальных администраторов остаются включенными, корпорация Майкрософт внесла изменения в Windows Vista и новее, когда только учетная запись RID 500 (локальный Администратор по умолчанию) может удаленно администрировать машину по умолчанию:
Когда пользователь, который является членом группы локальных администраторов на целевом удаленном компьютере, устанавливает удаленное административное соединение ... он не будет подключаться как настоящий администратор. У пользователя нет возможности повышения прав на удаленном компьютере, и он не может выполнять административные задачи.
Однако, как показывает наш опыт, в корпоративных средах все еще довольно часто включена учетная запись локального администратора RID 500.
3.1.5 — Привилегия SeRestorePrivilege
Привилегия восстановления описывается как "необходимая для выполнения операций восстановления" и заставляет систему предоставлять все права доступа для записи любому файлу в системе, независимо от файлов ACL. Кроме того, эта привилегия позволяет процессу хранения или потоку изменять владельца файла. Последствия получения этой привилегии должны быть очевидны.
Чтобы использовать эту привилегию, необходимо разрешить флаг FILE_FLAG_BACKUP_SEMANTICS для поддержки API. Это подсказывает ядру, что в запрашивающем процессе могут быть включены SeBackupPrivilege или SeRestorePrivilege, и проверять его перед short circuiting проверки DACL. Из краткого описания привилегии в MSDN могут заинтересоваться наблюдатели, которые также позволяют создавать или изменять разделы реестра.
Произвольные записи в HKLM открывают бесконечный потенциал для повышения привилегий. Мы решили использовать ключ Image File Execution Options, используемый для отладки программного обеспечения в системе. Когда запускается системный двоичный файл, если для него существует запись HKLM по адресу HKLM\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Image File Execution Options, и он содержит ключ отладчика, он выполнит заданную запись. Этот метод довольно распространен для уязвимостей, связанных с сохранением вредоносных программ и повышением привилегий (https://bugs.chromium.org/p/project-zero/issues/detail?id=872). Эксплуатация просто требует открытия реестра, указания флага резервного копирования и создания ключа:
В приведенном выше примере мы использовали wsqmcons, службу консолидатора, которая работает с правами SYSTEM и запускается обычным пользователем в системе. Помимо добавления записей реестра, можно также перетаскивать библиотеки DLL в системные папки для перехвата библиотек DLL, перезаписи критических системных ресурсов или изменения других служб.
3.1.6 — Привилегия SeCreateTokenPrivilege
Привилегия SeCreateTokenPrivilege позволяет пользователям создавать первичные токены с помощью функции ZwCreateToken. К сожалению, одно это право не позволяет пользователю использовать только что созданный токен. Поэтому наивные попытки создать и использовать токены для пользователей с высокими привилегиями, таких как "NT AUTHORITY\SYSTEM", не увенчаются успехом.
Напомним правила имперсонализации токенов из раздела 1.4. Пользователю разрешено выдавать себя за токен даже без SeImpersonatePrivilege, если токен предназначен для того же пользователя и уровень целостности меньше или равен текущему уровню целостности процесса.
Чтобы использовать SeCreateTokenPrivilege, нам нужно только создать новый токен имперсонализации, который соответствует запрашивающему токену с добавлением привилегированной SID группы. Теоретически это довольно просто сделать, но API для этих интерфейсов и функция ZwCreateToken не очень дружелюбны. Большинство полей мы можем запросить из нашего исполняющего токена через функцию GetTokenInformation, с несколькими исключениями.
Как уже упоминалось, мы хотим включить локальную группу администраторов на токене. Для этого мы создаем SID, используя RID группы:
Затем мы перебираем группы токена и повышаем его статус от простого пользователя до уровня Администратора:
Последнее изменение гарантирует, что мы создаем токен TokenImpersonation. Это можно установить в атрибутах объекта токена:
При условии, что мы поддерживаем пользователя токена и уровень целостности, мы можем наконец использовать токен и выдать себя за исполняющий поток.
3.1.7 — Привилегия SeLoadDriverPrivilege
Привилегия LoadDriver описывается Microsoft в статье “User Right: Load and unload device drivers”. Тот факт, что драйверы устройств работают в ядре, делает эту привилегию очень желательной.
Наша цель - выполнить произвольный код в ядре, заданном только SeLoadDriverPrivilege, и обойти любые требования подписи драйверов в процессе. В большинстве документов об этой привилегии и соответствующем вызове Windows API “NtLoadDriver” предполагается, что вызывающий дополнительные привилегии в системе, в частности, возможность записи в раздел HKLM (HKEY_LOCAL_MACHINE) реестра Windows.
Формат вызова API Windows для загрузки драйвера выглядит следующим образом:
NTSTATUS NtLoadDriver( _In_ PUNICODE_STRING DriverServiceName);
DriverServiceName[in] это указатель на подсчитанную строку Unicode, которая указывает путь к разделу реестра драйвера, \Registry\Machine\System\CurrentControlSet\Services\DriverName, где DriverName - это имя драйвера.
Параметр DriverServiceName является указателем на местоположение реестра. Под ключом “DriverName” должно быть как минимум два следующих значения:
+ ImagePath - строка в формате "\??\C:\path\to\driver.sys"
+ Type - DWORD, который должен быть установлен в "1"
Обратите внимание, что формат параметра "DriverServiceName" предположительно (согласно документации) должен начинаться с "\Registry\Machine", который является ссылкой на раздел реестра HKLM, к которому мы предполагаем, что у нас нет доступа. Чтобы обойти это препятствие, мы можем вместо этого использовать путь, который указывает на HKCU (HKEY_CURRENT_USER), например "\Registry\User\S-1-5-21-582075628-3447520101-2530640108-1003\". Здесь числовой идентификатор в строке - это RID для нашего текущего пользователя. Мы должны сделать это, потому что ядро не знает, что такое HKCU, поскольку это просто помощник уровня пользователя, который указывает на куст HKLM.
Так как "System\CurrentControlSet\Services\DriverName" не существует по этому пути, мы должны его создать, а это мы можем сделать, так как это куст реестра текущего пользователя. Формат загрузки простого драйвера довольно прост. Мы должны, как минимум, определить две вещи:
Тип определяет службу, как указано в wdm.h:
В нашем случае мы будем использовать тип SERVICE_KERNEL_DRIVER.
Как только эти значения установлены, мы можем затем вызвать функцию NtLoadDriver с путем к нашему ключу реестра и загрузить драйвер в ядро.
Есть несколько других стратегий, которые можно использовать для загрузки драйверов режима ядра, таких как ключ FltMgr или condrv, но они не могут быть использованы без административных привилегий.
3.1.8 — Привилегия SeTakeOwnershipPrivilege
Эта привилегия оскорбительно похожа на SeRestorePrivilege. Согласно MSDN, она позволяет процессу "вступать во владение объектом без предоставления дискреционного доступа", предоставляя право доступа WRITE_OWNER. Эксплуатация этой привилегии очень похоже на SeRestorePrivilege, за исключением того, что сначала мы должны стать владельцем раздела реестра, в который мы хотим записать.
Чтобы стать владельцем раздела реестра, необходимо создать ACL с обновленным владельцем, а затем изменить DACL, чтобы мы могли писать в него. Для построения ACL требуется создание объекта EXPLICIT_ACCESS:
Затем мы можем использовать SetEntriesInAcl для создания объекта ACL. Как только объект ACL составлен, мы вступаем во владение путем реестра:
Как только у нас есть ключ реестра, мы делаем последний вызов SetNamedSecurityInfo, чтобы включить ранее составленный ACL и получить права на запись для записи:
На этом этапе мы можем выполнить те же шаги, что и с помощью метода SeRestorePrivilege, и убедиться, что восстановление реестра уже завершено.
Подобно SeRestorePrivilege, мы также можем контролировать критические системные файлы или папки, чтобы злоупотреблять порядком загрузки DLL или другими подобными методами.
3.1.9 — Привилегия SeDebugPrivilege
Привилегия SeDebugPrivilege очень мощная, он позволяет владельцу отлаживать другой процесс, включая чтение и запись в память этого процесса. Эта привилегия в течение многих лет широко использовалась авторами вредоносных программ и разработчиками эксплойтов, и поэтому многие из методов, которые можно было бы использовать для получения EoP с помощью этой привилегии, будут отмечены современными решениями для защиты конечных точек.
Существует множество различных стратегий внедрения памяти, которые можно использовать с этой привилегией, которые обходят большинство решений AV/HIPS. Их поиск оставлен в качестве упражнения для читателя.
3.2 - Эксплуатация Частичной Записи
Понимая, как можно использовать индивидуальные привилегии, мы можем теперь начать демонстрировать, как и почему они могут быть полезны для нас. Тематические исследования будут обсуждаться далее в разделе 4.
Этот метод был рожден в результате попыток уклониться от различных изменений ядра и пользовательского пространства с помощью частичной эксплуатации записи памяти.
Т.е. частичная запись - это инструкция, в которой контролируется пункт контролируется, но записанное значение может и не контролироваться. Или значение может быть однобитовой или однобайтовой модификацией, такой как уменьшение или сложение. Взять, к примеру, инструкцию DEC; если адрес получателя может контролироваться, то у нас есть возможность произвольно уменьшить любой адрес на один. Инструкция OR является еще одним примером; в то время как мы можем контролировать назначение, в котором мы выполняем операцию OR, мы не можем контролировать значение, в котором мы используем инструкцию OR: OR DWORD PTR[controlled], 4.
Как правило, для их эксплуатации потребуется изменить поля объекта, такие как длина, чтобы получить произвольный или частичный примитив для чтения/записи. Другие могут вносить изменения в таблицы страниц или искажать другие структуры данных режима ядра. Это все части большой цепочки эксплойтов, которая обычно заканчивается обнулением в ACL, обменом токенов процесса или выполнением привилегированного процесса пользовательского режима. Привилегированное исполнение (см.: полезная нагрузка) изменяется в зависимости от условий защиты.
Вместо того, чтобы пытаться выполнить ROP в ядре или манипулировать объектами ядра, почему бы не делегировать повышение привилегий пользователю? Подобно стратегии обмена процессами, если мы сможем включить повышенные привилегии в пользовательском пространстве, мы выиграем от повышения надежности, тривиального обхода защиты ядра и повышенной гибкости в доставке некоторой вредоносной полезной нагрузки. Мы хотим оставаться в системе надолго, и для этого мы должны оставаться в тайне.
Как обсуждалось в разделе 1.2, каждый токен имеет структуру _SEP_TOKEN_PRIVILEGES, содержащую те привилегии, которые токен имеет в настоящее время. Наш путь к повышению должен быть довольно очевидным: злоупотребляя частичной записью и утечкой информации из пользовательского пространства, мы можем перебросить несколько битов в нашем токене процесса и получить доступ к административным привилегиям. Поскольку мы часто не можем контролировать записываемое значение, важно, чтобы все возможные значения учитывали повышение привилегий. Мы определили три байта, в которых не предоставляются административные привилегии: 0x00, 0x40, 0x41. То есть, если бы вы получили MOV BYTE PTR [controlled], примитив 0x41, вы не смогли бы разрешить эксплуатируемую привилегию. Это предполагает инструкцию MOV для битовых масок привилегий по умолчанию; другие операции или комбинации привилегий могут предоставлять повышенные привилегии. Все остальные значения предоставляют доступ к эксплуатируемой привилегии.
Чтобы заполучить этот токен процесса, мы должны иметь возможность получить адрес токена контролируемого процесса. Поскольку в этой стратегии мы нацелены на уязвимости, связанные с повышением привилегий, мы можем использовать и продолжать использовать (начиная с v1703) общий API-интерфейс, т.е. функцию NtQuerySystemInformation для утечки нашего адреса токена процесса, как показано ниже:
Значение dwPid - это процесс, в котором существует токен, а hToken - дескриптор токена процесса. Обратите внимание, что в Windows 8.1 эта стратегия больше не работает в процессах с низкой целостностью, и поэтому необходимо будет использовать другие методы. Для краткости и ясности мы будем работать в предположении, что мы нацелены на повышение привилегий из процесса средней целостности (по умолчанию IL для пользователей) и полагаемся на вышеописанный метод.
3.3 - Злоупотребление существующими учетными записями служб
В дополнение к тому, что они полезны для локального повышения привилегий через произвольные примитивы записи в ядре, эти же методы могут быть полезны в более распространенном сценарии, когда злоумышленник обращается к компьютеру как к локальной учетной записи службы. Существует ряд распространенных сценариев, в которых злоумышленник может выполнить код в контексте учетной записи службы на целевом компьютере, включая следующие:
+ Сам сервис скомпрометирован из-за некоторой уязвимости. Типичные сценарии включают в себя уязвимости веб-приложений, которые допускают выполнение в контексте учетной записи, на которой запущен IIS, и уязвимости внедрения SQL, где XP_CMDSHELL можно использовать для запуска кода в контексте учетной записи службы SQL.
+ Учетные данные учетной записи службы утекли
+ Атаки в стиле Kerberoast. Билет Kerberos запрашивается для целевой учетной записи с контроллера домена. Часть этого билета зашифрована с использованием хэша пароля целевой учетной записи. Это может быть эффективно взломано в автономном режиме, чтобы получить пароль учетной записи.
В любом из этих сценариев, если учетная запись службы имеет одну из привилегий, описанных в предыдущем разделе, можно получить локальное повышение привилегий, просто используя соответствующий модуль из этого проекта.
3.3.1 - Общие учетные записи служб
В этом разделе, мы дадим краткие примеры некоторых общих учетных записей служб, которые могут использоваться для EoP из-за их привилегий токена по умолчанию.
3.3.1.2 - MSSQL / IIS
Если мы рассмотрим привилегии по умолчанию, назначенные учетным записям служб MSSQL и IIS с помощью инструмента "AccessChk" от Sysinternals, мы обнаружим следующее:
+ IIS - SeImpersonatePrivilege — BUILTIN\IIS_IUSRS
+ MSSQL - SeAssignPrimaryTokenPrivilege - NT SERVICE\SQLAgent$SQLEXPRESS, NT SERVICE\MSSQLLaunchpad$SQLEXPRESS, NT SERVICE\MSSQL$SQLEXPRESS
Этих прав достаточно для EoP, используя модули в этом проекте. Компрометация этих учетных записей - очень распространенный сценарий тестирования на проникновение. Каждый раз, когда SQL-инъекция в MSSQL или уязвимость веб-приложения в IIS используются для выполнения команд, злоумышленники получают эти привилегии. Традиционно это считалось ограничивающим сценарием с ограниченной локальной учетной записью, и злоумышленнику необходимо будет использовать другой метод для EoP. Используя методы, описанные в этой статье, это просто вопрос злоупотребления существующими привилегиями токена.
3.3.1.3 - Продукты для резервного копирования
Каждый коммерческий продукт для резервного копирования на рынке будет работать с какими-то повышенными привилегиями. Во многих случаях учетная запись службы резервного копирования будет работать с привилегиями SYSTEM, что делает ненужным EoP. Там, где администраторы начали совершенствоваться, мы начинаем видеть, что привилегии для этих учетных записей становятся более ограниченными.
Ниже приведены минимальные привилегии, необходимые для решения Veritas NetBackup, беззастенчиво заимствованные с их веб-сайта (https://www.veritas.com/support/en_US/article.TECH36718):
+ * Действовать как часть операционной системы (только для Windows Server 2000).
+ * Создать маркерный объект.
+ Войти как сервис.
+ Вход в систему как пакетное задание.
+ Управление аудитом и журналом безопасности.
+ * Резервное копирование файлов и каталогов.
+ * Восстановление файлов и каталогов.
Обратите внимание на 4 пункт в списке, которые мы пометили звездочкой (*).
Любая из этих привилегий в отдельности может быть использована для EoP с помощью одного из методов, описанных в этом проекте.
3.3.1.4 - Локальные сервисные аккаунты
На каждом компьютере Windows также есть предопределенные учетные записи служб, которые содержат привилегии, которые можно использовать для EoP. Это "NT AUTHORITY\SERVICE", "NT AUTHORITY\NETWORK SERVICE" и "NT AUTHORITY\LOCAL SERVICE".
Каждый из них имеет несколько отличающиеся привилегии, некоторые содержат несколько эксплуатируемых привилегий, однако все они имеют доступ к эксплуатируемому SeImpersonatePrivilege.
Если злоумышленник каким-то образом может получить доступ к системе в контексте одной из этих ограниченных локальных учетных записей, он может тривиально повысить свои привилегии до "NT AUTHORITY\SYSTEM", используя методы, описанные выше.
4 - Примеры разработки ядра эксплойтов
Теперь мы подробно рассмотрим несколько тематических исследований, которые демонстрируют, как и почему мы можем захотеть делегировать привилегированную эксплуатацию пользователю. Обратите внимание, что наша стратегия применяется к повышению привилегий; в своем текущем состоянии она не может использоваться дистанционно.
4.1 — MS16-135
MS16-135 - это ошибка, которую мы впервые написали для эксплуатации этой стратегии, и она оказалась фантастическим примером. Первоначально выпущенный Google после выявления активной эксплуатации в дикой среде ( https://security.googleblog.com/2016/10/disclosing-vulnerabilities-to-protect.html), триггер был быстро выпущен, и началась гонка за вооружение.
Одним из первых публичных демонстраций эксплуатации провел Enrique Nissim на Zero Nights 2016 (https://github.com/IOActive/I-know-where-your-page-lives/), в котором он использовал ошибку, чтобы продемонстрировать слабости рандомизации PML4. Несколько других POC последовали за ним, большинство из них злоупотребляли стратегией PML4, другие использовали технику pvscan0. Ошибка может быть вызвана через функцию SetWindowLongPtr со специально созданным окном и значением индекса. Результатом является управляемая операция OR: OR DWORD PTR[controlled], 4. Первым шагом является определение адреса _SEP_TOKEN_PRIVILEGES. Это может быть достигнуто с помощью следующего кода:
Сначала мы открываем дескриптор для родительского токена процесса, а затем используем функцию-обертку NtQuerySystemInformation для извлечения фактического адреса токена. Структура, за которой мы следуем, находится на 0x40 байтов впереди. Обратите внимание, что утечка функции NtQuerySystemInformation работает только из процессов средней целостности в Windows 8.1+. Чтобы эксплуатировать это в процессе с низким уровнем целостности, нам нужно использовать другую утечку (https://github.com/sam-b/windows_kernel_address_leaks ). Используя адрес токена, мы теперь корректируем смещение для включенной битовой маски и вызываем следующий код:
Если мы сгенерируем эту ошибку несколько раз при сдвиге смещения, мы можем изменить каждый байт в битовой маске Enabled.
Это включает следующие привилегии:
Как мы уже видели, три из четырех включенных привилегий тривиально эксплуатированы. Наш POC использует злоупотребление SeRestorePrivilege, и его можно найти в репозитории проекта git (https://github.com/hatRiot/token-priv).
Хотя многие публичные эксплойты для этой ошибки были написаны таким образом, чтобы продемонстрировать технику, мы обнаруживаем, что представленный пример подчеркивает простоту и надежность нашей стратегии: он опирается только на внешнюю необходимость утечки адреса токена из ядра. Публичный POC сложнее и требует разнообразных примитивных танцев c бубном над ядром.
4.2 — MS15-061
Это была еще одна фантастическая ошибка, наблюдаемая в дикой среде во время вирусной кампаний RussianDoll, и она была быстро исправлена и взята на вооружение в сообществе. Как и MS16-135, это частичная перезапись, которая позволяет уменьшить управляемый адрес. Эта ошибка использовала UAF в win32k, в частности еще одна проблема с обратными вызовами пользовательского режима, исходящими из в пределах win32k (https://community.rapid7.com/community/metasploit/blog/2015/10/01/flipping-bits).
Наша стратегия здесь не сильно отличается от примера в 4.1: мы идентифицируем наш адрес токена, собираем кучу для получения нашего произвольного уменьшения и запускаем его несколько раз, чтобы покрыть битовые маски Enabled и Present. Это включает целый ряд различных привилегий, а именно:
Всего доступно 14 привилегий; сразу же часто используемые показаны выше. Подтверждение концепции снова предоставлено в репозитории git проекта (https://github.com/hatRiot/token-priv).
Публичные образцы используют различные стратегии; один из первых, выпущенных NCC, использует более старую технику из Pwn2Own 2013, в которой шелл-код хранится в структуре tagWND, а бит ServerSideWindowProc уменьшается до тех пор, пока не будет перенесен, что означает, что оконная процедура tagWND будет выполняться без переключения контекста. Шелл-код использовал обнуление из ACL-списка winlogon и внедрялся в привилегированный процесс. В других примерах используется более современная стратегия обмена токенами процесса.
Дело в том, что нам не нужно выполнять какой-либо шелл-код, и нам не нужно работать для получения каких-либо других примитивов.
4.3 — HEVD
HEVD, или HacksysExtremeVulnerableDriver (https://github.com/hacksysteam/HackSysExtremeVulnerableDriver), является специальным уязвимым драйвером для Windows, который можно загрузить в систему для изучения и исследования различных стратегий и методов эксплуатации. Кроме того, он предоставляет простой способ демонстрации стратегий уклонения от безопасности в современных системах без использования 0days.
Мы демонстрируем нашу технику, используя произвольную ошибку записи, присутствующую в HEVD, инициируемую с помощью управляющего кода 0x22200b. Как уже упоминалось, "ошибка" является преднамеренным и управляемым примитивом write-what-where в драйвере. Ниже находтся код для краткости
Есть несколько публичных демонстраций эксплуатации этого; большинство для Windows 7, несколько для Windows 10 build 1607. Один от хакера Cn33liz (https://github.com/Cn33liz/HSEVD-ArbitraryOverwriteGDI ) демонстрирует эксплуатацию этой ошибки с помощью технологии GDI Reloaded, представленной на Ekoparty №16. Стратегия Reloaded улучшает первоначальную стратегию GDI pvscan, включая обход исправления KASLR таблицы общих дескрипторов GDI, представленного в v1607, за счет утечки адресов ядра через глобальную таблицу gSharedInfo (старая, но явно все еще жизнеспособная утечка).
В релизе v1703 (Creators Update) структура таблицы была изменена, а утечки адресов удалены. Таким образом, техника GDI Reloaded все еще работает, при условии, что мы можем идентифицировать другую утечку KASLR.
Последний пример примечания взят из GradiusX, который демонстрирует использование в системе Windows 10 v1703. В примере используется примитив GDI rw для утечки структуры EPROCESS из текущего процесса, который затем можно использовать для утечки адреса токена запущенного процесса. После утечки адреса он теряет адрес токена SYSTEM и переписывает, используя примитив GDI, свой резидентный токен токена SYSTEM. Таким образом, токены поменяется местами. Использование примитива GDI для утечки структуры EPROCESS (через _THREADINFO) позволяет ему обойти необходимость отдельной утечки KASLR и, следовательно, должно нормально работать из-за процессов с низкой целостностью.
Наша стратегия здесь довольно проста; нет необходимости собирать кучу, выполнять шеллкод или устанавливать примитивы rw. Из-за изменений в структуре _SEP_TOKEN_PRIVILEGES мы должны выполнить два отдельных вызова DeviceIoControl; один для перезаписи маски Enabled и один для маски Present. Как и в предыдущих примерах, мы получаем адрес токена и смещения:
Затем мы устанавливаем наши примитивы what/where (используя структуру PWRITE_WHAT_WHERE, как определено HEVD):
Затем просто запустите драйвер через функцию DeviceIoControl. Мы выполняем это во второй раз с флагом enabled_offset . Теперь у нас включены повышенные привилегии на токене.
5 — Заключение
Поскольку Microsoft продолжает развивать и совершенствовать базовые стратегии защиты в Windows, как в пользовательском пространстве, так и в ядре, злоумышленники дополнительно продолжают развиваться. Представленные здесь идеи не могут быть прорывом в наступательной эксплуатации ядра, но мы полагаем, что они дают представление о том, куда движутся методы эксплуатации. Навстречу логике, вдали от ядра, на земле, где приложения и скрипты и документы Office резвятся с критическими, привилегированными элементами управления. Мы продемонстрировали, как тривиально может быть эксплуатируемо rite-what-where, предсказуемым и стабильным способом, используя только информации из ядра.
Нападение на индивидуальные привилегии представляется логичным развитием существующих тенденций: замена токенов, искажение DACL, запись в привилегированные файлы. Ограничение области привилегированного доступа повышает общую эффективность и снижает вероятность атаки.
В идеале, мы можем определить другие такие привилегии, которыми следует злоупотреблять, поскольку они настолько же обильны, насколько и непрозрачны. Структура EPROCESS содержит флаги и маски, управляющие различными механизмами доступа в ядре. Мы касались имперсонализации(олицетворения) токенов с разделенным доступом, потоков и других подобных средств, но их примитивный потенциал почти гарантирован. Да будет проклято это чертово ядро Windows.
6 — Приветы
Людей слишком много, но основных я перечислю ниже:
themson, bannedit, quitos, tiraniddo, aionescu, phrack, pastor laphroaig, shellster
Источник: https://www.exploit-db.com/exploits/42556
Автор перевода: yashechka
Специально для xss.pro (c)
За последние несколько лет эксплуатация ядра Windows становилась все более сложной, особенно с выпуском системы Windows 10 и её последующими обновлениями ядра. Методы рождались и умирали в течение нескольких месяцев, новые быстро заменяли старые, которые не работали. Сами техники стали хрупкими, подходящими только к определенному шаблону или пространству.
Эксплуатация часто опиралась на получение какой-либо формы выполнения произвольного кода. Получение примитива для чтения/записи, стратегий для работы со стеком, позволяющих обойти защиту, и в конечном итоге получить выполнение привилегированного действия. Недавно мы наблюдали тенденцию к базовым логическим действиям, таким как кража токенов, включение битов режима бога, обнуление дескрипторов безопасности токена. (https://blogs.technet.microsoft.com/mmpc/2017/01/13/hardening-windows-10-with-zero-day-exploit-mitigations/) Эти действия делегируют кражу привилегий в пользовательском пространстве, освобождая разработку эксплоита от адовых ограничений ядра.
Шеллкод для кражи токенов, популярен среди многих разработчиков эксплоитов и авторов вредоносных программ на протяжении многих лет, и это не случайность. Это был чрезвычайно надежный метод, предлагающий стабильный, простой шеллкод и, до недавнего времени, приемлемое поведение. Microsoft реализовала эвристическое обнаружение в рамках платформы Advanced Threat Protection (https://blogs.technet.microsoft.com/mmpc/2016/11/01/our-commitment-to-our-customers-security/), но пока еще ничего не реализовала в самом ядре Windows. Для авторов вредоносных программ и скрипт-киддисов это может быть приемлемо, но для серьёзных противников или "Red Team" конечно нет.
Цель этой статьи состоит в том, чтобы обсудить один из таких логических приемов, которые мы усовершенствовали за последние несколько месяцев и применяем в действии. Хотя сами методы не новы (https://blogs.technet.microsoft.com/mmpc/2016/11/01/our-commitment-to-our-customers-security/), мы надеемся представить новые подходы и идеи, которые могут помочь в дальнейшем совершенствовании этого метода и других.
В дополнение к эксплуатации ядра, злоупотребления привилегиями токена могут быть использованы другими, менее экзотическими способами. В ситуациях, когда служебная учетная запись скомпрометирована, у которой включены нестандартные привилегии, их часто можно использовать для повышения привилегий (EoP - elevation of privilege). Методы для этого специфичны для каждой привилегии, часто недокументированны и во многих случаях нетривиальны. В разделе 3.3 этого документа мы показываем, сколько из этих привилегий может быть использовано для EoP в обычных тестах на проникновение и в сценариях "Red Team".
Мы стремимся объединить разрозненные источники и предоставить справочную информацию для будущей работы. Мы признаем время и усилия других исследователей в том же области работы и надеемся дать что-то значимое сообществу в целом.
1 - Обзор Токена
Основа нашей стратегии опять-таки основана на самой модели доступа к объектам в Windows. Windows использует объекты токенов для описания контекста безопасности определенного потока или процесса. Эти объекты токенов, представленные структурой nt!_TOKEN, которая содержит обширную информацию о безопасности и справочную информацию, включая уровень целостности, привилегии, группы и многое другое. Наше внимание сосредоточено на привилегиях, содержащихся в этих токенах.
1.1 - Модель Привилегий Windows
Мы кратко опишем модель привилегий Windows, поскольку она относится к токенам процессов и потоков. Если вам нужно подробное объяснение, авторы рекомендуют прочитать книгу «Внутреннее устройство Windows» или провести некоторое время в отладчике Windbg.
Каждый процесс в системе содержит ссылку на объект токена в своей структуре EPROCESS, которая используется во время согласования доступа к объекту или привилегированных системных задач. Этот токен предоставляется через службу LSASS во время процесса входа в систему, и, таким образом, изначально все процессы в сеансе выполняются под одним и тем же токеном.
Процесс содержит основной токен, и потоки, выполняющиеся внутри процесса, наследуют этот же токен. Когда потоку необходим доступ к объекту с использованием другого набора учетных данных, он может использовать токен исполнения роли(impersonation token). Использование токена исполнения роли не влияет на основной токен или другие потоки, а только на выполнение в контексте потока исполнения роли. Эти токены исполнения роли могут быть получены с помощью ряда различных API, предоставляемых ядром.
Токен служит билетом доступа к процессам, который должен быть предоставлен различным компонентам (gatekeepers) в Windows; токен предоставляется через функцию SeAccessCheck при доступе к объекту и функцию SeSinglePrivilegeCheck во время привилегированных операций. Например, когда процесс запрашивает доступ для записи в файл, функция SeAccessCheck оценивает уровень целостности токенов, а затем оценивает его Дискреционный Список Контроля Доступа (DACL). Когда процесс пытается завершить работу системы через функцию NtShutdownSystem, ядро проверяет, активирован ли токен у запрашивающего процесса SeShutdownPrivilege или нет.
1.2 - Структура Токенов и Привилегии
Как уже упоминалось, структура _TOKEN в основном содержит контекстную информацию о безопасности процесса или потока. Соответствующая запись для наших целей это _SEP_TOKEN_PRIVILEGES, расположенная по смещению 0x40, содержащая информацию о привилегиях токена:
C:
kd> dt nt!_SEP_TOKEN_PRIVILEGES c5d39c30+40
+0x000 Present : 0x00000006`02880000
+0x008 Enabled : 0x800000
+0x010 EnabledByDefault : 0x800000
Запись Present представляет переменную типа long long без знака, содержащую текущие привилегии в токене. Это не означает, что они включены или отключены, но только то, что они существуют на токене. После создания токена, вы не можете добавить к нему привилегии; Вы можете только включить или отключить существующие, найденные в этом поле. Второе поле, Enabled, представляет переменную типа long long без знака, содержащую все включенные привилегии на токене. В этой битовой маске должны быть включены привилегии для передачи функции SeSinglePrivilegeCheck. Последнее поле, EnabledByDefault, представляет начальное состояние токена на момент создания.
Привилегии могут быть включены или отключены путем изменения определенных битов в этих полях. Например, чтобы задействовать SeCreateTokenPrivilege, нужно просто выполнить: _SEP_TOKEN_PRIVILEGES + 0x44 | = 1 << 0x000000002. Отключение привилегии будет обратным: _SEP_TOKEN_PRIVILEGES + 0x44 & = ~ (1 << 0x000000002). Вспомогательный скрипт на Pykd можно найти здесь (https://github.com/hatRiot/token-priv).
До недавнего времени, нужно было только устанавливать биты в поле "Enabled", чтобы фактически переключать привилегии в токене. Это означает, что для включения привилегий достаточно одной записи - частичной или другой. Однако с выпуском Windows 10 v1607 ядро теперь проверяет, что разрешающие биты также отображаются в поле Present (http://www.anti-reversing.com/2251/).
Хотя на первый взгляд модель безопасности токенов для определения конкретных привилегий для различных задач, по-видимому, позволяет реализовать детализированные средства управления доступом, специфичные для службы, при более внимательном рассмотрении выявляется более сложная ситуация. Многие из привилегий, если они включены, позволяют пользователю выполнять привилегированные действия, которые могут привести к повышению привилегий. Это эффективно разрушает "мелкозернистую" структуру управления доступом и может дать ложное чувство безопасности.
1.3 — Токен Исполнения Роли
Прежде чем углубляться в конкретные привилегии, будет полезно описать механизм Windows для определения, может ли конкретный поток использовать данный токен. Любой пользователь может получить дескриптор привилегированного токена, но возможность использовать его - другое дело.
В Windows "Токен Исполнения Роли" - это когда новый токен назначается потоку, который отличается от токена родительского процесса. Хотя слова Исполнения Роли подразумевают, что один пользователь использует токен, принадлежащий другому пользователю, это не всегда так. Пользователь может выдавать себя за принадлежащий ему токен, но просто имеет другой набор привилегий или некоторые другие модификации.
Одним из полей, указанных в каждом токене, является уровень исполнения роли токенов. Это поле определяет, можно ли использовать этот токен в целях исполнения роли и в какой степени. Существует четыре уровня исполнения роли:
SecurityAnonymous - Сервер не может идентифицировать клиента.
SecurityIdentification - Сервер может получить идентификационные данные и привилегии клиента, но не может выдать себя за клиента.
SecurityImpersonation - Сервер может исполнить роль контекста безопасности клиента в локальной системе.
SecurityDelegation - Сервер может исполнить роль контекст безопасности клиента в удаленных системах.
SecurityImpersonation and SecurityDelegation это самые интересные случаи для нас. Идентификационные токены и ниже не могут быть использованы для запуска кода.
Разрешено ли данному пользователю выдавать себя за конкретный токен или нет, можно определить следующим образом:
ЕСЛИ уровень токенов МЕНЬШЕ Impersonate, ТО разрешить (такие токены называются уровнем "Идентификация" и не могут использоваться для привилегированных действий).
ЕСЛИ процесс имеет привилегию "Impersonate" ТОГДА разрешать.
ЕСЛИ уровень целостности процесса МЕНЬШЕ ИЛИ РАВЕН уровню целостности токена И пользователь процесса РАВЕН пользователю токена, ТОГДА разрешить ИНАЧЕ ограничить токен уровнем "Идентификация" (привилегированные действия невозможны).
2 - Современные средства безопасности и их методы
Windows 10 значительно повышает безопасность ядра Windows, как в плане общего уменьшения поверхности атаки, так и улучшения существующих средств защиты. Microsoft продолжает перебирать защитные механизмы: KASLR продолжает получать столь необходимые улучшения (все еще много утечек), усложнение часто эксплуатируемых структур для примитивов чтения/записи (tagWND) и улучшение защитного механизм против техники NULL SecurityDescriptor (https://labs.nettitude.com/blog/ana...n-in-the-latest-windows-10-v1607-build-14393/). На протяжении многих лет злоумышленники демонстрировали, что подавление отдельных стратегий порождает только новые, и цикл продолжается.
Чтобы обеспечить некоторый контекст по обсуждаемым темам, далее следует краткое описание современных методов защиты ядра и методов эксплуатации. Если у тебя есть вопросы, просто напиши Ionescu.
2.1 - Современные средства защиты ядра Windows
Windows 10 и последующие обновления (Anniversary/Creators) включили в себя ряд улучшений по защите от эксплойтов, подробно описанных на Blackhat 2016 (https://www.blackhat.com/docs/us-16/materials/us-16-Weston-Windows-10-Mitigation-Improvements.pdf). Мы рекомендуем ознакомиться со ссылками на слайды для получения дополнительной информации и статистики по общим вопросам связанных с безопасностью Windows. Мы сосредоточимся на мерах по безопасности, относящихся к данной теме.
Ядро ASLR улучшилось за счет включения ASLR в различных регионах и структурах ядра. Хотя это сильная стратегия зашиты для удаленных эксплойтов ядра, существует несколько открытых и закрытых стратегий для утечки адресов объектов в ядре (https://github.com/sam-b/windows_kernel_address_leaks), и, таким образом, это не представляет большой угрозы для описанной здесь техники. Основные структуры данных и области памяти, когда-то использовавшиеся для обходов KASLR, такие, как таблица диспетчеризации HAL, теперь полностью рандомизированы.
AppContainer, впервые представленный в Windows 8, предоставляет возможности песочницы для пользовательских приложений. Этот элемент управления был расширен в Windows 10 для включения фильтрации системных вызовов win32k, который ограничивает процесс от злоупотребления различными системными вызовами win32k. Эта защита в настоящее время только (официально) включено для браузера Edge, и, следовательно, она не очень интересна.
Распространенным примитивом чтения/записи в ядре является структура tagWND, которая при повреждении допускает произвольное чтение/запись через функции InternalGetWindowText/NtUserDefSetText. Было обнаружено, что эксплойты для MS15-061 и совсем недавно MS16-135 используют эту технику. Anniversary Update теперь предоставляет дополнительные проверки границ для этого конкретного объекта, делая эксплоит бесполезным. Хотя эта форма атаки является элементарной, очень раздражает необходимость находить другие примитивы чтения/записи, она может быть эффективной для взлома уже развернутых/разработанных цепочек.
Внедрение SMEP в Windows 8 означает, что мы больше не можем просить ядро выполнить шелл-код из пользовательского режима для нас. Многие обходы использовались на протяжении многих лет, некоторые все еще эффективны, но большинство нет. Уклонение от данной атаки обычно включает в себя отключение некоторого элемента безопасности или получения кода в ядре (через регионы RWX или Kernel ROP). Поскольку мы не стремимся выполнять какой-либо шеллкод в ядре или даже в пользовательском пространстве, эта защита не применяется.
Еще одна интересная защита - мифический Patchguard, ранее известный как Kernel Patch Protection, давно представленная ядру x64 NT. Если вы не знакомы с этой защитой, знайте, что это бугимен ядра. Защита отслеживает случайное подмножество проверок в случайные моменты времени по случайным причинам. Его инициализация и поведение во время выполнения обфусцированы. Его исходный код и поведение скрыты даже от внутренних разработчиков Windows. Skywing и Skape ранее проделали довольно много работы, реверся и документируя его части, и на них следует ссылаться для дальнейшего рассказа (http://uninformed.org/index.cgi?v=8&a=5&p=2)
Начиная с Windows 10, в Patchguard имеется 44 различных проверки (видимые через !analyse -show 109), и хотя авторы с подозрением относятся к полноте списков, защита токенов процесса не предусмотрена.
2.2 - Соответствующие Cтратегии Эксплуатации
Предыдущая, связанная работа, которая обеспечила влияние и косвенное руководство для стратегии этой статьи, представлена здесь. Эти сопутствующие методы кратко изложены, чтобы предоставить справочную информацию и воздать должное тем, кто был до нас.
Статья Cesar Cerrudos Easy Local Windows Kernel Exploration, опубликованная на Blackhat 2012 (https://media.blackhat.com/bh-us-12/Briefings/Cerrudo/BH_US_12_Cerrudo_Windows_Kernel_WP.pdf), представил три различных стратегии повышения привилегий и указала многим разработчикам эксплойтов на силу злоупотребления токенами процесса. Первый метод, продемонстрированный в статье, рассказывает подробно о стратегии NULL ACL, в которой произвольная запись может использоваться для NULL привилегированного объекта ACL. Это была и остается очень распространенной стратегией для эффективного перехода к более привилегированным процессам.
Вторая стратегия Cerrudos это наша версия с бомбардировкой, в которой произвольная запись может включить все привилегии в токене процесса. С этими привилегиями можно использовать атрибут SeDebugPrivilege и переходить к более привилегированному процессу, создавать токены с привилегиями SeCreateTokenPrivilege или загружать драйверы ядра с привилегиями SeLoadDriverPrivilege.
Третья и последняя стратегия Cerrudos это еще один метод, очень популярный в современных эксплойтах EoP, который предусматривает замену токена процесса на токен SYSTEM. Это было подробно описано с разных точек зрения в других местах и не будет повторяться здесь.
Yin Liang и Zhou Li из Tencent провели и опубликовали аналогичное исследование на Blackhat Europe 2016, в котором они продемонстрировали злоупотребление частичной записью с объектами Windows для получения примитивов для чтения/записи (https://www.blackhat.com/docs/eu-16/materials/eu-16-Liang-Attacking-Windows-By-Windows.pdf). Как и наша работа, они были сосредоточены на частично контролируемых, ограниченных ошибках записи, таких как MS16-135 и CVE-2016-0174, в которых можно контролировать инструкции OR или DEC. В этих случаях, особенно с участием системы win32k, наша стратегия устраняет необходимость в получении примитива.
Moritz Jodeit опубликовал отличную статью о CVE-2014-4113, в которой он изменил структуру _SEP_TOKEN_PRIVILEGES с помощью неконтролируемой записи, чтобы включить дополнительные привилегии токена (https://labs.bluefrostsecurity.de/publications/2016/01/07/exploiting-cve-2014-4113-on-windows-8.1/). Его метод представляет интерес, поскольку в то время он продемонстрировал современное уклонение от защиты без перезаписи указателя или выполнения шеллкода, с дополнительным преимуществом значительно упрощенного процесса эксплуатации.
3 - Злоупотребление привилегиями токена
3.1 - Эксплуатируемые привилегии
Для целей этой статьи, мы определим "эксплуатируемую" привилегию как любую привилегию токена, которую можно использовать отдельно для получения доступа уровня "NT AUTHORITY\SYSTEM» на целевой системе.
Как упоминалось в разделе 1.2, структура nt!_SEP_TOKEN_PRIVILEGES - это двоичное поле в токене, где каждый бит определяет, присутствует ли данная привилегия в токене. Точную структуру этой битовой маски, какие биты соответствуют каким привилегиям, можно найти в коде, выпущенном вместе с этим проектом, в частности в скрипте pykd — "tokenum.py".
В оставшейся части этого раздела рассматриваются детали каждой привилегии, которую мы смогли успешно использовать для получения повышенных привилегий. Образцы кода для использования каждой из этих привилегий включены в этот проект.
3.1.1 – Привилегия SeImpersonatePrivilege
SeImpersonatePrivilege описывается в MSDN как "User Right: Impersonate a client after authentication." Это привилегия, упомянутая в разделе 1.4 на втором этапе, при проверке того, может ли конкретный процесс выдавать себя за данный токен. Любой процесс, обладающий этой привилегией, может выдавать себя за любой токен, для которого он может получить дескриптор. Следует отметить, что эта привилегия не позволяет создавать новые токены.
Эта особая привилегия довольно интересна, поскольку она требуется для ряда общих учетных записей служб Windows, таких, как LocalService и для MSSQL и IIS. "Эксплойт" для этой привилегии также может привести к повышению привилегий, если какие-либо из этих учетных записей будут скомпрометированы. Это было предметом предыдущей работы авторов (https://foxglovesecurity.com/2016/0...e-escalation-from-service-accounts-to-system/).
В (https://bugs.chromium.org/p/project-zero/issues/detail?id=325) описан способ получения дескриптора токена для учетной записи "NT AUTHORITY\SYSTEM". В основном службе Windows (DCOM) передается специально созданный объект, который содержит ссылку на контролируемый злоумышленником TCP-слушатель на локальном компьютере. Когда Windows пытается разрешить эту ссылку, злоумышленник запрашивает NTLM аутентификацию, а затем передает аутентификацию NTLM, отправленную слушателю, для создания нового токена на локальном компьютере. Этот токен будет для пользователя "NT AUTHORITY\SYSTEM" и будет иметь все права администратора.
Любой пользователь может выполнить ранее описанную процедуру, чтобы получить дескриптор токена для пользователя "NT AUTHORITY\SYSTEM", однако для использования этого дескриптора требуется возможность исполнять роль; SeImpersonatePrivilege позволяет нам сделать это. Все, что требуется для порождения нового процесса с повышенным токеном, - это вызвать функцию CreateProcessWithTokenw, передав новый токен в качестве первого аргумента.
3.1.2 — Привилегия SeAssignPrimaryPrivilege
SeAssignPrimaryPrivilege очень похожа на ранее обсуждавшуюся SeImpersonatePrivilege. Необходимо только "назначить основной токен процесса". Наша стратегия заключается в создании нового процесса с использованием, более привилегированного токена.
Чтобы создать новый процесс с привилегированным токеном, нам сначала нужно получить дескриптор такого токена. Для этого мы следуем процедуре, описанной в (https://bugs.chromium.org/p/project-zero/issues/detail?id=325) и кратко изложенной в разделе 3.1.1.
Как следует из названия этой привилегии, она позволяет нам назначить основной токен новому или приостановленному процессу. Используя стратегию, описанную в 3.1.1, для получения токена, мы находимся с привилегированным токеном исполнения роли и, таким образом, сначала должны извлечь из него первичный токен. Это может быть выполнено через функцию DuplicateTokenEx:
DuplicateTokenEx(hClientToken, TOKEN_ALL_ACCESS, NULL, SecurityAnonymous, TokenPrimary, &hDupedToken);
С привилегированным первичным токеном у нас теперь есть несколько вариантов. К сожалению, мы не можем просто поменять токен нашего текущего запущенного процесса на привилегированный, так как изменение основных токенов в запущенных процессах не поддерживается. Это контролируется полем PrimaryTokenFrozen в структуре EPROCESS.
Самый простой вариант - вызвать функцию "CreateProcessAsUser", используя новый токен в качестве аргумента для создания нового процесса с высокими привилегиями.
В качестве альтернативы, мы можем запустить новый процесс в приостановленном состоянии и выполнить ту же операцию, что и выше. Когда создаются новые процессы с вызовом функции CreateProcess (..., CREATE_SUSPENDED, ...), значение для PrimaryTokenFrozen еще не установлено, что позволяет менять токен.
Следствием предыдущего пункта является то, что в некоторых сценариях частичной эксплуатации записи мы можем фактически заменить токен запущенного в данный момент процесса на привилегированный. Если мы можем заставить поле PrimaryTokenFrozen быть сброшенным посредством частичной записи, предполагая, что мы обладаем, или можем получить SeAssignPrimaryTokenPrivilege, мы можем затем вызвать функцию NtSetInformationProcess, чтобы заменить старый токен на новый. Поскольку PrimaryTokenFrozen является однобитовым полем, окружающие поля являются релевантными, потому что они, вероятно, будут уничтожены при любой частичной записи:
C:
+0x0c8 RefTraceEnabled : Pos 9, 1 Bit
+0x0c8 DisableDynamicCode : Pos 10, 1 Bit
+0x0c8 EmptyJobEvaluated : Pos 11, 1 Bit
+0x0c8 DefaultPagePriority : Pos 12, 3 Bits
+0x0c8 PrimaryTokenFrozen : Pos 15, 1 Bit
+0x0c8 ProcessVerifierTarget : Pos 16, 1 Bit
+0x0c8 StackRandomizationDisabled : Pos 17, 1 Bit
Если, например, у вас есть произвольный декремент, как в уязвимости MS15-061, вы можете сбросить бит TokenPrimaryFrozen и обменять токен процесса:
NtSetInformationProcess(hCurrentProcess,(PROCESS_INFORMATION_CLASS)0x09, &hElevatedPrimaryToken, 8);
3.1.3 — Привилегия SeTcbPrivilege
SeTcbPrivilege довольно интересна. MSDN описывает её как "Эта привилегия идентифицирует своего владельца как часть базы доверенных компьютеров. Некоторым доверенным защищенным подсистемам предоставляется эта привилегия". В дополнение к этому, в ряде книг, статей и сообщений на форумах описывается привилегия TCB как эквивалент полностью привилегированного доступа к машине. Однако, несмотря на все это, ни один из общедоступных ресурсов, кажется, не указывает, как SeTcbPrivilege может использоваться сам по себе для выполнения привилегированных операций.
Мы начали с анализа документации MSDN, пытаясь выяснить, какие вызовы Windows API были разрешены для учетных записей с помощью SeTcbPrivilge. В документации по
функции LsaLogonUser в качестве первого параметра мы находим следующее:
LsaHandle [in] это дескриптор, полученный из предыдущего вызова функции LsaRegisterLogonProcess. Вызывающая сторона должна иметь SeTcbPrivilege, только если выполняется одно или несколько из следующих условий:
+ Используется пакет Subauthentication.
+ KERB_S4U_LOGON используется, и вызывающая сторона запрашивает токен имперсонализации.
+ Параметр LocalGroups не равен NULL.
Если SeTcbPrivilege не требуется, вызовите функцию LsaConnectUntrusted для получения дескриптора.
Обычно вызов функции LsaLogonUser используется для аутентификации пользователя с использованием некоторой формы учетных данных, однако мы предполагаем, что нам неизвестно о целевой системе и ее пользователях. Кроме того, какой пользователь будет пытаться войти? Наконец, как мы будем исполнять роль полученного токен, поскольку у нас нет SeImpersonatePrivilege?
К счастью, James Forshaw включил очень полезное сообщение <140 символов:
"Вы можете использовать функцию LsaLogonUser, чтобы добавить группу администраторов к токену вашего собственного пользователя, а затем выдать себя за другого".
В Windows есть интересный тип входа, известный как вход в систему S4U (и упоминаемый выше как KERB_S4U_LOGON). Это эффективно описано в блоге MSDN (https://blogs.msdn.microsoft.com/winsdk/2015/08/28/logon-as-a-user-without-a-password/) следующим образом:
"В Windows можно войти в систему как пользователь другого домена без каких-либо учетных данных. Это известно как S4U или Сервис для входа пользователя. Это расширение Microsoft для Kerberos, представленное в Windows Server 2003."
Кажется, это соответствует тому, что мы пытаемся сделать идеально; используя тип входа S4U, мы можем получить токен для любого пользователя. Возвращаясь к документации по параметру LsaHandle выше, если у нас есть SeTcbPrivilege, очевидно, что полученный токен может быть токеном "имперсонализации", то есть мы можем назначить его потоку.
Снова обращаясь к параметру LsaHandle, последний пункт означает, что мы можем вызвать функцию LsaLogonUser с помощью SeTcbPrivilege и добавить произвольные группы к результирующему токену, возвращенному этим вызовом. Мы добавим SID группы "S-1-5-18" к токену, это SID для учетной записи Локальной Системы, и если мы используем токен, которым он принадлежит, мы получим полную привилегию в системе. Добавить системный SID довольно просто:
C:
WCHAR systemSID[] = L"S-1-5-18";
ConvertStringSidToSid(systemSID, &pExtraSid);
pGroups->Groups[pGroups->GroupCount].Attributes = SE_GROUP_ENABLED | SE_GROUP_MANDATORY;
pGroups->Groups[pGroups->GroupCount].Sid = pExtraSid;
pGroups→GroupCount++;
Единственная часть, оставшаяся в этой головоломке, - это то, как мы будем использовать полученный маркер имперсонализации, поскольку мы предполагаем, что у нас есть привилегия SeTcbPrivilege, но нет никаких других привилегий. Возвращаясь к разделу 1.4, посвященному правилам, связанным с токеном имперсонализаци, мы видим, что мы должны иметь возможность выдавать себя за токен без каких-либо специальных привилегий, если токен предназначен для нашего текущего пользователя, а уровень целостности токена равен “Medium”. Таким образом, используя токен, возвращенный функцией LsaLogonUser, мы просто устанавливаем уровень целостности “Medium”, а затем вызываем функцию SetThreadToken, чтобы заменить токен нашего текущего потока новым.
3.1.4 — Привилегия SeBackupPrivilege
Привилегия SeBackupPrivilege описывается в MSDN следующим образом:
«Требуется для выполнения операций резервного копирования. Эта привилегия заставляет систему предоставлять все права доступа для чтения любому файлу, независимо от списка управления доступом (ACL), указанного для файла. Любой запрос доступа, кроме чтения, все еще оценивается с помощью ACL. Эта привилегия требуется для функций RegSaveKey и RegSaveKeyExfunctions. Следующие права доступа предоставляются, если эта привилегия удерживается: -READ_CONTROL -ACCESS_SYSTEM_SECURITY -FILE_GENERIC_READ -FILE_TRAVERSE ”
Чтобы использовать эту привилегию для EoP, мы читаем хэши паролей учетных записей локальных Администраторов из реестра и затем передаем их локальной службе, из которой мы можем получить выполнение кода, самым популярным методом здесь будет просто передать хеш с помощью “psexec” или “wmiexec”.
К сожалению, для нас есть два предостережения в этом сценарии. Во-первых, многие организации за последние несколько лет начали отключать локальные учетные записи администраторов. Во-вторых, даже в тех случаях, когда некоторые учетные записи локальных администраторов остаются включенными, корпорация Майкрософт внесла изменения в Windows Vista и новее, когда только учетная запись RID 500 (локальный Администратор по умолчанию) может удаленно администрировать машину по умолчанию:
Когда пользователь, который является членом группы локальных администраторов на целевом удаленном компьютере, устанавливает удаленное административное соединение ... он не будет подключаться как настоящий администратор. У пользователя нет возможности повышения прав на удаленном компьютере, и он не может выполнять административные задачи.
Однако, как показывает наш опыт, в корпоративных средах все еще довольно часто включена учетная запись локального администратора RID 500.
3.1.5 — Привилегия SeRestorePrivilege
Привилегия восстановления описывается как "необходимая для выполнения операций восстановления" и заставляет систему предоставлять все права доступа для записи любому файлу в системе, независимо от файлов ACL. Кроме того, эта привилегия позволяет процессу хранения или потоку изменять владельца файла. Последствия получения этой привилегии должны быть очевидны.
Чтобы использовать эту привилегию, необходимо разрешить флаг FILE_FLAG_BACKUP_SEMANTICS для поддержки API. Это подсказывает ядру, что в запрашивающем процессе могут быть включены SeBackupPrivilege или SeRestorePrivilege, и проверять его перед short circuiting проверки DACL. Из краткого описания привилегии в MSDN могут заинтересоваться наблюдатели, которые также позволяют создавать или изменять разделы реестра.
Произвольные записи в HKLM открывают бесконечный потенциал для повышения привилегий. Мы решили использовать ключ Image File Execution Options, используемый для отладки программного обеспечения в системе. Когда запускается системный двоичный файл, если для него существует запись HKLM по адресу HKLM\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Image File Execution Options, и он содержит ключ отладчика, он выполнит заданную запись. Этот метод довольно распространен для уязвимостей, связанных с сохранением вредоносных программ и повышением привилегий (https://bugs.chromium.org/p/project-zero/issues/detail?id=872). Эксплуатация просто требует открытия реестра, указания флага резервного копирования и создания ключа:
C:
RegCreateKeyExA(HKEY_LOCAL_MACHINE,“HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\wsqmcons.exe”, 0, NULL, REG_OPTION_BACKUP_RESTORE, KEY_SET_VALUE, NULL, &hkReg, NULL);
RegSetValueExA(hkReg, "Debugger", 0, REG_SZ, (const BYTE*)regentry, strlen(regentry) + 1);
В приведенном выше примере мы использовали wsqmcons, службу консолидатора, которая работает с правами SYSTEM и запускается обычным пользователем в системе. Помимо добавления записей реестра, можно также перетаскивать библиотеки DLL в системные папки для перехвата библиотек DLL, перезаписи критических системных ресурсов или изменения других служб.
3.1.6 — Привилегия SeCreateTokenPrivilege
Привилегия SeCreateTokenPrivilege позволяет пользователям создавать первичные токены с помощью функции ZwCreateToken. К сожалению, одно это право не позволяет пользователю использовать только что созданный токен. Поэтому наивные попытки создать и использовать токены для пользователей с высокими привилегиями, таких как "NT AUTHORITY\SYSTEM", не увенчаются успехом.
Напомним правила имперсонализации токенов из раздела 1.4. Пользователю разрешено выдавать себя за токен даже без SeImpersonatePrivilege, если токен предназначен для того же пользователя и уровень целостности меньше или равен текущему уровню целостности процесса.
Чтобы использовать SeCreateTokenPrivilege, нам нужно только создать новый токен имперсонализации, который соответствует запрашивающему токену с добавлением привилегированной SID группы. Теоретически это довольно просто сделать, но API для этих интерфейсов и функция ZwCreateToken не очень дружелюбны. Большинство полей мы можем запросить из нашего исполняющего токена через функцию GetTokenInformation, с несколькими исключениями.
Как уже упоминалось, мы хотим включить локальную группу администраторов на токене. Для этого мы создаем SID, используя RID группы:
C:
SID_BUILTIN SIDLocalAdminGroup = { 1, 2, { 0, 0, 0, 0, 0, 5 }, { 32, DOMAIN_ALIAS_RID_ADMINS } };
Затем мы перебираем группы токена и повышаем его статус от простого пользователя до уровня Администратора:
C:
for (int i = 0; i < groups->GroupCount; ++i, pSid++)
{
PISID piSid = PISID)pSid->Sid;
if (piSid->SubAuthority[piSid->SubAuthorityCount - 1] == DOMAIN_ALIAS_RID_USERS)
{
memcpy(piSid, &TkSidLocalAdminGroup, sizeof(TkSidLocalAdminGroup));
pSid->Attributes = SE_GROUP_ENABLED;
}
}
Последнее изменение гарантирует, что мы создаем токен TokenImpersonation. Это можно установить в атрибутах объекта токена:
C:
SECURITY_QUALITY_OF_SERVICE sqos = { sizeof(sqos), SecurityImpersonation, SECURITY_STATIC_TRACKING, FALSE };
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, 0, 0, 0, &sqos };
При условии, что мы поддерживаем пользователя токена и уровень целостности, мы можем наконец использовать токен и выдать себя за исполняющий поток.
3.1.7 — Привилегия SeLoadDriverPrivilege
Привилегия LoadDriver описывается Microsoft в статье “User Right: Load and unload device drivers”. Тот факт, что драйверы устройств работают в ядре, делает эту привилегию очень желательной.
Наша цель - выполнить произвольный код в ядре, заданном только SeLoadDriverPrivilege, и обойти любые требования подписи драйверов в процессе. В большинстве документов об этой привилегии и соответствующем вызове Windows API “NtLoadDriver” предполагается, что вызывающий дополнительные привилегии в системе, в частности, возможность записи в раздел HKLM (HKEY_LOCAL_MACHINE) реестра Windows.
Формат вызова API Windows для загрузки драйвера выглядит следующим образом:
NTSTATUS NtLoadDriver( _In_ PUNICODE_STRING DriverServiceName);
DriverServiceName[in] это указатель на подсчитанную строку Unicode, которая указывает путь к разделу реестра драйвера, \Registry\Machine\System\CurrentControlSet\Services\DriverName, где DriverName - это имя драйвера.
Параметр DriverServiceName является указателем на местоположение реестра. Под ключом “DriverName” должно быть как минимум два следующих значения:
+ ImagePath - строка в формате "\??\C:\path\to\driver.sys"
+ Type - DWORD, который должен быть установлен в "1"
Обратите внимание, что формат параметра "DriverServiceName" предположительно (согласно документации) должен начинаться с "\Registry\Machine", который является ссылкой на раздел реестра HKLM, к которому мы предполагаем, что у нас нет доступа. Чтобы обойти это препятствие, мы можем вместо этого использовать путь, который указывает на HKCU (HKEY_CURRENT_USER), например "\Registry\User\S-1-5-21-582075628-3447520101-2530640108-1003\". Здесь числовой идентификатор в строке - это RID для нашего текущего пользователя. Мы должны сделать это, потому что ядро не знает, что такое HKCU, поскольку это просто помощник уровня пользователя, который указывает на куст HKLM.
Так как "System\CurrentControlSet\Services\DriverName" не существует по этому пути, мы должны его создать, а это мы можем сделать, так как это куст реестра текущего пользователя. Формат загрузки простого драйвера довольно прост. Мы должны, как минимум, определить две вещи:
C:
+ REG_DWORD Type
+ REG_SZ ImagePath
Тип определяет службу, как указано в wdm.h:
C:
#define SERVICE_KERNEL_DRIVER 0x00000001
#define SERVICE_FILE_SYSTEM_DRIVER 0x00000002
#define SERVICE_ADAPTER 0x00000004
#define SERVICE_RECOGNIZER_DRIVER 0x00000008
[....]
В нашем случае мы будем использовать тип SERVICE_KERNEL_DRIVER.
Как только эти значения установлены, мы можем затем вызвать функцию NtLoadDriver с путем к нашему ключу реестра и загрузить драйвер в ядро.
Есть несколько других стратегий, которые можно использовать для загрузки драйверов режима ядра, таких как ключ FltMgr или condrv, но они не могут быть использованы без административных привилегий.
3.1.8 — Привилегия SeTakeOwnershipPrivilege
Эта привилегия оскорбительно похожа на SeRestorePrivilege. Согласно MSDN, она позволяет процессу "вступать во владение объектом без предоставления дискреционного доступа", предоставляя право доступа WRITE_OWNER. Эксплуатация этой привилегии очень похоже на SeRestorePrivilege, за исключением того, что сначала мы должны стать владельцем раздела реестра, в который мы хотим записать.
Чтобы стать владельцем раздела реестра, необходимо создать ACL с обновленным владельцем, а затем изменить DACL, чтобы мы могли писать в него. Для построения ACL требуется создание объекта EXPLICIT_ACCESS:
C:
ea[0].grfAccessPermissions = KEY_ALL_ACCESS;
ea[0].grfAccessMode = SET_ACCESS;
ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_USER;
ea[0].Trustee.ptstrName = (LPTSTR)user->User.Sid; // owner
Затем мы можем использовать SetEntriesInAcl для создания объекта ACL. Как только объект ACL составлен, мы вступаем во владение путем реестра:
C:
SetNamedSecurityInfo(_TEXT("MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options"), SE_REGISTRY_KEY, OWNER_SECURITY_INFORMATION, user->User.Sid, NULL, NULL, NULL);
Как только у нас есть ключ реестра, мы делаем последний вызов SetNamedSecurityInfo, чтобы включить ранее составленный ACL и получить права на запись для записи:
C:
SetNamedSecurityInfo(_TEXT("MACHINE\\SOFTWARE\\Microsoft\\WindowsNT\\CurrentVersion\\Image File Execution Options"), SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, NULL, NULL, pACL, NULL);
На этом этапе мы можем выполнить те же шаги, что и с помощью метода SeRestorePrivilege, и убедиться, что восстановление реестра уже завершено.
Подобно SeRestorePrivilege, мы также можем контролировать критические системные файлы или папки, чтобы злоупотреблять порядком загрузки DLL или другими подобными методами.
3.1.9 — Привилегия SeDebugPrivilege
Привилегия SeDebugPrivilege очень мощная, он позволяет владельцу отлаживать другой процесс, включая чтение и запись в память этого процесса. Эта привилегия в течение многих лет широко использовалась авторами вредоносных программ и разработчиками эксплойтов, и поэтому многие из методов, которые можно было бы использовать для получения EoP с помощью этой привилегии, будут отмечены современными решениями для защиты конечных точек.
Существует множество различных стратегий внедрения памяти, которые можно использовать с этой привилегией, которые обходят большинство решений AV/HIPS. Их поиск оставлен в качестве упражнения для читателя.
3.2 - Эксплуатация Частичной Записи
Понимая, как можно использовать индивидуальные привилегии, мы можем теперь начать демонстрировать, как и почему они могут быть полезны для нас. Тематические исследования будут обсуждаться далее в разделе 4.
Этот метод был рожден в результате попыток уклониться от различных изменений ядра и пользовательского пространства с помощью частичной эксплуатации записи памяти.
Т.е. частичная запись - это инструкция, в которой контролируется пункт контролируется, но записанное значение может и не контролироваться. Или значение может быть однобитовой или однобайтовой модификацией, такой как уменьшение или сложение. Взять, к примеру, инструкцию DEC; если адрес получателя может контролироваться, то у нас есть возможность произвольно уменьшить любой адрес на один. Инструкция OR является еще одним примером; в то время как мы можем контролировать назначение, в котором мы выполняем операцию OR, мы не можем контролировать значение, в котором мы используем инструкцию OR: OR DWORD PTR[controlled], 4.
Как правило, для их эксплуатации потребуется изменить поля объекта, такие как длина, чтобы получить произвольный или частичный примитив для чтения/записи. Другие могут вносить изменения в таблицы страниц или искажать другие структуры данных режима ядра. Это все части большой цепочки эксплойтов, которая обычно заканчивается обнулением в ACL, обменом токенов процесса или выполнением привилегированного процесса пользовательского режима. Привилегированное исполнение (см.: полезная нагрузка) изменяется в зависимости от условий защиты.
Вместо того, чтобы пытаться выполнить ROP в ядре или манипулировать объектами ядра, почему бы не делегировать повышение привилегий пользователю? Подобно стратегии обмена процессами, если мы сможем включить повышенные привилегии в пользовательском пространстве, мы выиграем от повышения надежности, тривиального обхода защиты ядра и повышенной гибкости в доставке некоторой вредоносной полезной нагрузки. Мы хотим оставаться в системе надолго, и для этого мы должны оставаться в тайне.
Как обсуждалось в разделе 1.2, каждый токен имеет структуру _SEP_TOKEN_PRIVILEGES, содержащую те привилегии, которые токен имеет в настоящее время. Наш путь к повышению должен быть довольно очевидным: злоупотребляя частичной записью и утечкой информации из пользовательского пространства, мы можем перебросить несколько битов в нашем токене процесса и получить доступ к административным привилегиям. Поскольку мы часто не можем контролировать записываемое значение, важно, чтобы все возможные значения учитывали повышение привилегий. Мы определили три байта, в которых не предоставляются административные привилегии: 0x00, 0x40, 0x41. То есть, если бы вы получили MOV BYTE PTR [controlled], примитив 0x41, вы не смогли бы разрешить эксплуатируемую привилегию. Это предполагает инструкцию MOV для битовых масок привилегий по умолчанию; другие операции или комбинации привилегий могут предоставлять повышенные привилегии. Все остальные значения предоставляют доступ к эксплуатируемой привилегии.
Чтобы заполучить этот токен процесса, мы должны иметь возможность получить адрес токена контролируемого процесса. Поскольку в этой стратегии мы нацелены на уязвимости, связанные с повышением привилегий, мы можем использовать и продолжать использовать (начиная с v1703) общий API-интерфейс, т.е. функцию NtQuerySystemInformation для утечки нашего адреса токена процесса, как показано ниже:
C:
NtQuerySystemInformation(16, bHandleInfo, sizeof(bHandleInfo), &BytesReturned));
PSYSTEM_HANDLE_INFORMATION shiHandleInfo = (PSYSTEM_HANDLE_INFORMATION)bHandleInfo;
PSYSTEM_HANDLE_TABLE_ENTRY_INFO hteCurrent = &shiHandleInfo > Handles[0];
for (i = 0; i<shiHandleInfo>NumberOfHandles; hteCurrent++, i++)
{
if(hteCurrent>UniqueProcessId == dwPid && hteCurrent>HandleValue == (USHORT)hToken)
return hteCurrent>Object;
}
Значение dwPid - это процесс, в котором существует токен, а hToken - дескриптор токена процесса. Обратите внимание, что в Windows 8.1 эта стратегия больше не работает в процессах с низкой целостностью, и поэтому необходимо будет использовать другие методы. Для краткости и ясности мы будем работать в предположении, что мы нацелены на повышение привилегий из процесса средней целостности (по умолчанию IL для пользователей) и полагаемся на вышеописанный метод.
3.3 - Злоупотребление существующими учетными записями служб
В дополнение к тому, что они полезны для локального повышения привилегий через произвольные примитивы записи в ядре, эти же методы могут быть полезны в более распространенном сценарии, когда злоумышленник обращается к компьютеру как к локальной учетной записи службы. Существует ряд распространенных сценариев, в которых злоумышленник может выполнить код в контексте учетной записи службы на целевом компьютере, включая следующие:
+ Сам сервис скомпрометирован из-за некоторой уязвимости. Типичные сценарии включают в себя уязвимости веб-приложений, которые допускают выполнение в контексте учетной записи, на которой запущен IIS, и уязвимости внедрения SQL, где XP_CMDSHELL можно использовать для запуска кода в контексте учетной записи службы SQL.
+ Учетные данные учетной записи службы утекли
+ Атаки в стиле Kerberoast. Билет Kerberos запрашивается для целевой учетной записи с контроллера домена. Часть этого билета зашифрована с использованием хэша пароля целевой учетной записи. Это может быть эффективно взломано в автономном режиме, чтобы получить пароль учетной записи.
В любом из этих сценариев, если учетная запись службы имеет одну из привилегий, описанных в предыдущем разделе, можно получить локальное повышение привилегий, просто используя соответствующий модуль из этого проекта.
3.3.1 - Общие учетные записи служб
В этом разделе, мы дадим краткие примеры некоторых общих учетных записей служб, которые могут использоваться для EoP из-за их привилегий токена по умолчанию.
3.3.1.2 - MSSQL / IIS
Если мы рассмотрим привилегии по умолчанию, назначенные учетным записям служб MSSQL и IIS с помощью инструмента "AccessChk" от Sysinternals, мы обнаружим следующее:
+ IIS - SeImpersonatePrivilege — BUILTIN\IIS_IUSRS
+ MSSQL - SeAssignPrimaryTokenPrivilege - NT SERVICE\SQLAgent$SQLEXPRESS, NT SERVICE\MSSQLLaunchpad$SQLEXPRESS, NT SERVICE\MSSQL$SQLEXPRESS
Этих прав достаточно для EoP, используя модули в этом проекте. Компрометация этих учетных записей - очень распространенный сценарий тестирования на проникновение. Каждый раз, когда SQL-инъекция в MSSQL или уязвимость веб-приложения в IIS используются для выполнения команд, злоумышленники получают эти привилегии. Традиционно это считалось ограничивающим сценарием с ограниченной локальной учетной записью, и злоумышленнику необходимо будет использовать другой метод для EoP. Используя методы, описанные в этой статье, это просто вопрос злоупотребления существующими привилегиями токена.
3.3.1.3 - Продукты для резервного копирования
Каждый коммерческий продукт для резервного копирования на рынке будет работать с какими-то повышенными привилегиями. Во многих случаях учетная запись службы резервного копирования будет работать с привилегиями SYSTEM, что делает ненужным EoP. Там, где администраторы начали совершенствоваться, мы начинаем видеть, что привилегии для этих учетных записей становятся более ограниченными.
Ниже приведены минимальные привилегии, необходимые для решения Veritas NetBackup, беззастенчиво заимствованные с их веб-сайта (https://www.veritas.com/support/en_US/article.TECH36718):
+ * Действовать как часть операционной системы (только для Windows Server 2000).
+ * Создать маркерный объект.
+ Войти как сервис.
+ Вход в систему как пакетное задание.
+ Управление аудитом и журналом безопасности.
+ * Резервное копирование файлов и каталогов.
+ * Восстановление файлов и каталогов.
Обратите внимание на 4 пункт в списке, которые мы пометили звездочкой (*).
Любая из этих привилегий в отдельности может быть использована для EoP с помощью одного из методов, описанных в этом проекте.
3.3.1.4 - Локальные сервисные аккаунты
На каждом компьютере Windows также есть предопределенные учетные записи служб, которые содержат привилегии, которые можно использовать для EoP. Это "NT AUTHORITY\SERVICE", "NT AUTHORITY\NETWORK SERVICE" и "NT AUTHORITY\LOCAL SERVICE".
Каждый из них имеет несколько отличающиеся привилегии, некоторые содержат несколько эксплуатируемых привилегий, однако все они имеют доступ к эксплуатируемому SeImpersonatePrivilege.
Если злоумышленник каким-то образом может получить доступ к системе в контексте одной из этих ограниченных локальных учетных записей, он может тривиально повысить свои привилегии до "NT AUTHORITY\SYSTEM", используя методы, описанные выше.
4 - Примеры разработки ядра эксплойтов
Теперь мы подробно рассмотрим несколько тематических исследований, которые демонстрируют, как и почему мы можем захотеть делегировать привилегированную эксплуатацию пользователю. Обратите внимание, что наша стратегия применяется к повышению привилегий; в своем текущем состоянии она не может использоваться дистанционно.
4.1 — MS16-135
MS16-135 - это ошибка, которую мы впервые написали для эксплуатации этой стратегии, и она оказалась фантастическим примером. Первоначально выпущенный Google после выявления активной эксплуатации в дикой среде ( https://security.googleblog.com/2016/10/disclosing-vulnerabilities-to-protect.html), триггер был быстро выпущен, и началась гонка за вооружение.
Одним из первых публичных демонстраций эксплуатации провел Enrique Nissim на Zero Nights 2016 (https://github.com/IOActive/I-know-where-your-page-lives/), в котором он использовал ошибку, чтобы продемонстрировать слабости рандомизации PML4. Несколько других POC последовали за ним, большинство из них злоупотребляли стратегией PML4, другие использовали технику pvscan0. Ошибка может быть вызвана через функцию SetWindowLongPtr со специально созданным окном и значением индекса. Результатом является управляемая операция OR: OR DWORD PTR[controlled], 4. Первым шагом является определение адреса _SEP_TOKEN_PRIVILEGES. Это может быть достигнуто с помощью следующего кода:
C:
OpenProcessToken(OpenProcess(PROCESS_QUERY_INFORMATION, 1, GetParentProcessId()), TOKEN_QUERY | TOKEN_QUERY_SOURCE, ¤t_token);
dwToken = (UINT)current_token & 0xffff;
_TOKEN = GetHandleAddress(GetCurrentProcessId(), dwToken);
startTokenOffset = (UINT)_TOKEN + 0x40;
Сначала мы открываем дескриптор для родительского токена процесса, а затем используем функцию-обертку NtQuerySystemInformation для извлечения фактического адреса токена. Структура, за которой мы следуем, находится на 0x40 байтов впереди. Обратите внимание, что утечка функции NtQuerySystemInformation работает только из процессов средней целостности в Windows 8.1+. Чтобы эксплуатировать это в процессе с низким уровнем целостности, нам нужно использовать другую утечку (https://github.com/sam-b/windows_kernel_address_leaks ). Используя адрес токена, мы теперь корректируем смещение для включенной битовой маски и вызываем следующий код:
C:
ULONG enabled_create_token = startTokenOffset + 0xa;
SetWindowLongPtr(childWnd, GWLP_ID, (LONG)(enabled_create_token — 0x14));
Если мы сгенерируем эту ошибку несколько раз при сдвиге смещения, мы можем изменить каждый байт в битовой маске Enabled.
Это включает следующие привилегии:
C:
02 0x000000002 SeCreateTokenPrivilege Attributes - Enabled
10 0x00000000a SeLoadDriverPrivilege Attributes - Enabled
18 0x000000012 SeRestorePrivilege Attributes - Enabled
23 0x000000017 SeChangeNotifyPrivilege Attributes — Enabled
Как мы уже видели, три из четырех включенных привилегий тривиально эксплуатированы. Наш POC использует злоупотребление SeRestorePrivilege, и его можно найти в репозитории проекта git (https://github.com/hatRiot/token-priv).
Хотя многие публичные эксплойты для этой ошибки были написаны таким образом, чтобы продемонстрировать технику, мы обнаруживаем, что представленный пример подчеркивает простоту и надежность нашей стратегии: он опирается только на внешнюю необходимость утечки адреса токена из ядра. Публичный POC сложнее и требует разнообразных примитивных танцев c бубном над ядром.
4.2 — MS15-061
Это была еще одна фантастическая ошибка, наблюдаемая в дикой среде во время вирусной кампаний RussianDoll, и она была быстро исправлена и взята на вооружение в сообществе. Как и MS16-135, это частичная перезапись, которая позволяет уменьшить управляемый адрес. Эта ошибка использовала UAF в win32k, в частности еще одна проблема с обратными вызовами пользовательского режима, исходящими из в пределах win32k (https://community.rapid7.com/community/metasploit/blog/2015/10/01/flipping-bits).
Наша стратегия здесь не сильно отличается от примера в 4.1: мы идентифицируем наш адрес токена, собираем кучу для получения нашего произвольного уменьшения и запускаем его несколько раз, чтобы покрыть битовые маски Enabled и Present. Это включает целый ряд различных привилегий, а именно:
C:
07 0x000000007 SeTcbPrivilege Attributes - Enabled
09 0x000000009 SeTakeOwnershipPrivilege Attributes - Enabled
10 0x00000000a SeLoadDriverPrivilege Attributes - Enabled
17 0x000000011 SeBackupPrivilege Attributes - Enabled
18 0x000000012 SeRestorePrivilege Attributes — Enabled
Всего доступно 14 привилегий; сразу же часто используемые показаны выше. Подтверждение концепции снова предоставлено в репозитории git проекта (https://github.com/hatRiot/token-priv).
Публичные образцы используют различные стратегии; один из первых, выпущенных NCC, использует более старую технику из Pwn2Own 2013, в которой шелл-код хранится в структуре tagWND, а бит ServerSideWindowProc уменьшается до тех пор, пока не будет перенесен, что означает, что оконная процедура tagWND будет выполняться без переключения контекста. Шелл-код использовал обнуление из ACL-списка winlogon и внедрялся в привилегированный процесс. В других примерах используется более современная стратегия обмена токенами процесса.
Дело в том, что нам не нужно выполнять какой-либо шелл-код, и нам не нужно работать для получения каких-либо других примитивов.
4.3 — HEVD
HEVD, или HacksysExtremeVulnerableDriver (https://github.com/hacksysteam/HackSysExtremeVulnerableDriver), является специальным уязвимым драйвером для Windows, который можно загрузить в систему для изучения и исследования различных стратегий и методов эксплуатации. Кроме того, он предоставляет простой способ демонстрации стратегий уклонения от безопасности в современных системах без использования 0days.
Мы демонстрируем нашу технику, используя произвольную ошибку записи, присутствующую в HEVD, инициируемую с помощью управляющего кода 0x22200b. Как уже упоминалось, "ошибка" является преднамеренным и управляемым примитивом write-what-where в драйвере. Ниже находтся код для краткости
C:
NTSTATUS TriggerArbitraryOverwrite(IN PWRITE_WHAT_WHERE UserWriteWhatWhere)
{
What = UserWriteWhatWhere -> What;
Where = UserWriteWhatWhere -> Where;
*(UserWriteWhatWhere->Where) = *(UserWriteWhatWhere->What);
}
Есть несколько публичных демонстраций эксплуатации этого; большинство для Windows 7, несколько для Windows 10 build 1607. Один от хакера Cn33liz (https://github.com/Cn33liz/HSEVD-ArbitraryOverwriteGDI ) демонстрирует эксплуатацию этой ошибки с помощью технологии GDI Reloaded, представленной на Ekoparty №16. Стратегия Reloaded улучшает первоначальную стратегию GDI pvscan, включая обход исправления KASLR таблицы общих дескрипторов GDI, представленного в v1607, за счет утечки адресов ядра через глобальную таблицу gSharedInfo (старая, но явно все еще жизнеспособная утечка).
В релизе v1703 (Creators Update) структура таблицы была изменена, а утечки адресов удалены. Таким образом, техника GDI Reloaded все еще работает, при условии, что мы можем идентифицировать другую утечку KASLR.
Последний пример примечания взят из GradiusX, который демонстрирует использование в системе Windows 10 v1703. В примере используется примитив GDI rw для утечки структуры EPROCESS из текущего процесса, который затем можно использовать для утечки адреса токена запущенного процесса. После утечки адреса он теряет адрес токена SYSTEM и переписывает, используя примитив GDI, свой резидентный токен токена SYSTEM. Таким образом, токены поменяется местами. Использование примитива GDI для утечки структуры EPROCESS (через _THREADINFO) позволяет ему обойти необходимость отдельной утечки KASLR и, следовательно, должно нормально работать из-за процессов с низкой целостностью.
Наша стратегия здесь довольно проста; нет необходимости собирать кучу, выполнять шеллкод или устанавливать примитивы rw. Из-за изменений в структуре _SEP_TOKEN_PRIVILEGES мы должны выполнить два отдельных вызова DeviceIoControl; один для перезаписи маски Enabled и один для маски Present. Как и в предыдущих примерах, мы получаем адрес токена и смещения:
C:
uToken = (USHORT)current_token & 0xffff;
_TOKEN = GetHandleAddress(GetCurrentProcessId(), uToken);
startTokenOffset = (ULONG)_TOKEN + 0x40;
enabled_offset = (ULONG)_TOKEN + 0x48;
Затем мы устанавливаем наши примитивы what/where (используя структуру PWRITE_WHAT_WHERE, как определено HEVD):
C:
pww = (PWRITE_WHAT_WHERE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WRITE_WHAT_WHERE));
pww->What = (PULONG_PTR)what;
pww->Where = (PULONG_PTR)startTokenOffset;
Затем просто запустите драйвер через функцию DeviceIoControl. Мы выполняем это во второй раз с флагом enabled_offset . Теперь у нас включены повышенные привилегии на токене.
5 — Заключение
Поскольку Microsoft продолжает развивать и совершенствовать базовые стратегии защиты в Windows, как в пользовательском пространстве, так и в ядре, злоумышленники дополнительно продолжают развиваться. Представленные здесь идеи не могут быть прорывом в наступательной эксплуатации ядра, но мы полагаем, что они дают представление о том, куда движутся методы эксплуатации. Навстречу логике, вдали от ядра, на земле, где приложения и скрипты и документы Office резвятся с критическими, привилегированными элементами управления. Мы продемонстрировали, как тривиально может быть эксплуатируемо rite-what-where, предсказуемым и стабильным способом, используя только информации из ядра.
Нападение на индивидуальные привилегии представляется логичным развитием существующих тенденций: замена токенов, искажение DACL, запись в привилегированные файлы. Ограничение области привилегированного доступа повышает общую эффективность и снижает вероятность атаки.
В идеале, мы можем определить другие такие привилегии, которыми следует злоупотреблять, поскольку они настолько же обильны, насколько и непрозрачны. Структура EPROCESS содержит флаги и маски, управляющие различными механизмами доступа в ядре. Мы касались имперсонализации(олицетворения) токенов с разделенным доступом, потоков и других подобных средств, но их примитивный потенциал почти гарантирован. Да будет проклято это чертово ядро Windows.
6 — Приветы
Людей слишком много, но основных я перечислю ниже:
themson, bannedit, quitos, tiraniddo, aionescu, phrack, pastor laphroaig, shellster
Источник: https://www.exploit-db.com/exploits/42556
Автор перевода: yashechka
Специально для xss.pro (c)