Есть разные способы злоупотреблять сессией пользователя на устройстве: кража учетных данных, манипуляции с токенами и другие. Но знаешь ли ты, что можно сымитировать получение TGT-билета пользователем через совершенно легитимные функции Windows?
Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
В этом году появилось несколько новых способов из такого разряда. Наиболее интересные — WTSImpersonator и GIUDA. Последний позволяет получать тикеты залогиненного пользователя, даже не зная его пароля! Давай разберемся, как это работает, а параллельно напишем реализацию на C++, которую я назвал TGSThief.
При входе пользователя в Windows появляется сессия пользователя, которая хранит все данные о нем. Для каждого нового пользователя создается новая сессия. Например, если на компьютере одновременно работают два пользователя, то будет две сессии.
Как выглядят Logon Sessions
Каждая сессия определяется с помощью LUID (locally unique identifier). Из названия понятно, что LUID уникален для каждой сессии. Информация хранится в виде одноименной структуры.
typedef struct _LUID { ULONG LowPart; LONG HighPart;} LUID, *PLUID;
Сам LUID представлен в виде двух значений: ULONG и LONG. Причем обычно заполняется лишь поле LowPart, а HighPart имеет значение 0.
Эта структура используется во всех функциях WinAPI, которые так или иначе связаны с сессиями пользователя.
С помощью GetTokenInformation() можно получить LUID пользователя. Для этого функции следует передать токен процесса, запущенного от имени текущего пользователя.
#include <windows.h>#include <iostream>#include <sddl.h>int main() { HANDLE tokenHandle; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tokenHandle)) { std::cerr << «Ошибка OpenProcessToken: « << GetLastError() << std::endl; return 1; } DWORD tokenInformationLength = 0; GetTokenInformation(tokenHandle, TokenStatistics, nullptr, 0, &tokenInformationLength); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { std::cerr << «GetTokenInformation неудачный первый вызов: « << GetLastError() << std::endl; CloseHandle(tokenHandle); return 1; } TOKEN_STATISTICS* tokenStats = reinterpret_cast<TOKEN_STATISTICS*>(new BYTE[tokenInformationLength]); if (tokenStats == nullptr) { std::cerr << «Ошибка выделения памяти для TOKEN_STATISTICS» << std::endl; CloseHandle(tokenHandle); return 1; } if (!GetTokenInformation(tokenHandle, TokenStatistics, tokenStats, tokenInformationLength, &tokenInformationLength)) { std::cerr << «Ошибка GetTokenInformation: « << GetLastError() << std::endl; delete[] tokenStats; CloseHandle(tokenHandle); return 1; } std::cout << «LUID: « << std::hex << «0x» << std::uppercase << tokenStats->AuthenticationId.LowPart << tokenStats->AuthenticationId.HighPart << std::endl; delete[] tokenStats; CloseHandle(tokenHandle); return 0;}
Информация о LUID пользователя упадет в поле AuthenticationId структуры TOKEN_STATISTICS.
typedef struct _TOKEN_STATISTICS { LUID TokenId; LUID AuthenticationId; LARGE_INTEGER ExpirationTime; TOKEN_TYPE TokenType; SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; DWORD DynamicCharged; DWORD DynamicAvailable; DWORD GroupCount; DWORD PrivilegeCount; LUID ModifiedId;} TOKEN_STATISTICS, *PTOKEN_STATISTICS;
Именно на манипуляциях с LUID основана логика инструмента GIUDA. Фактически идет подмена LUID, что открывает возможность запросить билет от лица пользователя, чья сессия просто присутствует на устройстве. Свой LUID мы получать научились, а как получать LUID других пользователей?
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
9990 рублей 4000 р.
[TD]
920 р.
[/TD]
Я уже участник «Xakep.ru»
warning
Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
В этом году появилось несколько новых способов из такого разряда. Наиболее интересные — WTSImpersonator и GIUDA. Последний позволяет получать тикеты залогиненного пользователя, даже не зная его пароля! Давай разберемся, как это работает, а параллельно напишем реализацию на C++, которую я назвал TGSThief.
Logon Session
При входе пользователя в Windows появляется сессия пользователя, которая хранит все данные о нем. Для каждого нового пользователя создается новая сессия. Например, если на компьютере одновременно работают два пользователя, то будет две сессии.
Как выглядят Logon Sessions
Каждая сессия определяется с помощью LUID (locally unique identifier). Из названия понятно, что LUID уникален для каждой сессии. Информация хранится в виде одноименной структуры.
typedef struct _LUID { ULONG LowPart; LONG HighPart;} LUID, *PLUID;
Сам LUID представлен в виде двух значений: ULONG и LONG. Причем обычно заполняется лишь поле LowPart, а HighPart имеет значение 0.
Эта структура используется во всех функциях WinAPI, которые так или иначе связаны с сессиями пользователя.
С помощью GetTokenInformation() можно получить LUID пользователя. Для этого функции следует передать токен процесса, запущенного от имени текущего пользователя.
#include <windows.h>#include <iostream>#include <sddl.h>int main() { HANDLE tokenHandle; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tokenHandle)) { std::cerr << «Ошибка OpenProcessToken: « << GetLastError() << std::endl; return 1; } DWORD tokenInformationLength = 0; GetTokenInformation(tokenHandle, TokenStatistics, nullptr, 0, &tokenInformationLength); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { std::cerr << «GetTokenInformation неудачный первый вызов: « << GetLastError() << std::endl; CloseHandle(tokenHandle); return 1; } TOKEN_STATISTICS* tokenStats = reinterpret_cast<TOKEN_STATISTICS*>(new BYTE[tokenInformationLength]); if (tokenStats == nullptr) { std::cerr << «Ошибка выделения памяти для TOKEN_STATISTICS» << std::endl; CloseHandle(tokenHandle); return 1; } if (!GetTokenInformation(tokenHandle, TokenStatistics, tokenStats, tokenInformationLength, &tokenInformationLength)) { std::cerr << «Ошибка GetTokenInformation: « << GetLastError() << std::endl; delete[] tokenStats; CloseHandle(tokenHandle); return 1; } std::cout << «LUID: « << std::hex << «0x» << std::uppercase << tokenStats->AuthenticationId.LowPart << tokenStats->AuthenticationId.HighPart << std::endl; delete[] tokenStats; CloseHandle(tokenHandle); return 0;}
Информация о LUID пользователя упадет в поле AuthenticationId структуры TOKEN_STATISTICS.
typedef struct _TOKEN_STATISTICS { LUID TokenId; LUID AuthenticationId; LARGE_INTEGER ExpirationTime; TOKEN_TYPE TokenType; SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; DWORD DynamicCharged; DWORD DynamicAvailable; DWORD GroupCount; DWORD PrivilegeCount; LUID ModifiedId;} TOKEN_STATISTICS, *PTOKEN_STATISTICS;
Именно на манипуляциях с LUID основана логика инструмента GIUDA. Фактически идет подмена LUID, что открывает возможность запросить билет от лица пользователя, чья сессия просто присутствует на устройстве. Свой LUID мы получать научились, а как получать LUID других пользователей?
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
-60% |
1 год
9990 рублей 4000 р.
[TD]
1 месяц
920 р.
[/TD]
Я уже участник «Xakep.ru»