Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
Помогли мы вам |
SSP тоже не так прост, как кажется: мы рассмотрели его использование в клиент‑серверных процессах в прошлой статье. Он не только помогает разработчикам шифровать данные, обеспечивать целостность передаваемой информации, выстраивать контекст, но и может расширить стандартную аутентификацию. Правда, для этой цели будет использоваться не просто SSP, а SSP/AP, о котором мы поговорим позже.
Их можно считать кирпичиками, из которых строится вся огромная система аутентификации в Windows. Отмечу, что теория из этой статьи будет распространяться и на последующий материал. Мы начнем с простейшего перехвата пароля, а затем будем понемногу усложнять себе задачу.
Security Package (SP) — программная реализация некоего протокола безопасности. Security Package содержатся в SSP (и/или SSP/AP) в виде DLL-файлов. Например, Kerberos и NTLM находятся в SSP Secur32.
. Да, именно в Secur32.
, так как именно этот SSP делегирует функции безопасности нужному SP. Например, если служба требует аутентификацию по Kerberos, то Secur32.
вызовет Kerberos.
.
AP — Authentication Package. Представляет собой библиотеку DLL, которая тоже содержит один или несколько SP. Главное отличие от стандартного SSP заключается в том, что SSP/AP может выступать в качестве пакета аутентификации (AP), то есть проверять подлинность введенных данных при входе пользователя в систему.
Тем не менее SSP/AP выполняет и все функции стандартного SSP (выстраивать контекст, шифровать данные и прочие). Чтобы SSP/AP мог функционировать и в качестве пакета аутентификации, и в качестве обычного SSP для клиент‑серверных процессов, при запуске системы он загружается в пространство процесса lsass.
.
Также, если требуется работать лишь с клиент‑серверными функциями конкретного SSP/AP, он без проблем может быть загружен в клиент‑серверное приложение. Например, методом динамического (функция LoadLibrary(
) либо статического связывания (pragma
), то есть без загрузки в процесс lsass.
. В таком случае функции пакета аутентификации использоваться не будут.
Security Providers не стоит путать с Security Package. Они все‑таки различаются.
Провайдеры безопасности реализованы в виде DLL и позволяют выполнить так называемую вторичную аутентификацию. То есть после того, как пользователь прошел аутентификацию на одной машине, он может пройти аутентификацию и на другой машине, например на сервере Linux. Таким образом, пользователь получает доступ к ресурсам UNIX-сервера с машины Windows без дополнительной аутентификации. Это называется Single Sign-On.
Провайдеры учетных данных — COM-объекты, служащие для беспарольного доступа к системе. Реализованы тоже в виде динамических библиотек DLL. Например, для распознавания лица используется FaceCredentialProvider.
, для смарт‑карт — SmartcardCredentialProvider.
.
Также может использоваться сторонний поставщик учетных данных. Все доступные поставщики учетных данных перечислены здесь:
Каждый ключ по этому пути реестра идентифицирует определенный класс поставщика безопасности по его CLSID
. Сам CLSID
должен быть зарегистрирован в HKCRCLSID
, так как является классом COM
. Для изучения всех доступных поставщиков также можно воспользоваться инструментом CPlist.exe.
С помощью Password Filters можно расширить стандартную парольную политику на конкретных хостах. Когда создается запрос на смену пароля, LSA вызывает все пакеты уведомлений, чтобы проверить, удовлетворяет ли новый пароль фильтрам, реализованным внутри пакета. Причем каждый пакет уведомлений вызывается дважды:
Password Filter можно считать частным случаем Notification Package.
Перед тем как мы сможем перехватить пароль, следует разобраться с процессом входа пользователя в систему. В этой статье мы не будем вдаваться в подробности инициализации Winlogon, создания рабочих столов, GINA. Просто знай, что именно благодаря процессу Winlogon.
осуществляется интерактивный вход. Система запустилась, пользователь сел за клавиатуру.
Для начала аутентификации отправляется комбинация клавиш SAS (по умолчанию Ctrl + Alt + Del). Winlogon получает это сообщение, начинается процесс входа.
Так как в современных системах присутствует множество вариантов беспарольного входа (отпечаток пальца, распознавание лица), то Winlogon обращается к Credential Providers, чтобы получить информацию об аутентифицирующемся пользователе.
Вне зависимости от ответа провайдера учетных данных Winlogon порождает процесс LogonUI.
. Он предоставляет интерфейс для ввода пароля и завершается после окончания этого действия. Таким образом, LogonUI.
может перезапускаться бесконечное количество раз, если вдруг возникают какие‑либо ошибки. Как следствие, обеспечивается защита от возможного краша Winlogon.
.
После того как пользователь ввел свой логин и пароль либо если провайдер учетных данных вернул их, Winlogon создает уникальный SID для входа этого пользователя. Данный SID назначается всему текущему экземпляру рабочего стола (клавиатура, мышь, экран). После чего идет обращение к процессу lsass.
с целью аутентификации пользователя.
Обращение можно разделить на несколько этапов. Сначала Winlogon.
регистрирует себя как процесс аутентификации. Делается это вызовом функции LsaRegisterLogonProcess(
. В случае успешного вызова процесс получает хендл на LSA для последующего взаимодействия. Причем взаимодействие будет осуществляться посредством ALPC (Advanced Local Procedure Calls).
Далее Winlogon.
получает хендл на пакет аутентификации MSV1_0 (и Kerberos в случае AD, тут мы рассматриваем только MSV1_0) путем вызова LsaLookupAuthenticationPackage(
:
NTSTATUS LsaLookupAuthenticationPackage(
[in] HANDLELsaHandle,
[in] PLSA_STRING PackageName,
[out] PULONGAuthenticationPackage
);
Эта функция ничего особенного не требует. 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.
вызывает функцию NtCreateToken(
для создания токена доступа. В этом токене будет содержаться информация о самом пользователе. Именно этот токен в дальнейшем станет использовать Winlogon.
для создания процесса от лица аутентифицированного пользователя.
Возможно, у тебя сразу появилась коварная мысль: «А могу ли я сам генерировать токены?» Как бы да и как бы нет. Для успешного создания токена требуется привилегия SeCreateTokenPrivilege
, которой обладает только lsass.
. Если у нас есть эта привилегия, то мы сможем нафантазировать что душе угодно. Абсолютно любой токен — любые группы, привилегии, вне зависимости от каких‑либо глобальных настроек и конфигураций. Например, я получал права на выполнение кода от лица группы.
И ставил SID = 0.
Дополнительно Winlogon.
собирает информацию о пользовательской среде. Информация эта самая разная. Заострю внимание на начальном процессе, он же системный шелл, — процессе, который будет порождать остальные процессы в системе от лица пользователя и применяя все установленные настройки пользовательского профиля. Все эти данные хранятся в HKLMSOFTWAREMicrosoftWindows NTCurrentVersionWinlogon
. В ключе Userinit
по умолчанию указан процесс userinit.
, который как раз таки восстанавливает настройки профиля пользователя. А в ключе shell
— системный шелл, обычно это explorer.
.
Сначала идет обращение именно к Userinit
, программа запускается, выполняется инициализация среды, а затем userinit.
обращается к ключу shell
и порождает системный шелл. После этого процесс Userinit
завершается. Собственно, ровно по этой причине мы и не видим родительского процесса у explorer.
— userinit.
уже завершился.
Пользователь заходит в систему и получает доступ к своему рабочему столу.
Именно LSA играет ключевую роль в процессе аутентификации пользователя. Каким образом LSA будет инициализировать наши вредоносные SP, AP и NP?
При запуске устройства LSA автоматически подгружает все зарегистрированные SP, реализованные в виде DLL, в свое адресное пространство. Все зарегистрированные DLL находятся по следующему пути:
Если этот ключ пустой, то используется значение по умолчанию:
Все эти DLL указываются без полного пути. Microsoft рекомендует помещать SP в папку %systemroot%/
. Далее у каждого 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). Их список извлекается из следующего ключа реестра:
В каждом из них будет вызвана функция 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_STRINGDatabase, _In_opt_ PLSA_STRINGConfidentiality, _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
}
}
|
|