Академический Документы
Профессиональный Документы
Культура Документы
ИЗУЧАЕМ ВОЗМОЖНОСТИ
WINAPI
ДЛЯ ПЕНТЕСТЕРА
Для начала давай запомним несколько терминов — скоро они нам пригодятся.
Контекст пользователя (user context), он же контекст безопасности (security
context), — набор уникальных отличительных признаков пользователя, слу‐
жащий для контроля доступа. Система хранит сведения о контексте в токене
(его также называют маркером доступа). Рассмотрим их чуть более подробно.
SID И ТОКЕНЫ
При входе в систему любой пользователь вводит свой логин и пароль. Затем,
если подключена доменная учетная запись, эти данные сверяются с хранили‐
щем учетных записей Active Directory на контроллере домена, которое называ‐
ется ntds.dit, либо с базой данных локального компьютера — SAM.
Если пароль верный, система начинает собирать сведения об учетной
записи. В случае Active Directory также собирается информация уровня домена
(например, доменные группы). И независимо от типа УЗ находятся сведения,
относящиеся к локальной системе, в том числе перечень локальных групп,
в которых состоит пользователь. Все эти данные помещаются в специальную
структуру, хранящуюся в объекте ядра, который называется токеном доступа.
В системах Windows у многих объектов — группы, домена, пользователя —
существует специальный идентификатор безопасности, SID (Security Identifier).
Он имеет вот такой формат:
S-R-I-S-S
В этой записи:
• S означает, что последовательность чисел представляет собой идентифика‐
тор безопасности;
• R — номер версии SID;
• I — число, представляющее уполномоченный орган (authority), который
создал или выдал SID;
• S — число, представляющее второй уполномоченный орган (subauthority).
Также содержит внутри себя RID (Relative Identifier) — дополнительный
идентификатор, который используется, чтобы отличить одного пользовате‐
ля от другого;
• S — еще один уполномоченный орган. SID может содержать внутри себя
любое количество уполномоченных органов.
ТОКЕН И ПРОЦЕСС
В Windows есть процессы, а есть потоки. Говоря простыми словами, это некие
объекты, обладающие собственным виртуальным адресным пространством.
Потоком называют ход выполнения программы. Поток выполняется в рамках
владеющего им процесса, или, как говорят, в контексте процесса. Любое
запущенное приложение представляет собой процесс, в контексте которого
выполняется по крайней мере один поток.
У процесса есть токен. Чаще всего используется токен пользователя,
запустившего процесс. Когда процесс порождает другие процессы, все они
используют этот же токен.
ПРИСТУПАЕМ К РАБОТЕ
Получаем токен
Существует несколько функций для получения токена. Для работы с процес‐
сами и потоками можно использовать следующие варианты.
Вариант 1: получить токен определенного процесса.
BOOL OpenProcessToken(
[in] HANDLE ProcessHandle,
[in] DWORD DesiredAccess,
[out] PHANDLE TokenHandle
);
BOOL OpenThreadToken(
[in] HANDLE ThreadHandle,
[in] DWORD DesiredAccess,
[in] BOOL OpenAsSelf,
[out] PHANDLE TokenHandle
);
BOOL LogonUserA(
[in] LPCSTR lpszUsername,
[in, optional] LPCSTR lpszDomain,
[in, optional] LPCSTR lpszPassword,
[in] DWORD dwLogonType,
[in] DWORD dwLogonProvider,
[out] PHANDLE phToken
);
BOOL PrivilegeCheck(
[in] HANDLE ClientToken,
[in, out] PPRIVILEGE_SET RequiredPrivileges,
[out] LPBOOL pfResult
);
Сам код может быть примерно следующий (принимает токен, в котором надо
проверить наличие привилегии, и ее имя. Допустим, SE_DEBUG_NAME):
BOOL SetTokenInformation(
[in] HANDLE TokenHandle,
[in] TOKEN_INFORMATION_CLASS TokenInformationClass,
[in] LPVOID TokenInformation,
[in] DWORD TokenInformationLength
);
Конечно же, в токене возможно изменить далеко не все параметры. Ниже опи‐
саны допустимые классы информации для SetTokenInformation(), а также
привилегии и маски доступа, которые для этого требуются.
BOOL LookupPrivilegeValueA(
[in, optional] LPCSTR lpSystemName,
[in] LPCSTR lpName,
[out] PLUID lpLuid
);
BOOL AdjustTokenPrivileges(
[in] HANDLE TokenHandle,
[in] BOOL DisableAllPrivileges,
[in, optional] PTOKEN_PRIVILEGES NewState,
[in] DWORD BufferLength,
[out, optional] PTOKEN_PRIVILEGES PreviousState,
[out, optional] PDWORD ReturnLength
);
Эта функция может как включить привилегии, так и отключить их. Не знаю,
почему Microsoft не реализовала что‑нибудь подобное:
__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;
BOOL DuplicateTokenEx(
[in] HANDLE hExistingToken,
[in] DWORD dwDesiredAccess,
[in, optional] LPSECURITY_ATTRIBUTES lpTokenAttributes,
[in] SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
[in] TOKEN_TYPE TokenType,
[out] PHANDLE phNewToken
);
Создание процесса
Следующим шагом идет вызов CreateProcessWithTokenW(). Эта функция
создает процесс, а затем привязывает к нему указанный токен:
BOOL CreateProcessWithTokenW(
[in] HANDLE hToken,
[in] DWORD dwLogonFlags,
[in, optional] LPCWSTR lpApplicationName,
[in, out, optional] LPWSTR lpCommandLine,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCWSTR lpCurrentDirectory,
[in] LPSTARTUPINFOW lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
#include <windows.h>
#include <iostream>
std::cin >> a;
// CloseHandle() опустил
return 0;
}
Применение к потоку
Можно привязать токен и к определенному потоку процесса. Для этого сущес‐
твует следующая функция:
BOOL SetThreadToken(
[in, optional] PHANDLE Thread,
[in, optional] HANDLE Token
);
Например:
HANDLE hProcToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, &
hProcToken);
HANDLE hImpToken;
DuplicateTokenEx(hProcToken, MAXIMUM_ALLOWED, nullptr,
SecurityIdentification, TokenImpersonation, &hImpToken);
CloseHandle(hProcToken);
SetThreadToken(nullptr, hImpToken);
// Делаем что-нибудь
RevertToSelf();
CloseHandle(hImpToken);
HANDLE hToken;
ImpersonateLoggedOnUser(hToken);
// Выполняем задачи от лица пользователя alice
RevertToSelf(); // Возвращаем исходный контекст
CloseHandle(hToken)
BOOL ImpersonateSelf(
[in] SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
);
Именованные каналы
Существует возможность имперсонации клиента пайпа:
BOOL ImpersonateNamedPipeClient(
[in] HANDLE hNamedPipe
);
#include <Windows.h>
#include <iostream>
int main() {
LPCWSTR pipeName = L"\\\\.\\pipe\\pipename";
LPVOID pipeBuffer = NULL;
HANDLE serverPipe;
DWORD readBytes = 0;
DWORD readBuffer = 0;
int err = 0;
BOOL isPipeConnected;
BOOL isPipeOpen;
DWORD bytesWritten = 0;
std::wcout << "Creating named pipe " << pipeName << std::endl;
serverPipe = CreateNamedPipe(pipeName, PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE, 1, 2048, 2048, 0, NULL);
STARTUPINFO si = {};
wchar_t command[] = L"C:\\Windows\\system32\\cmd.exe";
PROCESS_INFORMATION pi = {};
HANDLE threadToken = GetCurrentThreadToken();
CreateProcessWithTokenW(threadToken, LOGON_WITH_PROFILE,
command, NULL, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
return 0;
}
Начало работы
Сначала следует перечислить все доступные для текущего хоста SSP. Это мож‐
но сделать с помощью следующей функции:
Например:
#define SECURITY_WIN32
#include <windows.h>
#include <stdio.h>
#include <sspi.h>
int main() {
ULONG pcPackages = 0;
SecPkgInfo* secPkgInfo = NULL;
SECURITY_STATUS status;
status = EnumerateSecurityPackages(&pcPackages, &secPkgInfo);
if (status != SEC_E_OK) {
wprintf(L"Security function failed with error: %i\n",
status);
}
for (ULONG i = 0; i < pcPackages; i++) {
wprintf(L"%ws\n", secPkgInfo[i].Name);
}
return 0;
}
Продолжение статьи →
ВЗЛОМ ← НАЧАЛО СТАТЬИ
СВИН API
ИЗУЧАЕМ ВОЗМОЖНОСТИ WINAPI
ДЛЯ ПЕНТЕСТЕРА
Например:
#define SECURITY_WIN32
#include <windows.h>
#include <stdio.h>
#include <sspi.h>
int main() {
ULONG pcPackages = 0;
SecPkgInfo* secPkgInfo = NULL;
SECURITY_STATUS status;
status = EnumerateSecurityPackages(&pcPackages, &secPkgInfo);
if (status != SEC_E_OK) {
wprintf(L"Security function failed with error: %i\n",
status);
}
for (ULONG i = 0; i < pcPackages; i++) {
wprintf(L"%d - %ws\n", i,secPkgInfo[i].Name);
}
printf("Which would u like? ");
int numberOfSSP = 0;
scanf_s("%d", &numberOfSSP);
CredHandle hCredentials;
TimeStamp tsExpires;
status = AcquireCredentialsHandle(NULL, secPkgInfo[numberOfSSP
].Name, SECPKG_CRED_BOTH, NULL, NULL, NULL, NULL, &hCredentials, &
tsExpires);
if (status != SEC_E_OK) {
wprintf(L"ERROR");
}
else {
wprintf(L"SUCCESS, lets authenticate!");
// Код для аутентификации
}
return 0;
}
Роль клиента
В процессе аутентификации клиент и сервер ведут себя по‑разному, поэтому
начнем с разбора того, что делает клиент.
Первым делом клиент инициирует исходящий контекст безопасности
из дескриптора учетных данных, полученных в результате вызова функции
AcquireCredentialsHandle(). Обычно эта функция вызывается в цикле
до тех пор, пока не будет установлен достаточный контекст безопасности.
Чтобы система сама выделила место под эти буферы, в вызове функции
InitializeSecurityContext() в параметре fContextReq требуется указать
ISC_REQ_ALLOCATE_MEMORY.
Наконец, итоговая функция на клиенте будет выглядеть следующим обра‐
зом:
ss = InitializeSecurityContext(phCredentials,
fFirstPass ? NULL : phContext, pszServer, *plAttrbibutes |
ISC_REQ_ALLOCATE_MEMORY, 0, SECURITY_NETWORK_DREP,
&secBufferDescriptorIn, 0, phContext, &
secBufDescriptorOut, plAttrbibutes, NULL);
DeleteSecurityContext(&hContext);
FreeCredentialsHandle(&hCredentials);
Роль сервера
После того как на клиенте будет запущена функция
InitializeSecurityContext(), она не будет возвращать управление до тех
пор, пока сервер не запустит свою функцию AcceptSecurityContext():
DeleteSecurityContext(&hContext);
FreeCredentialsHandle(&hCredentials);
Имперсонация
Функция ImpersonateSecurityContext позволяет серверу олицетворять кли‐
ента с помощью хендла контекста, ранее полученного вызовом
AcceptSecurityContext() или QuerySecurityContextToken(). Функция
ImpersonateSecurityContext() дает серверу возможность выступать от лица
клиента при всех проверках прав доступа:
RevertSecurityContext()
Позволяет прекратить олицетворение вызывающего объекта и восстановить
собственный контекст безопасности:
ВЫВОДЫ