В первой части этой серии мы начали наше погружение в Mimikatz. Идея была проста - показать, как Mimikatz творит свою магию, позволяя разрабатывать специальные полезные нагрузки.
Если если вы ещё не читали то, загляните сюда. Продолжая, в этом посте мы рассмотрим то, что стало хорошим способом подрыва средств контроля безопасности, добавленных Microsoft для предотвращения сброса учетных данных
(таких как Credential Guard), а также извлечения учетных данных, предоставленных жертвой. Это, конечно, поддержка Mimikatz SSP.
Провайдер поддержки безопасности (SSP) - это библиотека DLL, которая позволяет разработчикам предоставлять ряд обратных вызовов, которые будут вызываться во время определенных событий аутентификации и авторизации.
Как мы видели в предыдущем посте, WDigest предоставляет учетные данные для кеширования с использованием именно этого интерфейса.
Mimikatz предлагает несколько различных методов использования SSP. Во-первых, это «Mimilib», библиотека DLL, обладающая различными функциями, одна из которых реализует интерфейс поставщика поддержки безопасности.
Во-вторых, есть memssp, который представляет собой интересный способ достижения той же цели, но полагается на изменение памяти, а не на загрузку DLL.
Начнем с изучения традиционного способа загрузки SSP, Mimilib.
Примечание. Как упоминалось в предыдущем посте, в этой статье широко используется исходный код Mimikatz, а также бесчисленные часы, посвященные ему разработчиками.
Спасибо Mimikatz, Benjamin Delpy и Vincent Le Toux за их потрясающую работу.
как эта библиотека действует как поставщик общих служб, предоставляя злоумышленникам возможность получить учетные данные по мере их ввода жертвой.
Mimilib работает, используя тот факт, что поставщик поддержки безопасности вызывается с учетными данными в виде открытого текста через интерфейс SSP. Это означает, что учетные данные могут быть извлечены в открытом виде.
Точка входа для функциональности Mimilib SSP находится в kssp.c, в частности
используется lsass для инициализации структуры, содержащей несколько обратных вызовов.
В случае Mimilib зарегистрированными обратными вызовами являются:
После заполнения каждого обратного вызова и зная, что
что и делает
Я не верю, что mimikatz.exe поддерживает загрузку Mimilib напрямую, но мы знаем из документации Microsoft, что SSP добавляется путем добавления ключа реестра и перезагрузки.
Однако после некоторых поисков я нашел этот твит:
Это, конечно, ссылка на API AddSecurityPackage, который фактически используется в сценарии Install-SSP.ps1 @mattifestation для загрузки SSP, а это означает, что Mimilib фактически может быть добавлен без перезагрузки.
И при загрузке мы обнаруживаем, что каждая попытка аутентификации вызывает запись учетных данных в файл kiwissp.log:
Теперь одним из недостатков работы с SSP в зрелой среде является тот факт, что SSP должен быть зарегистрирован в lsass. Это дает защитникам ряд артефактов, с которыми можно работать при попытке отследить вашу вредоносную активность,
будь то ключи реестра, созданные для ссылки на SSP, или просто необычная DLL, находящаяся в процессе lsass. Мы также видим, что SSP предоставляют как имя, так и комментарий, которые можно перечислить с помощью функции
Как мы видим ниже, в выходных данных отображается информация о каждом загруженном SSP, что означает, что Mimilib может немного выделяться:
Итак, что мы можем сделать, чтобы не выделяться? Что ж, очевидно, что нужно просто изменить описание, возвращаемое обратным вызовом Mimilib в SpGetInfo, которое жестко закодированно:
Итак, мы меняем поля Name и Comment и тада:
Хорошо, очевидно, это все еще не очень хорошо (даже с нашими полями имени и комментариев).
И помните, что без удаления и повторной компиляции Mimilib содержит множество функций, помимо работы в качестве SSP.
Так как же обойти это? Что ж, к счастью, Mimikatz также поддерживает
Давайте посмотрим на функцию, с которой все это начинается, kuhl_m_misc_memssp. Здесь мы видим, что процесс lsass открывается, и начинается поиск библиотеки DLL
которая представляет собой пакет аутентификации, поддерживающий интерактивную аутентификацию:
Далее идет поиск шаблона в памяти, опять же, аналогично тому, что мы видели с WDigest:
Если мы остановим наш обзор кода и перейдем в Ghidra, мы сможем найти используемый шаблон, который приведет нас сюда:
Здесь мы раскрываем, что на самом деле происходит ... memssp используется для перехвата функции
как будет выглядеть ловушка после добавления.
Сначала мы подтверждаем, что
Затем, по мере выполнения, мы обнаруживаем, что попали в заглушку, отвечающую за создание файла журнала, путем создания имени файла в стеке и передачи его в fopen:
После открытия учетные данные, переданные в
Наконец, выполнение возвращается к
Если вы хотите увидеть код для этой ловушки, фактический источник можно найти в функции misc_msv1_0_SpAcceptCredentials файла
Итак, каковы наши риски использования этой техники? Что ж, мы видим, что вышеуказанный хук копируется в lsass через
В зависимости от среды вызов
Теперь одно из преимуществ изучения техник Mimikatz - это возможность изменить профиль взаимодействия с lsass, что немного усложняет задачу BlueTeam,
чтобы указать на их волшебство обнаружения и сказать: «А, я видел эту цепочку события до этого, это Mimikatz !! ". Итак, давайте посмотрим, что мы можем сделать, чтобы немного запутать ситуацию.
Первый метод (Mimilib) основан на регистрации SSP, который можно выявить, вернув список зарегистрированных поставщиков через
в нее входит множество дополнительных функций. Кроме того, при загрузке с помощью
что он не требует потенциально опасного вызова API
Второй метод (memssp) сильно зависит от отслеживаемых вызовов API, таких как
что он не отображается в списке зарегистрированных SSP или как загруженная DLL.
Итак, что мы можем сделать, чтобы немного изменить ситуацию? Что ж, мы потенциально можем объединить эти два метода для загрузки нашего кода с помощью
избегая появления в качестве зарегистрированного поставщика общих служб. А как насчет того, чтобы найти способ избежать прямого вызова API
который решает перехватить эту функцию.
Давайте начнем с рассмотрения того, как
Открывая это в Ghidra, мы быстро видим, что на самом деле это просто оболочка для вызова sspcli.dll:
Дизассемблируя
Это имеет смысл, поскольку каким-то образом этот вызов должен сигнализировать lsass, что должен быть загружен новый SSP:
Следуя вызову NdrClientCall3, мы обнаруживаем, что переданы следующие параметры:
Это дает нам значение параметра
Теперь у нас достаточно информации, чтобы зайти в RpcView и показать вызов RPC как
Чтобы использовать этот вызов, нам нужно знать переданные параметры, которые, конечно же, можно восстановить из RpcView:
Однако, прежде чем мы сможем реализовать этот вызов, нам нужно будет знать значение, передаваемое в качестве параметра arg_2 (arg_1 помечен как размер arg_2, а arg_3, arg_4 и arg_5 помечены как «out»).
Самый простой способ, который я нашел для этого, - запустить отладчик и добавить точку останова непосредственно перед тем, как AddSecurityPackage выполнит свой вызов NdrClientCall3:
Однако, прежде чем мы сможем реализовать этот вызов, нам нужно будет знать значение, передаваемое в качестве параметра arg_2 (arg_1 помечен как размер arg_2, а arg_3, arg_4 и arg_5 помечены как «out»).
Самый простой способ, который я нашел для этого, - запустить отладчик и добавить точку останова непосредственно перед тем, как
После того, как мы приостановили выполнение, мы можем дапмануть значения, переданные в каждом параметре. Давайте возьмем размер буфера, передаваемого в этом параметре arg_1, используя
Итак, мы знаем, что в этом случае размер передаваемого буфера составляет
Немного покопавшись, я смог сопоставить большинство этих значений. Давайте переформатируем выведенный запрос как QWORD и разметим все, чтобы мы могли видеть, с чем имеем дело:
Теперь, когда мы сопоставили большую часть передаваемых данных, мы можем попытаться выполнить вызов RPC, не вызывая напрямую вызов API
Код, который я для этого создал, доступен через Gist здесь.
Имея возможность загружать пакет без прямого вызова
Давайте закинем
Сразу после дизассемблирования SspirCallRpc мы сталкиваемся с тем, что выполнение передается через
Фактически это указатель на массив функций, заполняемый через lsasrv.dll и указывающий на
Нас интересует
Опять же, для отправки вызова используется еще один массив указателей на функции, на который указывает
Теперь это массив, который должен нас заинтересовать, поскольку мы, вероятно, ищем s_AddPackage на основе его имени, а индекс также соответствует индексу 0xb «ID функции», который мы нашли в запросе.
Двигаясь дальше по кроличьей норе, мы попадаем в
а затем пытается открыть раздел реестра
Если это удастся (обратите внимание, что это потенциально новый бэкдор для privesc
, то выполнение переходит на
Если SSP загружен успешно, DLL добавляется в реестр автозагрузки:
Таким образом, мы, вероятно, захотим пропустить этот последний бит, поскольку мы не будем использовать его для сохранения, и было бы неплохо не трогать реестр, где мы можем помочь. Мы также в идеале не хотим,
чтобы наша DLL указывалась как присутствующая в lsass чем-то вроде
возвращая FALSE из его DllMain. Это приведет к пропуску изменения реестра, а также будет означать, что наша DLL выгружается из процесса.
Используя Mimikatz memssp в качестве нашего шаблона, я создал DLL для загрузки через наш вызов RPC, который будет исправлять
Это доступно через Gist здесь.
Давайте посмотрим, как загружается наша DLL с помощью нашего raw вызова RPC
Вы также не ограничены загрузкой DLL из локальной системы с этим, поскольку пути UNC работают нормально, если передаются через вызов RPC (хотя вы должны убедиться, что EDR, с которым вы столкнулись, не помечает это как подозрительное).
Конечно, вы также не ограничены загрузкой этой DLL с помощью
давайте возьмем наш SAMR RPC-скрипт из предыдущего сообщения в блоге и загрузим нашу DLL через
Конечно, есть несколько способов повысить эффективность этих примеров, но, как и в случае с Частью 1, я надеюсь, что этот пост дал вам представление о том, как создать свой собственный SSP, чтобы брать его с собой.
Хотя в этом посте рассматривается только несколько возможных способов смешать вещи при загрузке SSP в lsass, понимая, как Mimikatz может предоставить эту функциональность, вы, надеюсь,
сможете адаптировать свою полезную нагрузку к среде при попытке обойти AV. или EDR, или просто для проверки возможности обнаружения BlueTeam помимо отметки Mimilib и memssp.
От ТС
Эта статья это перевод, материала взятого тут
Первая часть уже есть на форуме
PS: Хочу похвалить новое окно предпросмотра, очень удобно
Перевод:
Azrv3l cпециально для xss.pro
Если если вы ещё не читали то, загляните сюда. Продолжая, в этом посте мы рассмотрим то, что стало хорошим способом подрыва средств контроля безопасности, добавленных Microsoft для предотвращения сброса учетных данных
(таких как Credential Guard), а также извлечения учетных данных, предоставленных жертвой. Это, конечно, поддержка Mimikatz SSP.
Провайдер поддержки безопасности (SSP) - это библиотека DLL, которая позволяет разработчикам предоставлять ряд обратных вызовов, которые будут вызываться во время определенных событий аутентификации и авторизации.
Как мы видели в предыдущем посте, WDigest предоставляет учетные данные для кеширования с использованием именно этого интерфейса.
Mimikatz предлагает несколько различных методов использования SSP. Во-первых, это «Mimilib», библиотека DLL, обладающая различными функциями, одна из которых реализует интерфейс поставщика поддержки безопасности.
Во-вторых, есть memssp, который представляет собой интересный способ достижения той же цели, но полагается на изменение памяти, а не на загрузку DLL.
Начнем с изучения традиционного способа загрузки SSP, Mimilib.
Примечание. Как упоминалось в предыдущем посте, в этой статье широко используется исходный код Mimikatz, а также бесчисленные часы, посвященные ему разработчиками.
Спасибо Mimikatz, Benjamin Delpy и Vincent Le Toux за их потрясающую работу.
Mimilib
Mimilib - своего рода хамелеон, он поддерживаетServerLevelPluginDll для бокового перемещения по RPC, DHCP Server Callout и даже действует как расширение в WinDBG. Однако для наших целей мы рассмотрим,как эта библиотека действует как поставщик общих служб, предоставляя злоумышленникам возможность получить учетные данные по мере их ввода жертвой.
Mimilib работает, используя тот факт, что поставщик поддержки безопасности вызывается с учетными данными в виде открытого текста через интерфейс SSP. Это означает, что учетные данные могут быть извлечены в открытом виде.
Точка входа для функциональности Mimilib SSP находится в kssp.c, в частности
kssp_SpLsaModeInitialize. Эта функция экспортируется из библиотеки DLL как SpLsaModeInitialize через файл определения mimilib.def ииспользуется lsass для инициализации структуры, содержащей несколько обратных вызовов.
В случае Mimilib зарегистрированными обратными вызовами являются:
- SpInitialize - используется для инициализации SSP и предоставления списка указателей на функции.
- SpShutDown - вызывается при выгрузке SSP, что дает возможность освободить ресурсы.
- SpGetInfoFn - предоставляет информацию о SSP, включая версию, имя и описание.
- SpAcceptCredentials - получает учетные данные в виде открытого текста, переданные LSA для кэширования поставщиком общих служб.
SpAcceptCredentials используется WDigest для кэширования учетных данных, что приводит к слабости, которой мы все наслаждаемся в течение многих лет.После заполнения каждого обратного вызова и зная, что
SpAcceptCredentials будет вызываться с копией учетных данных в открытом виде, все, что остается Mimilib - это сохранять учетные данные в том виде, в каком они предоставлены,что и делает
kssp_SpAcceptCredentials:
C:
NTSTATUS NTAPI kssp_SpAcceptCredentials(SECURITY_LOGON_TYPE LogonType, PUNICODE_STRING AccountName, PSECPKG_PRIMARY_CRED PrimaryCredentials, PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials)
{
FILE *kssp_logfile;
#pragma warning(push)
#pragma warning(disable:4996)
if(kssp_logfile = _wfopen(L"kiwissp.log", L"a"))
#pragma warning(pop)
{
klog(kssp_logfile, L"[%08x:%08x] [%08x] %wZ\\%wZ (%wZ)\t", PrimaryCredentials->LogonId.HighPart, PrimaryCredentials->LogonId.LowPart, LogonType, &PrimaryCredentials->DomainName, &PrimaryCredentials->DownlevelName, AccountName);
klog_password(kssp_logfile, &PrimaryCredentials->Password);
klog(kssp_logfile, L"\n");
fclose(kssp_logfile);
}
return STATUS_SUCCESS;
}
Я не верю, что mimikatz.exe поддерживает загрузку Mimilib напрямую, но мы знаем из документации Microsoft, что SSP добавляется путем добавления ключа реестра и перезагрузки.
Однако после некоторых поисков я нашел этот твит:
Технически вы можете использовать AddSecurityPackage и DeleteSecurityPackage, чтобы избежать некоторых перезагрузок(но не <W7)
- ? Бенджамин Дельпи (@gentilkiwi) 5 апреля 2018 г.
Это, конечно, ссылка на API AddSecurityPackage, который фактически используется в сценарии Install-SSP.ps1 @mattifestation для загрузки SSP, а это означает, что Mimilib фактически может быть добавлен без перезагрузки.
И при загрузке мы обнаруживаем, что каждая попытка аутентификации вызывает запись учетных данных в файл kiwissp.log:
Теперь одним из недостатков работы с SSP в зрелой среде является тот факт, что SSP должен быть зарегистрирован в lsass. Это дает защитникам ряд артефактов, с которыми можно работать при попытке отследить вашу вредоносную активность,
будь то ключи реестра, созданные для ссылки на SSP, или просто необычная DLL, находящаяся в процессе lsass. Мы также видим, что SSP предоставляют как имя, так и комментарий, которые можно перечислить с помощью функции
EnumerateSecurityPackages следующим образом:
C:
#define SECURITY_WIN32
#include <stdio.h>
#include <Windows.h>
#include <Security.h>
int main(int argc, char **argv) {
ULONG packageCount = 0;
PSecPkgInfoA packages;
if (EnumerateSecurityPackagesA(&packageCount, &packages) == SEC_E_OK) {
for (int i = 0; i < packageCount; i++) {
printf("Name: %s\nComment: %s\n\n", packages[i].Name, packages[i].Comment);
}
}
}
Как мы видим ниже, в выходных данных отображается информация о каждом загруженном SSP, что означает, что Mimilib может немного выделяться:
Итак, что мы можем сделать, чтобы не выделяться? Что ж, очевидно, что нужно просто изменить описание, возвращаемое обратным вызовом Mimilib в SpGetInfo, которое жестко закодированно:
C:
NTSTATUS NTAPI kssp_SpGetInfo(PSecPkgInfoW PackageInfo)
{
PackageInfo->fCapabilities = SECPKG_FLAG_ACCEPT_WIN32_NAME | SECPKG_FLAG_CONNECTION;
PackageInfo->wVersion = 1;
PackageInfo->wRPCID = SECPKG_ID_NONE;
PackageInfo->cbMaxToken = 0;
PackageInfo->Name = L"KiwiSSP";
PackageInfo->Comment = L"Kiwi Security Support Provider";
return STATUS_SUCCESS;
}
Итак, мы меняем поля Name и Comment и тада:
Хорошо, очевидно, это все еще не очень хорошо (даже с нашими полями имени и комментариев).
И помните, что без удаления и повторной компиляции Mimilib содержит множество функций, помимо работы в качестве SSP.
Так как же обойти это? Что ж, к счастью, Mimikatz также поддерживает
misc::memssp, который предлагает хорошую альтернативу.MemSSP
MemSSP возвращается к процессу копания в памяти lsass, на этот раз путем определения и исправления функций для перенаправления выполнения.Давайте посмотрим на функцию, с которой все это начинается, kuhl_m_misc_memssp. Здесь мы видим, что процесс lsass открывается, и начинается поиск библиотеки DLL
msv1_0.dll, которая представляет собой пакет аутентификации, поддерживающий интерактивную аутентификацию:
C:
NTSTATUS kuhl_m_misc_memssp(int argc, wchar_t * argv[])
{
...
if(kull_m_process_getProcessIdForName(L"lsass.exe", &processId))
{
if(hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION, FALSE, processId))
{
if(kull_m_memory_open(KULL_M_MEMORY_TYPE_PROCESS, hProcess, &aLsass.hMemory))
{ if(kull_m_process_getVeryBasicModuleInformationsForName(aLsass.hMemory, L"msv1_0.dll", &iMSV))
{
...
Далее идет поиск шаблона в памяти, опять же, аналогично тому, что мы видели с WDigest:
Код:
...
sSearch.kull_m_memoryRange.kull_m_memoryAdress = iMSV.DllBase;
sSearch.kull_m_memoryRange.size = iMSV.SizeOfImage;
if(pGeneric = kull_m_patch_getGenericFromBuild(MSV1_0AcceptReferences, ARRAYSIZE(MSV1_0AcceptReferences), MIMIKATZ_NT_BUILD_NUMBER))
{
aLocal.address = pGeneric->Search.Pattern;
if(kull_m_memory_search(&aLocal, pGeneric->Search.Length, &sSearch, TRUE))
{
...
Если мы остановим наш обзор кода и перейдем в Ghidra, мы сможем найти используемый шаблон, который приведет нас сюда:
Здесь мы раскрываем, что на самом деле происходит ... memssp используется для перехвата функции
SpAcceptCredentials msv1_0.dll для восстановления учетных данных. Давайте перейдем к нашему отладчику и посмотрим, как будет выглядеть ловушка после добавления.
Сначала мы подтверждаем, что
SpAcceptCredentials содержит перехватчик:
Затем, по мере выполнения, мы обнаруживаем, что попали в заглушку, отвечающую за создание файла журнала, путем создания имени файла в стеке и передачи его в fopen:
После открытия учетные данные, переданные в
SpAcceptCredentials, записываются в этот файл:
Наконец, выполнение возвращается к
msv1_0.dll:
Если вы хотите увидеть код для этой ловушки, фактический источник можно найти в функции misc_msv1_0_SpAcceptCredentials файла
kuhl_m_misc.c.Итак, каковы наши риски использования этой техники? Что ж, мы видим, что вышеуказанный хук копируется в lsass через
kull_m_memory_copy, который на самом деле использует WriteProcessMemory. В зависимости от среды вызов
WriteProcessMemory в другой процесс может быть обнаружен или помечен как подозрительный, особенно если он нацелен на lsass.Теперь одно из преимуществ изучения техник Mimikatz - это возможность изменить профиль взаимодействия с lsass, что немного усложняет задачу BlueTeam,
чтобы указать на их волшебство обнаружения и сказать: «А, я видел эту цепочку события до этого, это Mimikatz !! ". Итак, давайте посмотрим, что мы можем сделать, чтобы немного запутать ситуацию.
Воссоздание memssp без WriteProcessMemory
Мы знаем, что после изучения представленных выше методов у каждого есть свои преимущества и недостатки.Первый метод (Mimilib) основан на регистрации SSP, который можно выявить, вернув список зарегистрированных поставщиков через
EnumerateSecurityPackages. Кроме того, если библиотека Mimilib не модифицируется, в нее входит множество дополнительных функций. Кроме того, при загрузке с помощью
AddSecurityProvider значения реестра будут изменены для сохранения SSP между перезагрузками. При этом одним большим преимуществом этого метода является то, что он не требует потенциально опасного вызова API
WriteProcessMemory для достижения своей цели.Второй метод (memssp) сильно зависит от отслеживаемых вызовов API, таких как
WriteProcessMemory, который используется для загрузки ловушки в lsass. Однако большим преимуществом этого метода является то, что он не отображается в списке зарегистрированных SSP или как загруженная DLL.
Итак, что мы можем сделать, чтобы немного изменить ситуацию? Что ж, мы потенциально можем объединить эти два метода для загрузки нашего кода с помощью
AddSecurityProvider, избегая появления в качестве зарегистрированного поставщика общих служб. А как насчет того, чтобы найти способ избежать прямого вызова API
AddSecurityProvider, который должен помочь обойти любой надоедливый AV или EDR, который решает перехватить эту функцию.
Давайте начнем с рассмотрения того, как
AddSecurityPackage работает для регистрации SSP, а это значит, что нам нужно будет немного изменить ситуацию. Мы начнем с DLL, предоставляющей этот API, Secur32.dll.Открывая это в Ghidra, мы быстро видим, что на самом деле это просто оболочка для вызова sspcli.dll:
Дизассемблируя
AddSecurityPackage в sspcli.dll, в частности исходящие вызовы API, используемые этой функцией, мы видим ссылки на NdrClientCall3, что означает, что эта функция использует RPC. Это имеет смысл, поскольку каким-то образом этот вызов должен сигнализировать lsass, что должен быть загружен новый SSP:
Следуя вызову NdrClientCall3, мы обнаруживаем, что переданы следующие параметры:
Это дает нам значение параметра
nProcNum равное 3, и если мы углубимся в структуру sspirpc_ProxyInfo, мы обнаружим UUID интерфейса RPC как 4f32adc8-6052-4a04-8701-293ccf2096f0:
Теперь у нас достаточно информации, чтобы зайти в RpcView и показать вызов RPC как
SspirCallRpc, доступный через sspisrv.dll:
Чтобы использовать этот вызов, нам нужно знать переданные параметры, которые, конечно же, можно восстановить из RpcView:
C:
long Proc3_SspirCallRpc(
[in][context_handle] void* arg_0,
[in]long arg_1,
[in][size_is(arg_1)]/*[range(0,0)]*/ char* arg_2,
[out]long* arg_3,
[out][ref][size_is(, *arg_3)]/*[range(0,0)]*/ char** arg_4,
[out]struct Struct_144_t* arg_5);
Однако, прежде чем мы сможем реализовать этот вызов, нам нужно будет знать значение, передаваемое в качестве параметра arg_2 (arg_1 помечен как размер arg_2, а arg_3, arg_4 и arg_5 помечены как «out»).
Самый простой способ, который я нашел для этого, - запустить отладчик и добавить точку останова непосредственно перед тем, как AddSecurityPackage выполнит свой вызов NdrClientCall3:
Однако, прежде чем мы сможем реализовать этот вызов, нам нужно будет знать значение, передаваемое в качестве параметра arg_2 (arg_1 помечен как размер arg_2, а arg_3, arg_4 и arg_5 помечены как «out»).
Самый простой способ, который я нашел для этого, - запустить отладчик и добавить точку останова непосредственно перед тем, как
AddSecurityPackage выполнит свой вызов NdrClientCall3:
После того, как мы приостановили выполнение, мы можем дапмануть значения, переданные в каждом параметре. Давайте возьмем размер буфера, передаваемого в этом параметре arg_1, используя
dq rsp + 0x20 L1
Итак, мы знаем, что в этом случае размер передаваемого буфера составляет
0xEC байтов. Теперь мы можем выгрузить arg_2 с помощью:
Немного покопавшись, я смог сопоставить большинство этих значений. Давайте переформатируем выведенный запрос как QWORD и разметим все, чтобы мы могли видеть, с чем имеем дело:
Теперь, когда мы сопоставили большую часть передаваемых данных, мы можем попытаться выполнить вызов RPC, не вызывая напрямую вызов API
AddSecurityPackage. Код, который я для этого создал, доступен через Gist здесь.
Имея возможность загружать пакет без прямого вызова
AddSecurityPackage, давайте посмотрим, сможем ли мы еще немного перемешать.Давайте закинем
sspisrv.dll в Ghidra и посмотрим, как RPC-вызов обрабатывается на стороне сервера. Сразу после дизассемблирования SspirCallRpc мы сталкиваемся с тем, что выполнение передается через
gLsapSspiExtension:
Фактически это указатель на массив функций, заполняемый через lsasrv.dll и указывающий на
LsapSspiExtensionFunctions:
Нас интересует
SspiExCallRpc, который очень похож на то, что мы нашли в RPCView. Эта функция проверяет параметры и передает выполнение в LpcHandler:
LpcHandler отвечает за дальнейшую проверку предоставленных параметров, прежде чем в конечном итоге передать выполнение на DispatchApi:
Опять же, для отправки вызова используется еще один массив указателей на функции, на который указывает
LpcDispatchTable:
Теперь это массив, который должен нас заинтересовать, поскольку мы, вероятно, ищем s_AddPackage на основе его имени, а индекс также соответствует индексу 0xb «ID функции», который мы нашли в запросе.
Двигаясь дальше по кроличьей норе, мы попадаем в
WLsaAddPackage, который сначала проверяет, достаточно ли у нас прав для вызова метода RPC, олицетворяя подключающегося клиента, а затем пытается открыть раздел реестра
HKLM\System\CurrentControlSet\Control\Lsa с помощью Read/Write привелегий:
Если это удастся (обратите внимание, что это потенциально новый бэкдор для privesc
SpmpLoadDll, который используется для загрузки предоставленного SSP в lsass через LoadLibraryExW:
Если SSP загружен успешно, DLL добавляется в реестр автозагрузки:
Таким образом, мы, вероятно, захотим пропустить этот последний бит, поскольку мы не будем использовать его для сохранения, и было бы неплохо не трогать реестр, где мы можем помочь. Мы также в идеале не хотим,
чтобы наша DLL указывалась как присутствующая в lsass чем-то вроде
ProcessExplorer, если возникает подозрение. Итак, что мы можем сделать, так это передать нашу DLL с помощью вызова RPC и заставить наш SSP отказывать при загрузке, возвращая FALSE из его DllMain. Это приведет к пропуску изменения реестра, а также будет означать, что наша DLL выгружается из процесса.
Используя Mimikatz memssp в качестве нашего шаблона, я создал DLL для загрузки через наш вызов RPC, который будет исправлять
SpAddCredentials с помощью той же ловушки, что и Mimikatz. Это доступно через Gist здесь.
Давайте посмотрим, как загружается наша DLL с помощью нашего raw вызова RPC
AddSecurityPackage:Вы также не ограничены загрузкой DLL из локальной системы с этим, поскольку пути UNC работают нормально, если передаются через вызов RPC (хотя вы должны убедиться, что EDR, с которым вы столкнулись, не помечает это как подозрительное).
Конечно, вы также не ограничены загрузкой этой DLL с помощью
AddSecurityPackage. Поскольку мы создали автономную DLL для выполнения исправлений memssp, давайте возьмем наш SAMR RPC-скрипт из предыдущего сообщения в блоге и загрузим нашу DLL через
LoadLibrary, записывая попытки входа в систему, когда они происходят через общий ресурс SMB:Конечно, есть несколько способов повысить эффективность этих примеров, но, как и в случае с Частью 1, я надеюсь, что этот пост дал вам представление о том, как создать свой собственный SSP, чтобы брать его с собой.
Хотя в этом посте рассматривается только несколько возможных способов смешать вещи при загрузке SSP в lsass, понимая, как Mimikatz может предоставить эту функциональность, вы, надеюсь,
сможете адаптировать свою полезную нагрузку к среде при попытке обойти AV. или EDR, или просто для проверки возможности обнаружения BlueTeam помимо отметки Mimilib и memssp.
От ТС
Эта статья это перевод, материала взятого тут
Первая часть уже есть на форуме
PS: Хочу похвалить новое окно предпросмотра, очень удобно
Перевод:
Azrv3l cпециально для xss.pro