Боль­шинс­тво механиз­мов защиты в Windows стро­ится на паролях учет­ных записей поль­зовате­лей. В сегод­няшней статье мы раз­берем нес­коль­ко спо­собов перех­вата паролей в момент авто­риза­ции поль­зовате­ля и напишем код для авто­мати­зации это­го про­цес­са.

Windows име­ет слож­ную сис­тему аутен­тифика­ции со мно­жес­твом ком­понен­тов. Фун­дамен­том этой сис­темы мож­но счи­тать LSA (Local Security Authority) и SSP.

LSA — огромная под­систе­ма, которая слу­жит для про­вер­ки под­линнос­ти поль­зовате­лей, их регис­тра­ции, сме­ны пароля и подоб­ных опе­раций. Еще в LSA хра­нит­ся информа­ция обо всех аспектах безопас­ности локаль­ного компь­юте­ра, нап­ример количес­тве неус­пешных вво­дов пароля для бло­киров­ки учет­ной записи. Пос­редс­твом LSA мож­но даже наз­начать при­виле­гии поль­зователь­ским учет­кам, для это­го раз­работан инс­тру­мент Privileger.

SSP тоже не так прост, как кажет­ся: мы рас­смот­рели его исполь­зование в кли­ент‑сер­верных про­цес­сах в прош­лой статье. Он не толь­ко помога­ет раз­работ­чикам шиф­ровать дан­ные, обес­печивать целос­тность переда­ваемой информа­ции, выс­тра­ивать кон­текст, но и может рас­ширить стан­дар­тную аутен­тифика­цию. Прав­да, для этой цели будет исполь­зовать­ся не прос­то SSP, а SSP/AP, о котором мы погово­рим поз­же.

 

Компоненты безопасности

Их мож­но счи­тать кир­пичика­ми, из которых стро­ится вся огромная сис­тема аутен­тифика­ции в Windows. Отме­чу, что теория из этой статьи будет рас­простра­нять­ся и на пос­леду­ющий матери­ал. Мы нач­нем с прос­тей­шего перех­вата пароля, а затем будем понем­ногу усложнять себе задачу.

 

Security Package

Security Package (SP) — прог­рам­мная реали­зация неко­его про­токо­ла безопас­ности. Security Package содер­жатся в SSP (и/или SSP/AP) в виде DLL-фай­лов. Нап­ример, Kerberos и NTLM находят­ся в SSP Secur32.dll. Да, имен­но в Secur32.dll, так как имен­но этот SSP делеги­рует фун­кции безопас­ности нуж­ному SP. Нап­ример, если служ­ба тре­бует аутен­тифика­цию по Kerberos, то Secur32.dll вызовет Kerberos.dll.

Работа SP
Ра­бота SP
 

SSP/AP (или же просто AP)

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 и клиентских процессах
SSP/AP в lsass.exe и кли­ент­ских про­цес­сах
 

Security Providers

Security Providers не сто­ит путать с Security Package. Они все‑таки раз­лича­ются.

Про­вай­деры безопас­ности реали­зова­ны в виде DLL и поз­воля­ют выпол­нить так называ­емую вто­рич­ную аутен­тифика­цию. То есть пос­ле того, как поль­зователь про­шел аутен­тифика­цию на одной машине, он может прой­ти аутен­тифика­цию и на дру­гой машине, нап­ример на сер­вере Linux. Таким обра­зом, поль­зователь получа­ет дос­туп к ресур­сам UNIX-сер­вера с машины Windows без допол­нитель­ной аутен­тифика­ции. Это называ­ется Single Sign-On.

Работа Security Providers
Ра­бота Security Providers
 

Credential Providers

Про­вай­деры учет­ных дан­ных — COM-объ­екты, слу­жащие для бес­пароль­ного дос­тупа к сис­теме. Реали­зова­ны тоже в виде динами­чес­ких биб­лиотек DLL. Нап­ример, для рас­позна­вания лица исполь­зует­ся FaceCredentialProvider.dll, для смарт‑карт — SmartcardCredentialProvider.dll.

Так­же может исполь­зовать­ся сто­рон­ний пос­тавщик учет­ных дан­ных. Все дос­тупные пос­тавщи­ки учет­ных дан­ных перечис­лены здесь:

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers

Каж­дый ключ по это­му пути реес­тра иден­тифици­рует опре­делен­ный класс пос­тавщи­ка безопас­ности по его CLSID. Сам CLSID дол­жен быть зарегис­три­рован в HKCR\CLSID, так как явля­ется клас­сом COM. Для изу­чения всех дос­тупных пос­тавщи­ков так­же мож­но вос­поль­зовать­ся инс­тру­мен­том CPlist.exe.

 

Password Filters

С помощью Password Filters мож­но рас­ширить стан­дар­тную пароль­ную полити­ку на кон­крет­ных хос­тах. Ког­да соз­дает­ся зап­рос на сме­ну пароля, LSA вызыва­ет все пакеты уве­дом­лений, что­бы про­верить, удов­летво­ряет ли новый пароль филь­трам, реали­зован­ным внут­ри пакета. При­чем каж­дый пакет уве­дом­лений вызыва­ется дваж­ды:

  1. Для про­вер­ки нового пароля. Если какой‑то из пакетов уве­дом­лений сооб­щает, что пароль не под­ходит, сис­тема пот­ребу­ет у поль­зовате­ля ука­зать иной пароль.
  2. Для уве­дом­ления о том, что пароль изме­нил­ся. Этот вызов про­изой­дет толь­ко тог­да, ког­да все пакеты уве­дом­лений сооб­щили, что пароль нор­маль­ный и соот­ветс­тву­ет их филь­трам.

Password Filter мож­но счи­тать час­тным слу­чаем Notification Package.

Notification Package в случае проверки пароля
Notification Package в слу­чае про­вер­ки пароля
 

Как происходит вход пользователя в систему

Пе­ред тем как мы смо­жем перех­ватить пароль, сле­дует разоб­рать­ся с про­цес­сом вхо­да поль­зовате­ля в сис­тему. В этой статье мы не будем вда­вать­ся в под­робнос­ти ини­циали­зации Winlogon, соз­дания рабочих сто­лов, GINA. Прос­то знай, что имен­но бла­года­ря про­цес­су Winlogon.exe осу­щест­вля­ется инте­рак­тивный вход. Сис­тема запус­тилась, поль­зователь сел за кла­виату­ру.

  1. Для начала аутен­тифика­ции отправ­ляет­ся ком­бинация кла­виш SAS (по умол­чанию Ctrl + Alt + Del). Winlogon получа­ет это сооб­щение, начина­ется про­цесс вхо­да.

  2. Так как в сов­ремен­ных сис­темах при­сутс­тву­ет мно­жес­тво вари­антов бес­пароль­ного вхо­да (отпе­чаток паль­ца, рас­позна­вание лица), то Winlogon обра­щает­ся к Credential Providers, что­бы получить информа­цию об аутен­тифици­рующем­ся поль­зовате­ле.

  3. Вне зависи­мос­ти от отве­та про­вай­дера учет­ных дан­ных Winlogon порож­дает про­цесс LogonUI.exe. Он пре­дос­тавля­ет интерфейс для вво­да пароля и завер­шает­ся пос­ле окон­чания это­го дей­ствия. Таким обра­зом, LogonUI.exe может переза­пус­кать­ся бес­конеч­ное количес­тво раз, если вдруг воз­ника­ют какие‑либо ошиб­ки. Как следс­твие, обес­печива­ется защита от воз­можно­го кра­ша Winlogon.exe.

  4. Пос­ле того как поль­зователь ввел свой логин и пароль либо если про­вай­дер учет­ных дан­ных вер­нул их, Winlogon соз­дает уни­каль­ный SID для вхо­да это­го поль­зовате­ля. Дан­ный SID наз­нача­ется все­му текуще­му экзем­пля­ру рабоче­го сто­ла (кла­виату­ра, мышь, экран). Пос­ле чего идет обра­щение к про­цес­су lsass.exe с целью аутен­тифика­ции поль­зовате­ля.

  5. Об­ращение мож­но раз­делить на нес­коль­ко эта­пов. Сна­чала Winlogon.exe регис­три­рует себя как про­цесс аутен­тифика­ции. Дела­ется это вызовом фун­кции LsaRegisterLogonProcess(). В слу­чае успешно­го вызова про­цесс получа­ет хендл на LSA для пос­леду­юще­го вза­имо­дей­ствия. При­чем вза­имо­дей­ствие будет осу­щест­влять­ся пос­редс­твом ALPC (Advanced Local Procedure Calls).

  6. Да­лее 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 пакета аутен­тифика­ции.

  7. По­лучив иден­тифика­тор пакета аутен­тифика­ции, Winlogon переда­ет ему информа­цию в вызове фун­кции LsaLogonUser(). Эта информа­ция содер­жит SID из шага 4, а так­же информа­цию об аутен­тифици­рующем­ся поль­зовате­ле. Переда­ча SID помога­ет пре­дот­вра­тить несан­кци­они­рован­ный дос­туп к рабоче­му сто­лу, нап­ример если взять и ввес­ти пароль одно­го поль­зовате­ля, а поп­робовать получить дос­туп к сто­лу дру­гого.

  8. Внут­ри MSV1_0 вызыва­ется фун­кция LsaApLogonUserEx(), где имя поль­зовате­ля и пароль про­ходят аутен­тифика­цию при помощи базы дан­ных SAM. Если аутен­тифика­ция успешна, там же соз­дает­ся сес­сия вхо­да в сис­тему: вызыва­ется LsaCreateLogonSession(), и ей прис­ваивает­ся LogonID (LUID), который генери­рует­ся пакетом аутен­тифика­ции. Пос­ле это­го MSV1_0 добав­ляет спе­циаль­ную информа­цию к сес­сии с помощью вызова LsaAddCredential(). Обыч­но это имя поль­зовате­ля, имя домена и кон­троль­ные сум­мы LM/NT-хеша пароля. Эта информа­ция впос­ледс­твии понадо­бит­ся, если поль­зователь попыта­ется получить дос­туп к уда­лен­ным узлам.

  9. Да­лее Winlogon дожида­ется отве­та от LSA по поводу вве­ден­ных учет­ных дан­ных.

  10. Пос­ле успешной аутен­тифика­ции поль­зовате­ля запус­кает­ся ини­циали­зация поль­зователь­ской обо­лоч­ки (User Shell). Поль­зователь­ская обо­лоч­ка — совокуп­ность про­цес­сов, запущен­ных от лица кон­крет­ной учет­ной записи.

  11. Прос­то взять и соз­дать поль­зователь­скую обо­лоч­ку, нап­ример с помощью обыч­ного CreateProcess(), не получит­ся. Сна­чала lsass.exe вызыва­ет фун­кцию NtCreateToken() для соз­дания токена дос­тупа. В этом токене будет содер­жать­ся информа­ция о самом поль­зовате­ле. Имен­но этот токен в даль­нейшем ста­нет исполь­зовать Winlogon.exe для соз­дания про­цес­са от лица аутен­тифици­рован­ного поль­зовате­ля.

    Воз­можно, у тебя сра­зу появи­лась ковар­ная мысль: «А могу ли я сам генери­ровать токены?» Как бы да и как бы нет. Для успешно­го соз­дания токена тре­бует­ся при­виле­гия SeCreateTokenPrivilege, которой обла­дает толь­ко lsass.exe. Если у нас есть эта при­виле­гия, то мы смо­жем нафан­тазиро­вать что душе угод­но. Абсо­лют­но любой токен — любые груп­пы, при­виле­гии, вне зависи­мос­ти от каких‑либо гло­баль­ных нас­тро­ек и кон­фигура­ций. Нап­ример, я получал пра­ва на выпол­нение кода от лица груп­пы.

    Выполнение кода от лица группы
    Вы­пол­нение кода от лица груп­пы

    И ста­вил SID = 0.

    Null sid
    Null sid
  12. До­пол­нитель­но Winlogon.exe собира­ет информа­цию о поль­зователь­ской сре­де. Информа­ция эта самая раз­ная. Заос­трю вни­мание на началь­ном про­цес­се, он же сис­темный шелл, — про­цес­се, который будет порож­дать осталь­ные про­цес­сы в сис­теме от лица поль­зовате­ля и при­меняя все уста­нов­ленные нас­трой­ки поль­зователь­ско­го про­филя. Все эти дан­ные хра­нят­ся в HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon. В клю­че Userinit по умол­чанию ука­зан про­цесс userinit.exe, который как раз таки вос­ста­нав­лива­ет нас­трой­ки про­филя поль­зовате­ля. А в клю­че shell — сис­темный шелл, обыч­но это explorer.exe.

  13. Сна­чала идет обра­щение имен­но к Userinit, прог­рамма запус­кает­ся, выпол­няет­ся ини­циали­зация сре­ды, а затем userinit.exe обра­щает­ся к клю­чу shell и порож­дает сис­темный шелл. Пос­ле это­го про­цесс Userinit завер­шает­ся. Собс­твен­но, ров­но по этой при­чине мы и не видим родитель­ско­го про­цес­са у explorer.exe — userinit.exe уже завер­шился.

  14. Поль­зователь заходит в сис­тему и получа­ет дос­туп к сво­ему рабоче­му сто­лу.

Процесс аутентификации
Про­цесс аутен­тифика­ции
 

Инициализация LSA

Имен­но LSA игра­ет клю­чевую роль в про­цес­се аутен­тифика­ции поль­зовате­ля. Каким обра­зом LSA будет ини­циали­зиро­вать наши вре­донос­ные SP, AP и NP?

При запус­ке устрой­ства LSA авто­мати­чес­ки под­гру­жает все зарегис­три­рован­ные SP, реали­зован­ные в виде DLL, в свое адресное прос­транс­тво. Все зарегис­три­рован­ные DLL находят­ся по сле­дующе­му пути:

HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages
Ключ со всеми SP
Ключ со все­ми SP

Ес­ли этот ключ пус­той, то исполь­зует­ся зна­чение по умол­чанию:

kerberos"\0"msv1_0"\0"schannel"\0"wdigest"\0"tspkg"\0"pku2u"\0

Все эти 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). Их спи­сок извле­кает­ся из сле­дующе­го клю­ча реес­тра:

HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Authentication Packages
Ключ со всеми AP
Ключ со все­ми 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
}
}
 

Эксплуатация

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    2 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии