Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Большинство механизмов защиты в Windows строится на паролях учетных записей пользователей. В сегодняшней статье мы разберем несколько способов перехвата паролей в момент авторизации пользователя и напишем код для автоматизации этого процесса.
Windows имеет сложную систему аутентификации со множеством компонентов. Фундаментом этой системы можно считать LSA (Local Security Authority) и SSP.
LSA — огромная подсистема, которая служит для проверки подлинности пользователей, их регистрации, смены пароля и подобных операций. Еще в LSA хранится информация обо всех аспектах безопасности локального компьютера, например количестве неуспешных вводов пароля для блокировки учетной записи. Посредством LSA можно даже назначать привилегии пользовательским учеткам, для этого разработан инструмент Privileger.
SSP тоже не так прост, как кажется: мы рассмотрели его использование в клиент‑серверных процессах в прошлой статье. Он не только помогает разработчикам шифровать данные, обеспечивать целостность передаваемой информации, выстраивать контекст, но и может расширить стандартную аутентификацию. Правда, для этой цели будет использоваться не просто SSP, а SSP/AP, о котором мы поговорим позже.
Их можно считать кирпичиками, из которых строится вся огромная система аутентификации в Windows. Отмечу, что теория из этой статьи будет распространяться и на последующий материал. Мы начнем с простейшего перехвата пароля, а затем будем понемногу усложнять себе задачу.
Security Package (SP) — программная реализация некоего протокола безопасности. Security Package содержатся в SSP (и/или SSP/AP) в виде DLL-файлов. Например, Kerberos и NTLM находятся в SSP Secur32.dll
. Да, именно в Secur32.dll
, так как именно этот SSP делегирует функции безопасности нужному SP. Например, если служба требует аутентификацию по Kerberos, то Secur32.dll
вызовет Kerberos.dll
.
Работа SP
AP — Authentication Package. Представляет собой библиотеку DLL, которая тоже содержит один или несколько SP. Главное отличие от стандартного SSP заключается в том, что SSP/AP может выступать в качестве пакета аутентификации (AP), то есть проверять подлинность введенных данных при входе пользователя в систему.
Тем не менее SSP/AP выполняет и все функции стандартного SSP (выстраивать контекст, шифровать данные и прочие). Чтобы SSP/AP мог функционировать и в качестве пакета аутентификации, и в качестве обычного SSP для клиент‑серверных процессов, при запуске системы он загружается в пространство процесса lsass.exe
.
Также, если требуется работать лишь с клиент‑серверными функциями конкретного SSP/AP, он без проблем может быть загружен в клиент‑серверное приложение. Например, методом динамического (функция LoadLibrary()
) либо статического связывания (pragma comment
), то есть без загрузки в процесс lsass.exe
. В таком случае функции пакета аутентификации использоваться не будут.
SSP/AP в lsass.exe и клиентских процессах
Security Providers не стоит путать с Security Package. Они все‑таки различаются.
Провайдеры безопасности реализованы в виде DLL и позволяют выполнить так называемую вторичную аутентификацию. То есть после того, как пользователь прошел аутентификацию на одной машине, он может пройти аутентификацию и на другой машине, например на сервере Linux. Таким образом, пользователь получает доступ к ресурсам UNIX-сервера с машины Windows без дополнительной аутентификации. Это называется Single Sign-On.
Работа Security Providers
Провайдеры учетных данных — COM-объекты, служащие для беспарольного доступа к системе. Реализованы тоже в виде динамических библиотек DLL. Например, для распознавания лица используется FaceCredentialProvider.dll
, для смарт‑карт — SmartcardCredentialProvider.dll
.
Также может использоваться сторонний поставщик учетных данных. Все доступные поставщики учетных данных перечислены здесь:
HKLMSOFTWAREMicrosoftWindowsCurrentVersionAuthenticationCredential Providers
Каждый ключ по этому пути реестра идентифицирует определенный класс поставщика безопасности по его CLSID
. Сам CLSID
должен быть зарегистрирован в HKCRCLSID
, так как является классом COM
. Для изучения всех доступных поставщиков также можно воспользоваться инструментом CPlist.exe.
С помощью Password Filters можно расширить стандартную парольную политику на конкретных хостах. Когда создается запрос на смену пароля, LSA вызывает все пакеты уведомлений, чтобы проверить, удовлетворяет ли новый пароль фильтрам, реализованным внутри пакета. Причем каждый пакет уведомлений вызывается дважды:
Password Filter можно считать частным случаем Notification Package.
Notification Package в случае проверки пароля
Перед тем как мы сможем перехватить пароль, следует разобраться с процессом входа пользователя в систему. В этой статье мы не будем вдаваться в подробности инициализации Winlogon, создания рабочих столов, GINA. Просто знай, что именно благодаря процессу Winlogon.exe
осуществляется интерактивный вход. Система запустилась, пользователь сел за клавиатуру.
Для начала аутентификации отправляется комбинация клавиш SAS (по умолчанию Ctrl + Alt + Del). Winlogon получает это сообщение, начинается процесс входа.
Так как в современных системах присутствует множество вариантов беспарольного входа (отпечаток пальца, распознавание лица), то Winlogon обращается к Credential Providers, чтобы получить информацию об аутентифицирующемся пользователе.
Вне зависимости от ответа провайдера учетных данных Winlogon порождает процесс LogonUI.exe
. Он предоставляет интерфейс для ввода пароля и завершается после окончания этого действия. Таким образом, LogonUI.exe
может перезапускаться бесконечное количество раз, если вдруг возникают какие‑либо ошибки. Как следствие, обеспечивается защита от возможного краша Winlogon.exe
.
После того как пользователь ввел свой логин и пароль либо если провайдер учетных данных вернул их, Winlogon создает уникальный SID для входа этого пользователя. Данный SID назначается всему текущему экземпляру рабочего стола (клавиатура, мышь, экран). После чего идет обращение к процессу lsass.exe
с целью аутентификации пользователя.
Обращение можно разделить на несколько этапов. Сначала Winlogon.exe
регистрирует себя как процесс аутентификации. Делается это вызовом функции LsaRegisterLogonProcess()
. В случае успешного вызова процесс получает хендл на LSA для последующего взаимодействия. Причем взаимодействие будет осуществляться посредством ALPC (Advanced Local Procedure Calls).
Далее Winlogon.exe
получает хендл на пакет аутентификации MSV1_0 (и Kerberos в случае AD, тут мы рассматриваем только MSV1_0) путем вызова LsaLookupAuthenticationPackage()
:
NTSTATUS LsaLookupAuthenticationPackage( [in] HANDLE LsaHandle, [in] PLSA_STRING PackageName, [out] PULONG AuthenticationPackage);
Эта функция ничего особенного не требует. LsaHandle
— хендл, полученный вызовом LsaRegisterLogonProcess()
; PackageName
— имя пакета аутентификации, например MSV1_0_PACKAGE_NAME
; AuthenticationPackage
— полученный идентификатор желаемого для использования Winlogon пакета аутентификации.
Получив идентификатор пакета аутентификации, Winlogon передает ему информацию в вызове функции LsaLogonUser()
. Эта информация содержит SID из шага 4, а также информацию об аутентифицирующемся пользователе. Передача SID помогает предотвратить несанкционированный доступ к рабочему столу, например если взять и ввести пароль одного пользователя, а попробовать получить доступ к столу другого.
Внутри MSV1_0 вызывается функция LsaApLogonUserEx()
, где имя пользователя и пароль проходят аутентификацию при помощи базы данных SAM. Если аутентификация успешна, там же создается сессия входа в систему: вызывается LsaCreateLogonSession()
, и ей присваивается LogonID (LUID), который генерируется пакетом аутентификации. После этого MSV1_0 добавляет специальную информацию к сессии с помощью вызова LsaAddCredential()
. Обычно это имя пользователя, имя домена и контрольные суммы LM/NT-хеша пароля. Эта информация впоследствии понадобится, если пользователь попытается получить доступ к удаленным узлам.
Далее Winlogon дожидается ответа от LSA по поводу введенных учетных данных.
После успешной аутентификации пользователя запускается инициализация пользовательской оболочки (User Shell). Пользовательская оболочка — совокупность процессов, запущенных от лица конкретной учетной записи.
Просто взять и создать пользовательскую оболочку, например с помощью обычного CreateProcess()
, не получится. Сначала lsass.exe
вызывает функцию NtCreateToken()
для создания токена доступа. В этом токене будет содержаться информация о самом пользователе. Именно этот токен в дальнейшем станет использовать Winlogon.exe
для создания процесса от лица аутентифицированного пользователя.
Возможно, у тебя сразу появилась коварная мысль: «А могу ли я сам генерировать токены?» Как бы да и как бы нет. Для успешного создания токена требуется привилегия SeCreateTokenPrivilege
, которой обладает только lsass.exe
. Если у нас есть эта привилегия, то мы сможем нафантазировать что душе угодно. Абсолютно любой токен — любые группы, привилегии, вне зависимости от каких‑либо глобальных настроек и конфигураций. Например, я получал права на выполнение кода от лица группы.
Выполнение кода от лица группы
И ставил SID = 0.
Null sid
Дополнительно Winlogon.exe
собирает информацию о пользовательской среде. Информация эта самая разная. Заострю внимание на начальном процессе, он же системный шелл, — процессе, который будет порождать остальные процессы в системе от лица пользователя и применяя все установленные настройки пользовательского профиля. Все эти данные хранятся в HKLMSOFTWAREMicrosoftWindows NTCurrentVersionWinlogon
. В ключе Userinit
по умолчанию указан процесс userinit.exe
, который как раз таки восстанавливает настройки профиля пользователя. А в ключе shell
— системный шелл, обычно это explorer.exe
.
Сначала идет обращение именно к Userinit
, программа запускается, выполняется инициализация среды, а затем userinit.exe
обращается к ключу shell
и порождает системный шелл. После этого процесс Userinit
завершается. Собственно, ровно по этой причине мы и не видим родительского процесса у explorer.exe
— userinit.exe
уже завершился.
Пользователь заходит в систему и получает доступ к своему рабочему столу.
Процесс аутентификации
Именно LSA играет ключевую роль в процессе аутентификации пользователя. Каким образом LSA будет инициализировать наши вредоносные SP, AP и NP?
При запуске устройства LSA автоматически подгружает все зарегистрированные SP, реализованные в виде DLL, в свое адресное пространство. Все зарегистрированные DLL находятся по следующему пути:
HKLMSYSTEMCurrentControlSetControlLsaSecurity Packages
Ключ со всеми SP
Если этот ключ пустой, то используется значение по умолчанию:
kerberos" "msv1_0" "schannel" "wdigest" "tspkg" "pku2u"
Все эти DLL указываются без полного пути. Microsoft рекомендует помещать SP в папку %systemroot%/system32
. Далее у каждого SP вызывается функция SpLsaModeInitialize()
, благодаря которой LSA получает специальную таблицу SECPKG_FUNCTION_TABLE
, содержащую указатели на функции. Они реализуют данный пакет безопасности. Выглядит это примерно вот так:
SECPKG_FUNCTION_TABLE SecurityPackageFunctionTable[] ={ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, SpInitialize, SpShutDown, SpGetInfo, SpAcceptCredentials, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }};NTSTATUS NTAPI SpLsaModeInitialize(ULONG LsaVersion, PULONG PackageVersion, PSECPKG_FUNCTION_TABLE * ppTables, PULONG pcTables){ *PackageVersion = SECPKG_INTERFACE_VERSION; *ppTables = SecurityPackageFunctionTable; *pcTables = 1; return 0;}
Если SpLsaModeInitialize()
успешно вернула таблицу, то LSA вызывает SpInitialize()
, которой передает структуру LSA_SECPKG_FUNCTION_TABLE
. В этой структуре содержатся указатели на функции, которые предоставляет LSA для использования внутри SP. Например, функцию CreateToken()
можно использовать для создания токена (это не токен доступа, а токен, который генерируется во время выстраивания контекста в клиент‑серверных приложениях).
Третьей вызывается SpGetInfo()
, благодаря которой LSA получает информацию о пакете. Например, его имя, описание и версию. Эта информация будет отображаться при вызове функции EnumerateSecurityPackages()
:
NTSTATUS NTAPI SpGetInfo(PSecPkgInfoW PackageInfo){ PackageInfo->fCapabilities = SECPKG_FLAG_ACCEPT_WIN32_NAME | SECPKG_FLAG_CONNECTION | SECPKG_FLAG_LOGON; PackageInfo->Name = (SEC_WCHAR*)L"MishaSSP"; PackageInfo->Comment = (SEC_WCHAR*)L"SSP with a wide Russian soul"; PackageInfo->wRPCID = SECPKG_ID_NONE; PackageInfo->cbMaxToken = 0; PackageInfo->wVersion = 1337; return 0;}
Далее LSA загружает все доступные пакеты аутентификации (AP). Их список извлекается из следующего ключа реестра:
HKLMSYSTEMCurrentControlSetControlLsaAuthentication Packages
Ключ со всеми AP
В каждом из них будет вызвана функция LsaApInitializePackage()
, в ней LSA передаст таблицу LSA_DISPATCH_TABLE, содержащую все функции LSA, которые может дергать AP. AP, в свою очередь, должен заполнить последний параметр функции, указав свое имя. Это имя LSA использует, чтобы определить, к какому AP хочет получить доступ программа, путем вызова LsaLookupAuthenticationPackage()
.
LSA_DISPATCH_TABLE DispatchTable;NTSTATUS LsaApInitializePackage(_In_ ULONG AuthenticationPackageId, _In_ PLSA_DISPATCH_TABLE LsaDispatchTable, _In_opt_ PLSA_STRING Database, _In_opt_ PLSA_STRING Confidentiality, _Out_ PLSA_STRING* AuthenticationPackageName){ // Сохраняем адреса функций DispatchTable.CreateLogonSession = LsaDispatchTable->CreateLogonSession; DispatchTable.DeleteLogonSession = LsaDispatchTable->DeleteLogonSession; DispatchTable.AddCredential = LsaDispatchTable->AddCredential; DispatchTable.GetCredentials = LsaDispatchTable->GetCredentials; DispatchTable.DeleteCredential = LsaDispatchTable->DeleteCredential; DispatchTable.AllocateLsaHeap = LsaDispatchTable->AllocateLsaHeap; DispatchTable.FreeLsaHeap = LsaDispatchTable->FreeLsaHeap; DispatchTable.AllocateClientBuffer = LsaDispatchTable->AllocateClientBuffer; DispatchTable.FreeClientBuffer = LsaDispatchTable->FreeClientBuffer; DispatchTable.CopyToClientBuffer = LsaDispatchTable->CopyToClientBuffer; DispatchTable.CopyFromClientBuffer = LsaDispatchTable->CopyFromClientBuffer; // Возвращаем имя нашего AP (*AuthenticationPackageName) = (LSA_STRING*)LsaDispatchTable->AllocateLsaHeap(sizeof(LSA_STRING)); if (NULL != (*AuthenticationPackageName)) { (*AuthenticationPackageName) = (LSA_STRING*) LsaDispatchTable->AllocateLsaHeap(sizeof(LSA_STRING)); (*AuthenticationPackageName)->Buffer = (char*) LsaDispatchTable->AllocateLsaHeap((ULONG)strlen ("myssp") + 1); if (NULL != (*AuthenticationPackageName)->Buffer) { (*AuthenticationPackageName)->Length = strlen("myssp"); (*AuthenticationPackageName)->MaximumLength = strlen("myssp") + 1; strcpy( (*AuthenticationPackageName)->Buffer, "myssp"); return 0x00000000L; // STATUS_SUCCESS } return 0xC0000002; // STATUS_NOT_IMPLEMENTED }}
Источник: xakep.ru