April 26, 2024

Локальная картошка. Исследуем уязвимость в локальной аутентификации Windows

  1. NTLM-аутентификация
  2. Локальная аутентификация NTLM
  3. Как работает LocalPotato
  4. StorSvc и DLL hijacking
  5. Компиляция эксплоита
  6. Эксплуатация
  7. Вывод

В этой статье речь пой­дет об уяз­вимос­ти, поз­воля­ющей выпол­нить локаль­ное повыше­ние при­виле­гий (LPE) в опе­раци­онных сис­темах семей­ства Windows. Баг нап­рямую свя­зан с NTLM-аутен­тифика­цией, поэто­му сна­чала погово­рим о том, как она устро­ена, а потом перей­дем непос­редс­твен­но к раз­бору CVE-2023-21746.

NTLM-АУТЕНТИФИКАЦИЯ

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

  1. Поль­зователь отправ­ляет сер­веру зап­рос с име­нем учет­ной записи.
  2. В ответ сер­вер отправ­ляет ему слу­чай­ное чис­ло, называ­емое challenge.
  3. Поль­зователь шиф­рует это чис­ло сво­им NT-хешем и отправ­ляет обратно.
  4. Сер­вер извле­кает из SAM (Security Account Manager — RPC-сер­вер Windows, опе­риру­ющий базой дан­ных учет­ных записей) хеш поль­зовате­ля и про­делы­вает те же самые дей­ствия, что и поль­зователь, срав­нивая получен­ные хеши. Если резуль­таты сов­пали, то аутен­тифика­ция счи­тает­ся успешной.

ЛОКАЛЬНАЯ АУТЕНТИФИКАЦИЯ NTLM

Рас­смот­рим локаль­ную аутен­тифика­цию NTLM, которая начина­ется с аутен­тифика­ции поль­зовате­ля на самой машине.

Эта аутен­тифика­ция выпол­няет­ся сле­дующим обра­зом:

  1. Поль­зователь вво­дит свои учет­ные дан­ные, логин и пароль, при вхо­де на машину.
  2. Вве­ден­ные дан­ные переда­ются под­систе­ме локаль­ной безопас­ности LSA, которая пре­обра­зует пароль в хеш.
  3. Да­лее LSA переда­ет имя поль­зовате­ля SAM, который извле­кает хеш ука­зан­ного поль­зовате­ля.
  4. LSA све­ряет хеши, и если они сов­пада­ют, то поль­зователь получа­ет дос­туп к машине.

Да­лее сра­баты­вает рас­смот­ренный выше механизм, осно­ван­ный на модели кли­ент — сер­вер.

Ло­каль­ная аутен­тифика­ция NTLM — это час­тный слу­чай, она при­меня­ется, ког­да кли­ент­ская и сер­верная час­ти работа­ют на одной машине.

Кли­ент получа­ет учет­ные дан­ные вошед­шего в сис­тему поль­зовате­ля и соз­дает зап­рос, содер­жащий имя рабочей стан­ции и домена кли­ента.

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

Со­обще­ние типа 2. Сер­вер соз­дает кон­текст безопас­ности, вызывая фун­кцию AcceptSecurityContext (NTLM), и в этом сооб­щении отправ­ляет кли­енту его иден­тифика­тор. Затем кли­ент может исполь­зовать иден­тифика­тор кон­тек­ста безопас­ности, что­бы свя­зать себя с соеди­нени­ем.

Со­обще­ние 3-го типа. Кли­ент получа­ет токен и переда­ет его в InitializeSecurityContext (NTLM). Если InitializeSecurityContext (NTLM) воз­вра­щает SEC_E_OK, то вза­имная аутен­тифика­ция завер­шена и мож­но начинать защищен­ный сеанс. Если же он воз­вра­щает код ошиб­ки, то перего­воры о вза­имной аутен­тифика­ции завер­шают­ся. В про­тив­ном слу­чае токен безопас­ности, воз­вра­щен­ный InitializeSecurityContext (NTLM), отправ­ляет­ся кли­енту и шаги 2 и 3 пов­торя­ются.

КАК РАБОТАЕТ LOCALPOTATO

LocalPotato исполь­зует недос­таток в механиз­ме локаль­ной аутен­тифика­ции NTLM. Экс­пло­ит обма­ныва­ет при­виле­гиро­ван­ный про­цесс и зас­тавля­ет аутен­тифици­ровать сеанс, запущен­ный хакером. В резуль­тате ата­кующий получа­ет соеди­нение, пре­дос­тавля­ющее ему дос­туп к любым ресур­сам с при­виле­гиями обма­нуто­го про­цес­са.

«Кар­тошка» работа­ет сле­дующим обра­зом:

  1. Ха­кер запус­кает при­виле­гиро­ван­ный про­цесс для под­клю­чения к под­кон­троль­ному ему сер­веру. В прин­ципе, это работа­ет ана­логич­но пре­дыду­щим Potato, ког­да неп­ривиле­гиро­ван­ный поль­зователь зас­тавлял ОС соз­давать соеди­нения, исполь­зующие пра­ва SYSTEM.
  2. Сер­вер на машине соз­даст кон­текст безопас­ности А для при­виле­гиро­ван­ного соеди­нения, но не будет отправ­лять его сра­зу. Хакер запус­тит свой кли­ент, что­бы он одновре­мен­но с локаль­ным ини­цииро­вал соеди­нение с сер­вером. Легитим­ный кли­ент отправ­ляет сооб­щение, что­бы ини­цииро­вать соеди­нение, а сер­вер в ответ пош­лет сооб­щение с иден­тифика­тором нового кон­тек­ста безопас­ности Б.
  3. Зло­умыш­ленник так­же запус­кает свой сер­вер и меня­ет иден­тифика­торы кон­тек­стов обо­их соеди­нений таким обра­зом, что­бы при­виле­гиро­ван­ный про­цесс получил кон­текст соеди­нения с сер­вером зло­умыш­ленни­ка, а не со сво­им собс­твен­ным. В резуль­тате хакер смо­жет получить дос­туп к любому сетево­му ресур­су с при­виле­гиями SYSTEM.

STORSVC И DLL HIJACKING

До сих пор мы исполь­зовали LocalPotato для записи любых фай­лов на целевую машину. Что­бы получить при­виле­гиро­ван­ную обо­лоч­ку, нам нуж­но выяс­нить, как исполь­зовать про­изволь­ную запись для выпол­нения коман­ды.

Не­дав­но был обна­ружен еще один век­тор повыше­ния при­виле­гий, ког­да зло­умыш­ленник мог перех­ватить отсутс­тву­ющую DLL для выпол­нения про­изволь­ных команд с при­виле­гиями SYSTEM. Единс­твен­ная проб­лема это­го век­тора зак­лючалась в том, что для его запус­ка зло­умыш­ленни­ку необ­ходимо было записать DLL в сис­темную перемен­ную PATH. По умол­чанию в PATH Windows вклю­чают­ся толь­ко те катало­ги, в которые могут писать лишь при­виле­гиро­ван­ные учет­ные записи. Хотя мож­но най­ти машины, на которых уста­нов­ка опре­делен­ных при­ложе­ний изме­нила перемен­ную PATH и сде­лала машину уяз­вимой, век­тор ата­ки при­меним толь­ко к кон­крет­ным сце­нари­ям. Ком­биниро­вание дан­ной ата­ки с LocalPotato поз­воля­ет пре­одо­леть это огра­ниче­ние и получить пол­ностью рабочий экс­пло­ит для повыше­ния при­виле­гий.

Как выяс­нила ком­пания BlackArrow, зло­умыш­ленник может отпра­вить RPC-вызов методу SvcRebootToFlashingMode, пре­дос­тавля­емо­му служ­бой StorSvc, что, в свою оче­редь, при­ведет к попыт­ке заг­рузки отсутс­тву­ющей DLL под наз­вани­ем SprintCSP.dll.

Ес­ли ты нез­наком с RPC, то счи­тай, что это API, который рас­кры­вает фун­кции для уда­лен­ного исполь­зования. В опи­сан­ном слу­чае служ­ба StorSvc рас­кры­вает метод SvcRebootToFlashingMode, который может выз­вать любой человек, име­ющий дос­туп к машине.

Пос­коль­ку StorSvc работа­ет с при­виле­гиями SYSTEM, соз­дание SprintCSP.dll где‑нибудь в PATH при­ведет к заг­рузке биб­лиоте­ки при каж­дом вызове SvcRebootToFlashingMode.

КОМПИЛЯЦИЯ ЭКСПЛОИТА

Те­перь, ког­да мы зна­ем всю теоре­тичес­кую базу, мы можем прис­тупить к выпол­нению прак­тичес­кой час­ти. Все необ­ходимое для даль­нейших дей­ствий мож­но най­ти в ре­пози­тории на GitHub.

Что­бы вос­поль­зовать­ся экс­пло­итом CVE-2023-21746, сна­чала надо ском­пилиро­вать два фай­ла:

  • SprintCSP.dll. Это недос­тающая DLL, которую мы собира­емся перех­ватить. Код по умол­чанию, пос­тавля­емый с экс­пло­итом, выпол­няет коман­ду whoami и выводит ответ в C:\Program Data\whoamiall.txt. Нам нуж­но изме­нить коман­ду, что­бы дос­тигнуть нашей цели;
  • RpcClient.exe. Эта прог­рамма запус­тит RPC-вызов SvcRebootToFlashingMode. В зависи­мос­ти от вер­сии Windows, на которую нацелен экс­пло­ит, его код может пот­ребовать­ся нем­ного под­пра­вить, пос­коль­ку в раз­ных вер­сиях Windows исполь­зуют­ся раз­ные иден­тифика­торы интерфей­сов для рас­кры­тия SvcRebootToFlashingMode.

Для начала раз­берем­ся с фай­лом RpcClient.exe. Как уже говори­лось, нам пот­ребу­ется изме­нить экс­пло­ит в зависи­мос­ти от вер­сии Windows на целевой машине. Для это­го нуж­но отре­дак­тировать пер­вые стро­ки фай­ла C:\tools\LPE via StorSvc\RpcClient\RpcClient\storsvc_c.c так, что­бы выбира­лась нуж­ная опе­раци­онная сис­тема. Пос­коль­ку моя машина работа­ет под управле­нием Windows Server 2019, я отре­дак­тирую файл сле­дующим обра­зом.

Это нас­тро­ит экс­пло­ит на исполь­зование кор­рек­тно­го иден­тифика­тора интерфей­са RPC для моей вер­сии Windows. Теперь, ког­да код исправ­лен, ском­пилиру­ем необ­ходимые нам инс­тру­мен­ты.

Пе­ред ком­пиляци­ей SprintCSP.dll нам нуж­но изме­нить фун­кцию DoStuff() в фай­ле SprintCSP\main.c таким обра­зом, что­бы она добав­ляла нашего текуще­го поль­зовате­ля в груп­пу Administrators. Вот код с нашей заменен­ной коман­дой:

void DoStuff() { // Replace all this code by your payload STARTUPINFO si = { sizeof(STARTUPINFO) }; PROCESS_INFORMATION pi; CreateProcess(L"c:\\windows\\system32\\cmd.exe",L" /C net localgroup administrators user /add", NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, L"C:\\Windows", &si, &pi); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return;}

Те­перь ском­пилиру­ем нашу DLL.

ЭКСПЛУАТАЦИЯ

Для начала удос­товерим­ся, что наш поль­зователь дей­стви­тель­но не вхо­дит в груп­пу локаль­ных адми­нис­тра­торов.

Для успешной экс­плу­ата­ции StorSvc необ­ходимо ско­пиро­вать SprintCSP.dll в любую дирек­торию текущей PATH. Про­верить PATH мож­но, выпол­нив сле­дующую коман­ду:

reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" -v Path

Мы будем исполь­зовать каталог %SystemRoot%\system32, который рас­ширя­ется до C:\windows\system32. Одна­ко ты можешь исполь­зовать любой из этих двух катало­гов.

Те­перь, ког­да все готово, можем запус­тить LocalPotato:

LocalPotato.exe -i SprintCSP.dll -o \Windows\System32\SprintCSP.dll

DLL успешно записа­лась в System32! Теперь мы можем запус­тить RpcClient.exe для запус­ка вызова SvcRebootToFlashingMode, выпол­нив полез­ную наг­рузку в нашей DLL.

Что­бы удос­товерить­ся, что экс­пло­ит сра­ботал, про­верим, находит­ся ли наш поль­зователь в груп­пе адми­нис­тра­торов.

ВЫВОД

Пос­ле успешно­го сра­баты­вания экс­пло­ита мы можем выпол­нить любые дей­ствия от лица адми­нис­тра­тора под учет­ной записью поль­зовате­ля user. Нап­ример, запус­тить коман­дную стро­ку.

Оп­тималь­ная защита от экс­плу­ата­ции этой уяз­вимос­ти (как и дру­гих подоб­ных) — сво­евре­мен­но уста­нав­ливать обновле­ния безопас­ности Microsoft. Тем не менее даже в этом слу­чае некото­рые про­токо­лы, исполь­зующие NTLM в качес­тве метода аутен­тифика­ции, могут быть уяз­вимы для подоб­ных атак.