Категория > Новости > Свин API. Изучаем возможности WinAPI для пентестера - «Новости»

Свин API. Изучаем возможности WinAPI для пентестера - «Новости»


18-03-2023, 17:07. Автор: Павел
до­кумен­тации Microsoft. Такие иден­тифика­торы называ­ются хорошо извес­тны­ми (well known).

То­кен же хра­нит внут­ри себя мно­жес­тво раз­личных SID, сре­ди которых мож­но выделить основные:



  • SID поль­зовате­ля — иден­тифици­рует учет­ную запись, для которой был соз­дан токен;

  • SID групп — иден­тифика­торы групп, в которые вхо­дит поль­зователь;

  • SID регис­тра­ции — уни­каль­ный SID, соз­данный в момент аутен­тифика­ции. Поз­воля­ет отли­чить один сеанс от дру­гого (если поль­зователь нес­коль­ко раз заходил в сис­тему, то сис­тема каж­дый раз соз­дает уни­каль­ный SID регис­тра­ции).


Читайте также - Для того, чтобы поднять настроение и зарядиться положительной энергией на целый день рекомендуется смотреть красивые картинки, поделки и многое другое. Фон море пляж посмотреть.

Дос­таточ­но слож­но, прав­да ведь? Но мож­но про­вес­ти прос­тую ана­логию. Токен — кар­точка сот­рудни­ка ком­пании. SID поль­зовате­ля — имя на этой кар­точке, SID груп­пы — напеча­тан­ная дол­жность. Сис­тема смот­рит на эту кар­точку каж­дый раз, ког­да мы начина­ем с ней вза­имо­дей­ство­вать.


 

Токен и процесс


В Windows есть про­цес­сы, а есть потоки. Говоря прос­тыми сло­вами, это некие объ­екты, обла­дающие собс­твен­ным вир­туаль­ным адресным прос­транс­твом. Потоком называ­ют ход выпол­нения прог­раммы. Поток выпол­няет­ся в рам­ках вла­деюще­го им про­цес­са, или, как говорят, в кон­тек­сте про­цес­са. Любое запущен­ное при­ложе­ние пред­став­ляет собой про­цесс, в кон­тек­сте которо­го выпол­няет­ся по край­ней мере один поток.


У про­цес­са есть токен. Чаще все­го исполь­зует­ся токен поль­зовате­ля, запус­тивше­го про­цесс. Ког­да про­цесс порож­дает дру­гие про­цес­сы, все они исполь­зуют этот же токен.


До­чер­ние про­цес­сы име­ют тот же токен

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


 

Приступаем к работе


 

Получаем токен


Су­щес­тву­ет нес­коль­ко фун­кций для получе­ния токена. Для работы с про­цес­сами и потока­ми мож­но исполь­зовать сле­дующие вари­анты.


Ва­риант 1: получить токен опре­делен­ного про­цес­са.


BOOL OpenProcessToken(
 [in] HANDLE ProcessHandle,
 [in] DWORD DesiredAccess,
 [out] PHANDLE TokenHandle
);

Ва­риант 2: получить токен опре­делен­ного потока.


BOOL OpenThreadToken(
 [in] HANDLE ThreadHandle,
 [in] DWORD DesiredAccess,
 [in] BOOL OpenAsSelf,
 [out] PHANDLE TokenHandle
);

Пе­репи­сывать MSDN и объ­яснять каж­дый параметр как‑то неп­равиль­но. Если вдруг ты нез­наком с WinAPI, то можешь написать мне, ски­ну матери­ал для изу­чения. Пред­лагаю обра­тить вни­мание лишь на вто­рой параметр — DesiredAccess.


Здесь ты дол­жен ука­зать, какой тип дос­тупа к токену хочешь получить. Это зна­чение пре­обра­зует­ся в мас­ку дос­тупа, на осно­ве которой Windows опре­деля­ет, мож­но выдавать токен или нель­зя. WinAPI пре­дос­тавля­ет для такой мас­ки некото­рые стан­дар­тные зна­чения.


Об­рати вни­мание, что прос­то так засунуть TOKEN_ALL_ACCESS нель­зя: сис­тема баналь­но не выдаст токен, так как в эту мас­ку вхо­дит и TOKEN_ADJUST_SESSIONID, который тре­бует наличие при­виле­гии SeTcbPrivilege. Такой при­виле­гией обла­дает лишь сис­тема.


При этом дан­ную ошиб­ку допус­кают очень час­то. Нап­ример, лишь в вер­сии 4.7 инс­тру­мен­та Cobalt Strike был исправ­лен этот недочет.


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


Ва­риант 3: зап­росить токен поль­зовате­ля, если мы зна­ем его логин и пароль.


BOOL LogonUserA(
 [in]LPCSTR lpszUsername,
 [in, optional] LPCSTR lpszDomain,
 [in, optional] LPCSTR lpszPassword,
 [in]DWORD dwLogonType,
 [in]DWORD dwLogonProvider,
 [out]PHANDLE phToken
);
 

Проверка наличия привилегии в токене


То­кен так­же содер­жит информа­цию о при­виле­гиях поль­зовате­ля. У самих при­виле­гий в Windows есть два пред­став­ления:



  • дру­жес­твен­ное имя — имя, которое отоб­ража­ется в интерфей­се Windows, нап­ример Act as part of the operating system;

  • прог­рам­мное имя — имя, которое исполь­зуют при­ложе­ния, нап­ример SE_TCB_NAME.


Для про­вер­ки мож­но исполь­зовать сле­дующую фун­кцию:


BOOL PrivilegeCheck(
 [in]HANDLEClientToken,
 [in, out] PPRIVILEGE_SET RequiredPrivileges,
 [out] LPBOOLpfResult
);

Сам код может быть при­мер­но сле­дующий (при­нима­ет токен, в котором надо про­верить наличие при­виле­гии, и ее имя. Допус­тим, SE_DEBUG_NAME):


bool IsPrivilegeEnabled(HANDLE hToken, PCWSTR name) {
 PRIVILEGE_SET set{};
 set.PrivilegeCount = 1;
 if (!::LookupPrivilegeValue(nullptr, name, &set.Privilege[0].Luid)) return false;
 BOOL result;
 return ::PrivilegeCheck(hToken, &set, &result) && result;
}
 

Изменение информации токена


До­пус­тимые изме­нения делят­ся на две груп­пы:



  • све­дения, которые мож­но изме­нить;

  • све­дения, которые мож­но задать.


Для боль­шинс­тва ситу­аций мож­но вос­поль­зовать­ся этой фун­кци­ей:


BOOL SetTokenInformation(
 [in] HANDLETokenHandle,
 [in] TOKEN_INFORMATION_CLASS TokenInformationClass,
 [in] LPVOIDTokenInformation,
 [in] DWORDTokenInformationLength
);

Ко­неч­но же, в токене воз­можно изме­нить далеко не все парамет­ры. Ниже опи­саны допус­тимые клас­сы информа­ции для SetTokenInformation(), а так­же при­виле­гии и мас­ки дос­тупа, которые для это­го тре­буют­ся.


Свин API. Изучаем возможности WinAPI для пентестера - «Новости»
Что мож­но изме­нить

Нап­ример, что­бы вклю­чить вир­туали­зацию UAC, исполь­зуй сле­дующий код:


// hProcess имеет PROCESS_QUERY_INFORMATION
HANDLE hToken;
OpenProcessToken(hProcess, TOKEN_ADJUST_DEFAULT, &hToken);
ULONG enable = 1;
SetTokenInformation(hToken, TokenVirtualizationEnabled,&enable, sizeof(enable));

При этом мы можем изме­нить и при­виле­гии, содер­жащи­еся в токене! Но тре­бует­ся знать, как получить из прог­рам­мно­го име­ни при­виле­гии ее LUID. Это поз­воля­ет сде­лать сле­дующая фун­кция:


BOOL LookupPrivilegeValueA(
 [in, optional] LPCSTR lpSystemName,
 [in]LPCSTR lpName,
 [out]PLUID lpLuid
);

Сле­дующим шагом мы дол­жны выз­вать AdjustTokenPrivilege():


BOOL AdjustTokenPrivileges(
 [in]HANDLETokenHandle,
 [in]BOOLDisableAllPrivileges,
 [in, optional] PTOKEN_PRIVILEGES NewState,
 [in]DWORDBufferLength,
 [out, optional] PTOKEN_PRIVILEGES PreviousState,
 [out, optional] PDWORDReturnLength
);

Эта фун­кция может как вклю­чить при­виле­гии, так и отклю­чить их. Не знаю, почему Microsoft не реали­зова­ла что‑нибудь подоб­ное:


BOOL EnableTokenPrivilege(HANDLE hToken, LPTSTR szPriv, BOOL bEnabled) {
 TOKEN_PRIVILEGES tp;
 LUID luid;
 BOOL bRet = FALSE;

 __try {
// Ищем уникальный для системы LUID привилегии
if (!LookupPrivilegeValue(NULL, szPriv /*SE_DEBUG_NAME*/, &luid)) {
// Если имя фиктивное
__leave;
}
// Создаем массив привилегий нашего маркера (в данном случае массив из одного элемента)
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = bEnabled ? SE_PRIVILEGE_ENABLED : 0;

// Изменяем состояние привилегий маркера, включая или отключая привилегии из нашего массива
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
__leave;
}
bRet = TRUE;
 }
 __finally {};
 return(bRet);

}

Этой фун­кции тре­бует­ся передать токен, прог­рам­мное имя при­виле­гии и булево зна­чение, TRUE или FALSE, то есть вклю­чить при­виле­гию или вык­лючить ее. При этом токен дол­жен иметь мас­ку TOKEN_ADJUST_PRIVILEGES.



Перейти обратно к новости