From f404e8960f56e77be649113a431cc8d2e2c37a10 Mon Sep 17 00:00:00 2001 From: Jamie Reece Wilson Date: Thu, 28 Dec 2023 21:00:07 +0000 Subject: [PATCH] [+] AuProcesses::RunAs --- Include/Aurora/Processes/Processes.hpp | 1 + Include/Aurora/Processes/RunAs.hpp | 54 + .../Aurora/Processes/StartupParameters.hpp | 5 + Source/AuProcAddresses.NT.cpp | 27 + Source/AuProcAddresses.NT.hpp | 211 ++++ Source/Processes/AuProcess.NT.cpp | 22 +- Source/Processes/AuProcess.NT.hpp | 2 +- Source/Processes/AuProcessElevation.Linux.cpp | 18 + Source/Processes/AuProcessElevation.Linux.hpp | 13 + Source/Processes/AuProcessElevation.NT.cpp | 997 ++++++++++++++++++ Source/Processes/AuProcessElevation.NT.hpp | 13 + 11 files changed, 1358 insertions(+), 5 deletions(-) create mode 100644 Include/Aurora/Processes/RunAs.hpp create mode 100644 Source/Processes/AuProcessElevation.Linux.cpp create mode 100644 Source/Processes/AuProcessElevation.Linux.hpp create mode 100644 Source/Processes/AuProcessElevation.NT.cpp create mode 100644 Source/Processes/AuProcessElevation.NT.hpp diff --git a/Include/Aurora/Processes/Processes.hpp b/Include/Aurora/Processes/Processes.hpp index e6c56355..ad2c9995 100644 --- a/Include/Aurora/Processes/Processes.hpp +++ b/Include/Aurora/Processes/Processes.hpp @@ -14,5 +14,6 @@ #include "StartupParameters.hpp" #include "Spawn.hpp" #include "OutputOf.hpp" +#include "RunAs.hpp" #include "Open.hpp" \ No newline at end of file diff --git a/Include/Aurora/Processes/RunAs.hpp b/Include/Aurora/Processes/RunAs.hpp new file mode 100644 index 00000000..432d0a9e --- /dev/null +++ b/Include/Aurora/Processes/RunAs.hpp @@ -0,0 +1,54 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: RunAs.hpp + Date: 2023-12-23 + Author: Reece +***/ +#pragma once + +namespace Aurora::Processes +{ + AUE_DEFINE(ERunAsUser, ( + eRegularUser, // + eSpecifiedImpersonation, // Privileged impersonation using admin creds and stated alternative uid/username (*) + eSuperUser, // Privileged status (root, standard run-as-admin privileges, etc) + eNTAS, // NT Authority/SYSTEM (*) + eNTTI // Trusted Installer (*) + )); + // (*) These APIs are somewhat spicey + + // Warning: In the default configurations of Windows, spawning processes with shared handles and such as elevated processes is generally not supported. + // The Aurora Runtime is importing APIs that'll probably make old anti-virus engines mald after sometime. However, it isn't some magic le epic uac bypass. + // You still need to have privileged credentials to hand. This should be noted bc retards on reddit and orange site are probably going to complain + // "hurhur this is malware. look, its editing policies and impersonating the UAC logon prompt in process. ooOoOO spooky." + // In reality, we're just trying to emulate the behaviour of consent.exe in-process for the likes of: + // * non-service level installers with temporary the local-sys-admin-says-its-ok permissions + // * remote daemon administration (build-bot daemons with admin credits in a toml file, running as local or network users, perhaps) + // * ssh-like servers + // * initially not-administrator processes temporarily elevating themselves to remove or re-enable kernel level drivers (drivers such as: reverse engineering tools, vidya gaym anticheats, debuggers, etc) + // These APIs will be of use for live-installers where installing for global-users is optional and the base requirements don't require escalation. + // Asking for creds in process after a software demo is playable is far nicer UX, than having to wait for 10 hours for Windows to spawn a consent.exe process, just to end up losing stdin/out/err redirection. + + struct RunAsDescriptor + { + AU_COPY_MOVE_DEF(RunAsDescriptor); + + ERunAsUser runAs = ERunAsUser::eRegularUser; + AuOptional numericUserId; + AuOptional username; + AuOptional password; + AuOptional server; + AuOptional impersonate; + AuOptional impersonateNumericUserId; + bool bLoginWithProfile {}; + + // if this structure is partially completed; + // a dialog may be shown if running under a desktop GUI (polkit, credui, etc), + // or a command-line based command (AuConsole) may be requested. + }; + + AUKN_SYM void RunAs(StartupParameters &startupParameters, + RunAsDescriptor &runAs); // SECURITY @ Try to enforce moving of AuOptional password; to hopefully purge it out of memory asap @ +} // SECURITY @ Noting that AuOptional password isn't memory safe. We should probably try to memset it after use and during move @ + // REGRESSION: make runAs move-only again \ No newline at end of file diff --git a/Include/Aurora/Processes/StartupParameters.hpp b/Include/Aurora/Processes/StartupParameters.hpp index 3d529e8a..59ad7d81 100644 --- a/Include/Aurora/Processes/StartupParameters.hpp +++ b/Include/Aurora/Processes/StartupParameters.hpp @@ -81,6 +81,7 @@ namespace Aurora::Processes LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION> ntLikeHookCreateProcessW; + AuConsumer<_SECURITY_ATTRIBUTES *> ntFixSharedHandleAttrs; #else AuSupplierConsumer ntLikeHookCreateProcessW; + AuConsumer ntFixSharedHandleAttrs; #endif + bool bForceNoJobParent {}; #endif #if defined(AURORA_IS_POSIX_DERIVED) AuVoidFunc posixApplySandboxCOW; + AuVoidFunc posixPCA; + AuVoidFunc posixPCB; #endif }; } \ No newline at end of file diff --git a/Source/AuProcAddresses.NT.cpp b/Source/AuProcAddresses.NT.cpp index e1034954..bd2be4f0 100644 --- a/Source/AuProcAddresses.NT.cpp +++ b/Source/AuProcAddresses.NT.cpp @@ -168,6 +168,7 @@ namespace Aurora ADD_LOAD_LIB(User32); ADD_LOAD_LIB(SetupAPI); ADD_LOAD_LIB(Router); + ADD_LOAD_LIB(CredUI); #define ADD_GET_PROC(name, proc) \ if (!IsBlocked(#proc)) \ @@ -333,6 +334,30 @@ namespace Aurora ADD_GET_PROC(AdvancedApi, SetNamedSecurityInfoW) ADD_GET_PROC(AdvancedApi, FreeSid) + // privilege escalation + ADD_GET_PROC(AdvancedApi, CreateProcessWithLogonW); + ADD_GET_PROC(AdvancedApi, CreateProcessAsUserW); + ADD_GET_PROC(AdvancedApi, AdjustTokenPrivileges); + ADD_GET_PROC(AdvancedApi, RevertToSelf); + ADD_GET_PROC(AdvancedApi, SetTokenInformation); + ADD_GET_PROC(AdvancedApi, GetSidSubAuthorityCount); + ADD_GET_PROC(AdvancedApi, GetSidSubAuthority); + ADD_GET_PROC(AdvancedApi, LogonUserW); + ADD_GET_PROC(AdvancedApi, OpenProcessToken); + ADD_GET_PROC(AdvancedApi, SetThreadToken); + ADD_GET_PROC(AdvancedApi, SetSecurityInfo); + ADD_GET_PROC(AdvancedApi, GetUserNameW); + ADD_GET_PROC(AdvancedApi, DuplicateTokenEx); + ADD_GET_PROC(AdvancedApi, LookupAccountSidW); + ADD_GET_PROC(AdvancedApi, GetTokenInformation); + ADD_GET_PROC(AdvancedApi, SetSecurityDescriptorDacl); + ADD_GET_PROC(AdvancedApi, InitializeSecurityDescriptor); + ADD_GET_PROC(AdvancedApi, LookupAccountNameW); + ADD_GET_PROC(AdvancedApi, LookupPrivilegeValueA); + ADD_GET_PROC(AdvancedApi, LsaOpenPolicy); + ADD_GET_PROC(AdvancedApi, LsaClose); + ADD_GET_PROC(AdvancedApi, LsaAddAccountRights); + ADD_GET_PROC_INTERNAL_MAP(AdvancedApi, RtlGenRandom, SystemFunction036) ADD_GET_PROC(BCrypt, BCryptGenRandom) @@ -373,6 +398,8 @@ namespace Aurora ADD_GET_PROC(Router, WNetOpenEnumW); ADD_GET_PROC(Router, WNetGetUniversalNameW); + ADD_GET_PROC(CredUI, CredUIPromptForWindowsCredentialsW); + ADD_GET_PROC(CredUI, CredUnPackAuthenticationBufferW); if (pNtCreateKeyedEvent && Threading::Primitives::gKeyedEventHandle == INVALID_HANDLE_VALUE) diff --git a/Source/AuProcAddresses.NT.hpp b/Source/AuProcAddresses.NT.hpp index c229b6dc..4e609abc 100644 --- a/Source/AuProcAddresses.NT.hpp +++ b/Source/AuProcAddresses.NT.hpp @@ -23,9 +23,11 @@ struct _SP_DEVINFO_DATA; struct _SP_DEVICE_INTERFACE_DATA; struct _SP_DEVICE_INTERFACE_DETAIL_DATA_W; struct _NETRESOURCEW; +enum _TOKEN_INFORMATION_CLASS; enum _SE_OBJECT_TYPE; enum _MINIDUMP_TYPE; enum _OBJECT_WAIT_TYPE; +enum _SE_OBJECT_TYPE; //#if defined(AURORA_COMPILER_MSVC) struct _IP_ADAPTER_ADDRESSES_LH; @@ -42,6 +44,21 @@ enum _OBJECT_WAIT_TYPE; #endif //#endif + +typedef UNICODE_STRING LSA_UNICODE_STRING, *PLSA_UNICODE_STRING; +typedef STRING LSA_STRING, *PLSA_STRING; +typedef OBJECT_ATTRIBUTES LSA_OBJECT_ATTRIBUTES, *PLSA_OBJECT_ATTRIBUTES; +typedef PVOID LSA_HANDLE, *PLSA_HANDLE; + +struct CREDUI_INFOW +{ + DWORD cbSize; + HWND hwndParent; + PCWSTR pszMessageText; + PCWSTR pszCaptionText; + HBITMAP hbmBanner; +}; + namespace Aurora { void InitNTAddresses(); @@ -64,6 +81,7 @@ namespace Aurora static const wchar_t *kUser32DllName { L"User32.dll" }; static const wchar_t *kSetupAPIDllName { L"SETUPAPI.dll" }; static const wchar_t *kRouterDllName { L"MPR.dll" }; + static const wchar_t *kCredUIDllName { L"credui.dll" }; struct WIN32_MEMORY_RANGE_ENTRY2 { @@ -665,6 +683,172 @@ namespace Aurora VOID * pSid ); + // Advanced API + // The proper dodgy APIs that'll probably get us hit by AV-engines + // The follow APIs are used for in-process privilege escalation + + inline BOOL(__stdcall *pCreateProcessWithLogonW)( + LPCWSTR lpUsername, + LPCWSTR lpDomain, + LPCWSTR lpPassword, + DWORD dwLogonFlags, + LPCWSTR lpApplicationName, + LPWSTR lpCommandLine, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation + ); + + inline BOOL(__stdcall *pCreateProcessAsUserW)( + HANDLE hToken, + LPCWSTR lpApplicationName, + LPWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation + ); + + inline BOOL(__stdcall *pAdjustTokenPrivileges)( + HANDLE TokenHandle, + BOOL DisableAllPrivileges, + PTOKEN_PRIVILEGES NewState, + DWORD BufferLength, + PTOKEN_PRIVILEGES PreviousState, + PDWORD ReturnLength + ); + + inline BOOL(__stdcall *pRevertToSelf)(); + + inline BOOL(__stdcall *pSetTokenInformation)( + HANDLE TokenHandle, + _TOKEN_INFORMATION_CLASS TokenInformationClass, + LPVOID TokenInformation, + DWORD TokenInformationLength + ); + + inline PUCHAR(__stdcall *pGetSidSubAuthorityCount)(PSID pSid); + + inline BOOL(__stdcall *pLookupPrivilegeValueA)( + LPCSTR lpSystemName, + LPCSTR lpName, + PLUID lpLuid + ); + + inline PDWORD(__stdcall *pGetSidSubAuthority)( + PSID pSid, + DWORD nSubAuthority + ); + + inline PDWORD(__stdcall *pLogonUserW)( + LPCWSTR lpszUsername, + LPCWSTR lpszDomain, + LPCWSTR lpszPassword, + DWORD dwLogonType, + DWORD dwLogonProvider, + PHANDLE phToken + ); + + inline PDWORD(__stdcall *pOpenProcessToken)( + HANDLE ProcessHandle, + DWORD DesiredAccess, + PHANDLE TokenHandle + ); + + inline BOOL(__stdcall *pSetThreadToken)( + PHANDLE Thread, + HANDLE Token + ); + + inline DWORD(__stdcall *pSetSecurityInfo)( + HANDLE handle, + _SE_OBJECT_TYPE ObjectType, + SECURITY_INFORMATION SecurityInfo, + PSID psidOwner, + PSID psidGroup, + PACL pDacl, + PACL pSacl + ); + + inline BOOL(__stdcall *pGetUserNameW)( + LPWSTR lpBuffer, + LPDWORD pcbBuffer + ); + + inline BOOL(__stdcall *pDuplicateTokenEx)( + HANDLE hExistingToken, + DWORD dwDesiredAccess, + LPSECURITY_ATTRIBUTES lpTokenAttributes, + SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, + TOKEN_TYPE TokenType, + PHANDLE phNewToken + ); + + inline BOOL(__stdcall *pLookupAccountSidW)( + LPCWSTR lpSystemName, + PSID Sid, + LPWSTR Name, + LPDWORD cchName, + LPWSTR ReferencedDomainName, + LPDWORD cchReferencedDomainName, + PSID_NAME_USE peUse + ); + + inline BOOL(__stdcall *pGetTokenInformation)( + HANDLE TokenHandle, + TOKEN_INFORMATION_CLASS TokenInformationClass, + LPVOID TokenInformation, + DWORD TokenInformationLength, + PDWORD ReturnLength + ); + + inline BOOL(__stdcall *pSetSecurityDescriptorDacl)( + PSECURITY_DESCRIPTOR pSecurityDescriptor, + BOOL bDaclPresent, + PACL pDacl, + BOOL bDaclDefaulted + ); + + inline BOOL(__stdcall *pInitializeSecurityDescriptor)( + PSECURITY_DESCRIPTOR pSecurityDescriptor, + DWORD dwRevision + ); + + inline NTSTATUS(__stdcall *pLsaOpenPolicy)( + PLSA_UNICODE_STRING SystemName, + PLSA_OBJECT_ATTRIBUTES ObjectAttributes, + ACCESS_MASK DesiredAccess, + PLSA_HANDLE PolicyHandle + ); + + inline NTSTATUS(__stdcall *pLsaClose)( + LSA_HANDLE ObjectHandle + ); + + inline NTSTATUS(__stdcall *pLsaAddAccountRights)( + LSA_HANDLE PolicyHandle, + PSID AccountSid, + PLSA_UNICODE_STRING UserRights, + ULONG CountOfRights + ); + + inline NTSTATUS(__stdcall *pLookupAccountNameW)( + LPCWSTR lpSystemName, + LPCWSTR lpAccountName, + PSID Sid, + LPDWORD cbSid, + LPWSTR ReferencedDomainName, + LPDWORD cchReferencedDomainName, + PSID_NAME_USE peUse + ); + + // USER32 - the shit microsoft will probably try to phase out and remove over time // [then give up and write a win32 emulator in a memelang, probably] @@ -973,6 +1157,33 @@ namespace Aurora LPINT lpAddressLength ); + // credui + + inline INT(__stdcall *pCredUIPromptForWindowsCredentialsW)( + void * pUiInfo, + DWORD dwAuthError, + ULONG * pulAuthPackage, + LPCVOID pvInAuthBuffer, + ULONG ulInAuthBufferSize, + LPVOID * ppvOutAuthBuffer, + ULONG * pulOutAuthBufferSize, + BOOL * pfSave, + DWORD dwFlags + ); + + inline BOOL(__stdcall *pCredUnPackAuthenticationBufferW)( + DWORD dwFlags, + PVOID pAuthBuffer, + DWORD cbAuthBuffer, + LPWSTR pszUserName, + DWORD * pcchMaxUserName, + LPWSTR pszDomainName, + DWORD * pcchMaxDomainName, + LPWSTR pszPassword, + DWORD * pcchMaxPassword + ); + + inline bool gUseNativeWaitMutex {}; inline bool gUseNativeWaitCondvar {}; inline bool gUseNativeWaitSemapahore {}; diff --git a/Source/Processes/AuProcess.NT.cpp b/Source/Processes/AuProcess.NT.cpp index 63043320..c2fe410e 100644 --- a/Source/Processes/AuProcess.NT.cpp +++ b/Source/Processes/AuProcess.NT.cpp @@ -235,6 +235,12 @@ namespace Aurora::Processes return WriteFile(this->pipeStdInWrite_, in.ptr, size, &size, NULL) && size == in.length; } + void ProcessImpl::RestLeakyMem() + { + AuResetMember(this->startup_.ntLikeHookCreateProcessW); + AuResetMember(this->startup_.ntFixSharedHandleAttrs); + } + bool ProcessImpl::Init() { SECURITY_ATTRIBUTES saAttr {}; @@ -260,6 +266,11 @@ namespace Aurora::Processes return false; } + if (this->startup_.ntFixSharedHandleAttrs) + { + this->startup_.ntFixSharedHandleAttrs(&saAttr); + } + this->exitCode_ = 0x10110100; if (this->startup_.fwdOut == EStreamForward::eAsyncPipe) { @@ -637,8 +648,7 @@ namespace Aurora::Processes uCreateFlags |= CREATE_UNICODE_ENVIRONMENT; } - BOOL result {}; - + DWORD result; if (auto &func = this->startup_.ntLikeHookCreateProcessW) { result = func(Locale::ConvertFromUTF8(this->startup_.process).c_str(), @@ -667,10 +677,12 @@ namespace Aurora::Processes &this->processInfo_); } + this->RestLeakyMem(); + if (!result) { DWORD wr = GetLastError(); - SysPushErrorGen("CreateProcess failed"); + SysPushErrorGen("CreateProcess failed: {}", wr); return false; } @@ -712,7 +724,8 @@ namespace Aurora::Processes return false; } - if (this->type_ == ESpawnType::eSpawnChildProcessWorker) + if (this->type_ == ESpawnType::eSpawnChildProcessWorker && + !this->startup_.bForceNoJobParent) { #if defined(AURORA_PLATFORM_WIN32) AssignJobWorker(this->processInfo_.hProcess); @@ -790,6 +803,7 @@ namespace Aurora::Processes if (!hi->Init()) { + hi->RestLeakyMem(); delete hi; return {}; } diff --git a/Source/Processes/AuProcess.NT.hpp b/Source/Processes/AuProcess.NT.hpp index b163244f..c241ca1d 100644 --- a/Source/Processes/AuProcess.NT.hpp +++ b/Source/Processes/AuProcess.NT.hpp @@ -44,7 +44,7 @@ namespace Aurora::Processes bool Start() override; bool Init(); - + void RestLeakyMem(); void RelOtherHandles(); private: diff --git a/Source/Processes/AuProcessElevation.Linux.cpp b/Source/Processes/AuProcessElevation.Linux.cpp new file mode 100644 index 00000000..6cfd0a57 --- /dev/null +++ b/Source/Processes/AuProcessElevation.Linux.cpp @@ -0,0 +1,18 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuProcessElevation.Linux.cpp + Date: 2023-12-26 + Author: Reece +***/ +#include +#include "AuProcessElevation.Linux.hpp" + +namespace Aurora::Processes +{ + AUKN_SYM void RunAs(StartupParameters &startupParameters, + RunAsDescriptor &runAs) + { + // ... + } +} \ No newline at end of file diff --git a/Source/Processes/AuProcessElevation.Linux.hpp b/Source/Processes/AuProcessElevation.Linux.hpp new file mode 100644 index 00000000..b61f28cb --- /dev/null +++ b/Source/Processes/AuProcessElevation.Linux.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuProcessElevation.Linux.hpp + Date: 2023-12-26 + Author: Reece +***/ +#pragma once + +namespace Aurora::Processes +{ + +} \ No newline at end of file diff --git a/Source/Processes/AuProcessElevation.NT.cpp b/Source/Processes/AuProcessElevation.NT.cpp new file mode 100644 index 00000000..907218af --- /dev/null +++ b/Source/Processes/AuProcessElevation.NT.cpp @@ -0,0 +1,997 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuProcessElevation.NT.cpp + Date: 2023-12-23 + Author: Reece +***/ +#include +#include "AuProcessElevation.NT.hpp" + +#include + +enum _SE_OBJECT_TYPE +{ + SE_KERNEL_OBJECT = 6 +}; + +#if !defined(POLICY_ALL_ACCESS) + #define POLICY_ALL_ACCESS 0xf0fff +#endif + +namespace Aurora::Processes +{ + struct SecureRunAs + { + RunAsDescriptor desc; + AuList pids {}; + bool bRepeat {}; + SECURITY_DESCRIPTOR sd {}; + + void BlankMemory() + { + if (this->desc.password) + { + AuMemset(this->desc.password.value().data(), + 0, + this->desc.password.value().size()); + } + } + + static HANDLE GetProcessTokenHandle() + { + static HANDLE gHandle = INVALID_HANDLE_VALUE; + static AuInitOnce gInitOnce; + + if (pOpenProcessToken) + { + gInitOnce.Call([&]() + { + pOpenProcessToken(OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()), + TOKEN_ALL_ACCESS, &gHandle); + }); + } + + return gHandle; + } + + static DWORD GetTokenIntegrityLevel() + { + char buffer[1000]; + TOKEN_MANDATORY_LABEL &info = *(TOKEN_MANDATORY_LABEL *)buffer; + DWORD dwLength { 1000 }; + + if (!pGetTokenInformation || + !pGetSidSubAuthority || + !pGetSidSubAuthorityCount) + { + return -1; + } + + if (!pGetTokenInformation(GetProcessTokenHandle(), TokenIntegrityLevel, &info, 1000, &dwLength)) + { + return -1; + } + + auto pSid = info.Label.Sid; + auto dwIntegrityLevel = *(pGetSidSubAuthority(pSid, (*(pGetSidSubAuthorityCount(pSid)) - 1U))); + + if (dwIntegrityLevel == SECURITY_MANDATORY_LOW_RID) + { + return dwIntegrityLevel; + } + else if (dwIntegrityLevel >= SECURITY_MANDATORY_MEDIUM_RID && dwIntegrityLevel < SECURITY_MANDATORY_HIGH_RID) + { + return SECURITY_MANDATORY_MEDIUM_RID; + } + else if (dwIntegrityLevel >= SECURITY_MANDATORY_HIGH_RID) + { + return SECURITY_MANDATORY_HIGH_RID; + } + else if (dwIntegrityLevel >= SECURITY_MANDATORY_SYSTEM_RID) + { + return SECURITY_MANDATORY_SYSTEM_RID; + } + + return -1; + } + + static bool SetTokenIntegrityLevel(HANDLE hToken, + DWORD dwIntegrity) + { + static const BYTE kSidPattern[6] { 0, 0, 0, 0, 0, 16 }; + TOKEN_MANDATORY_LABEL tokenLabel {}; + SID_IDENTIFIER_AUTHORITY sidAuth; + AuMemcpy(sidAuth.Value, kSidPattern, sizeof(sidAuth.Value)); + + if (!pSetTokenInformation || + !pAllocateAndInitializeSid) + { + return false; + } + + PSID sid; + if (!pAllocateAndInitializeSid(&sidAuth, + 1, + dwIntegrity, 0, 0, 0, 0, 0, 0, 0, + &sid)) + { + return false; + } + + tokenLabel.Label.Sid = sid; + tokenLabel.Label.Attributes = SE_GROUP_INTEGRITY; + return pSetTokenInformation(hToken, + TokenIntegrityLevel, + &tokenLabel, + sizeof(TOKEN_MANDATORY_LABEL)); + } + + static bool IsAdmn() + { + return false; + } + + enum class EPrompt + { + eError, + eCancel, + eOK + }; + + static EPrompt PromptForCreds(std::wstring &wideusername, + std::wstring &widepassword, + SecureRunAs *pRunAs, + const std::wstring &wideserver) + { + LPVOID pAuthBuffer; + ULONG ulPackageLen {}; + ULONG ulAuthLen {}; + bool bSecond {}; + CREDUI_INFOW info {}; + + if (!pCredUnPackAuthenticationBufferW || + !pCredUIPromptForWindowsCredentialsW || + !pCoTaskMemFree) + { + return EPrompt::eError; + } + + info.cbSize = sizeof(info); + info.pszCaptionText = L"User Account Control"; + info.pszMessageText = L""; + + AuString a { "Please log in to elevate the process to " }; + if (pRunAs->desc.runAs == ERunAsUser::eSpecifiedImpersonation) + { + if (pRunAs->desc.impersonate) + { + a += pRunAs->desc.impersonate.value(); + } + } + else if (pRunAs->desc.runAs == ERunAsUser::eRegularUser) + { + if (wideusername.size()) + { + a = fmt::format("Please log in to your account ({})", AuLocale::ConvertFromWChar(wideusername.data())); + } + else + { + a = "Please log in to your account"; + } + } + else if (pRunAs->desc.runAs == ERunAsUser::eSuperUser) + { + a += "Administrator status"; + } + else + { + switch (pRunAs->desc.runAs) + { + case ERunAsUser::eNTAS: + { + a += "NT Authority\\SYSTEM"; + break; + } + case ERunAsUser::eNTTI: + { + a += "Trusted Installer"; + break; + } + } + } + + auto tempMessageBuffer = AuLocale::ConvertFromUTF8(a); + info.pszMessageText = tempMessageBuffer.data(); + + HRESULT result = pCredUIPromptForWindowsCredentialsW(&info, + 0, + &ulPackageLen, + {}, 0, + &pAuthBuffer, &ulAuthLen, + nullptr, + 0x1); + if (result != ERROR_SUCCESS) + { + if (result == ERROR_CANCELLED) + { + return EPrompt::eCancel; + } + else + { + return EPrompt::eError; + } + } + + wideusername.resize(512); + widepassword.resize(512); + DWORD dwUsernameLen = wideusername.size(); + DWORD dwPasswordLen = widepassword.size(); + + bSecond = pCredUnPackAuthenticationBufferW(0, + pAuthBuffer, ulAuthLen, + wideusername.data(), &dwUsernameLen, + nullptr /*TODO: server*/, 0, + widepassword.data(), &dwPasswordLen); + + wideusername.resize(dwUsernameLen - 1); + widepassword.resize(dwPasswordLen - 1); + + AuMemset(pAuthBuffer, 0, ulAuthLen); + pCoTaskMemFree(pAuthBuffer); + + if (result == ERROR_SUCCESS && + bSecond) + { + return EPrompt::eOK; + } + else + { + return EPrompt::eError; + } + } + + static std::wstring GetUsername() + { + wchar_t buffer[512]; + DWORD dwLength = AuArraySize(buffer); + + if (!pGetUserNameW) + { + return {}; + } + + if (!pGetUserNameW(buffer, &dwLength)) + { + return {}; + } + + return { buffer, dwLength }; + } + + static bool GetSidOfUser(const std::wstring &username, + AuList buffer, + PSID *ppSid) + { + static const auto kDefaultSize = 32; + buffer.resize(kDefaultSize); + bool bOk {}; + + if (!pLookupAccountNameW) + { + return false; + } + + while (!bOk) + { + SID_NAME_USE eSidType; + AuList dmain; + dmain.resize(2024); + + DWORD dw = dmain.size(); + DWORD cbSid = buffer.size(); + + if (pLookupAccountNameW(NULL, + username.c_str(), + (PSID)buffer.data(), + &cbSid, + dmain.data(), + &dw, + &eSidType)) + { + bOk = true; + break; + } + + auto dwErrorCode = GetLastError(); + + if (dwErrorCode == ERROR_INSUFFICIENT_BUFFER) + { + buffer.resize(cbSid); + } + else + { + break; + } + } + + if (bOk) + { + *ppSid = (PSID)buffer.data(); + } + + return bOk; + } + + static HANDLE GetToken(DWORD pid) + { + HANDLE hToken; + + auto hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + if (!hProcess) + { + return {}; + } + + if (!pOpenProcessToken) + { + return {}; + } + + if (!pOpenProcessToken(hProcess, MAXIMUM_ALLOWED, &hToken)) + { + hToken = {}; + } + + CloseHandle(hProcess); + + return hToken; + } + + static void GetSelfSID(std::function callback) + { + auto hToken = GetProcessTokenHandle(); + + if (!pGetTokenInformation) + { + return; + } + + DWORD dwSize {}; + if (!pGetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize) && + GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + return; + } + + AuList buf(dwSize, '\x00'); + auto pTokenUser = (PTOKEN_USER)buf.data(); + if (!pGetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize)) + { + return; + } + + callback(pTokenUser->User.Sid); + } + + void SetRight(HANDLE hAdmin) + { + if (!pLsaOpenPolicy || + !pLsaAddAccountRights || + !pLsaClose || + !pRevertToSelf) + { + return; + } + + pRevertToSelf(); + + GetSelfSID([=](PSID pSelf) + { + if (pSetThreadToken) + { + pSetThreadToken(NULL, hAdmin); + } + + LSA_OBJECT_ATTRIBUTES attr {}; + LSA_HANDLE hHandle; + + if (pLsaOpenPolicy(NULL, &attr, POLICY_ALL_ACCESS, &hHandle) == 0) + { + LSA_UNICODE_STRING unistr; + unistr.Buffer = (PWSTR)L"SeAssignPrimaryTokenPrivilege"; + unistr.Length = 29 * 2; + unistr.MaximumLength = 29 * 2; + + if (pLsaAddAccountRights(hHandle, + pSelf, + &unistr, + 1) == 0) + { + this->bRepeat = true; + } + } + + pLsaClose(hHandle); + }); + } + + static AuString GetProcessUserName(DWORD pid) + { + AuString username; + wchar_t lpName[MAX_PATH]; + wchar_t lpDomain[MAX_PATH]; + DWORD dwNameSize { MAX_PATH }; + DWORD dwDomainSize { MAX_PATH }; + SID_NAME_USE SidType; + + if (!pLookupAccountSidW || + !pOpenProcessToken || + !pGetTokenInformation) + { + return {}; + } + + HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + if (!hProcess) + { + return {}; + } + + HANDLE hToken {}; + if (!pOpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) + { + CloseHandle(hProcess); + return {}; + } + + DWORD dwSize {}; + if (!pGetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize) && + GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + CloseHandle(hToken); + CloseHandle(hProcess); + return {}; + } + + AuList buf(dwSize, '\x00'); + auto pTokenUser = (PTOKEN_USER)buf.data(); + if (!pGetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize)) + { + CloseHandle(hToken); + CloseHandle(hProcess); + return {}; + } + + if (!pLookupAccountSidW(NULL, pTokenUser->User.Sid, lpName, &dwNameSize, lpDomain, &dwDomainSize, &SidType)) + { + CloseHandle(hToken); + CloseHandle(hProcess); + return {}; + } + + username = AuLocale::ConvertFromWChar(lpDomain); + username += "/"; + username += AuLocale::ConvertFromWChar(lpName); + + CloseHandle(hToken); + CloseHandle(hProcess); + return username; + } + + void CachePids() + { + HANDLE hProcSnap; + PROCESSENTRY32 pe32; + + if (this->pids.size()) + { + return; + } + + hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + pe32.dwSize = sizeof(PROCESSENTRY32); + + if (!Process32First(hProcSnap, &pe32)) + { + CloseHandle(hProcSnap); + return; + } + + while (Process32Next(hProcSnap, &pe32)) + { + if (AuToLower(GetProcessUserName(pe32.th32ProcessID)) == "nt authority/system") + { + this->pids.push_back(pe32.th32ProcessID); + } + } + + CloseHandle(hProcSnap); + } + + static bool EnableTokenPrivilege(HANDLE hToken, + const char *lpName) + { + LUID luidValue { }; + TOKEN_PRIVILEGES tokenPrivileges; + + if (!pLookupPrivilegeValueA || + !pAdjustTokenPrivileges) + { + return {}; + } + + if (!pLookupPrivilegeValueA(NULL, + lpName, + &luidValue)) + { + return {}; + } + + tokenPrivileges.PrivilegeCount = 1; + tokenPrivileges.Privileges[0].Luid = luidValue; + tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + return pAdjustTokenPrivileges(hToken, + FALSE, + &tokenPrivileges, + sizeof(tokenPrivileges), + NULL, + NULL); + } + + static void AddGenericElPerms(HANDLE hHandle) + { + EnableTokenPrivilege(hHandle, SE_DEBUG_NAME); + EnableTokenPrivilege(hHandle, SE_CREATE_TOKEN_NAME); + EnableTokenPrivilege(hHandle, SE_INCREASE_QUOTA_NAME); + EnableTokenPrivilege(hHandle, SE_ASSIGNPRIMARYTOKEN_NAME); + EnableTokenPrivilege(hHandle, SE_TCB_NAME); + EnableTokenPrivilege(hHandle, SE_IMPERSONATE_NAME); + } + + static void AddAllTokenPrivileges(HANDLE hHandle) + { + EnableTokenPrivilege(hHandle, SE_CREATE_TOKEN_NAME); + EnableTokenPrivilege(hHandle, SE_ASSIGNPRIMARYTOKEN_NAME); + EnableTokenPrivilege(hHandle, SE_LOCK_MEMORY_NAME); + EnableTokenPrivilege(hHandle, SE_INCREASE_QUOTA_NAME); + EnableTokenPrivilege(hHandle, SE_UNSOLICITED_INPUT_NAME); + EnableTokenPrivilege(hHandle, SE_MACHINE_ACCOUNT_NAME); + EnableTokenPrivilege(hHandle, SE_TCB_NAME); + EnableTokenPrivilege(hHandle, SE_SECURITY_NAME); + EnableTokenPrivilege(hHandle, SE_TAKE_OWNERSHIP_NAME); + EnableTokenPrivilege(hHandle, SE_LOAD_DRIVER_NAME); + EnableTokenPrivilege(hHandle, SE_SYSTEM_PROFILE_NAME); + EnableTokenPrivilege(hHandle, SE_SYSTEMTIME_NAME); + EnableTokenPrivilege(hHandle, SE_PROF_SINGLE_PROCESS_NAME); + EnableTokenPrivilege(hHandle, SE_INC_BASE_PRIORITY_NAME); + EnableTokenPrivilege(hHandle, SE_CREATE_PAGEFILE_NAME); + EnableTokenPrivilege(hHandle, SE_CREATE_PERMANENT_NAME); + EnableTokenPrivilege(hHandle, SE_BACKUP_NAME); + EnableTokenPrivilege(hHandle, SE_RESTORE_NAME); + EnableTokenPrivilege(hHandle, SE_SHUTDOWN_NAME); + EnableTokenPrivilege(hHandle, SE_DEBUG_NAME); + EnableTokenPrivilege(hHandle, SE_AUDIT_NAME); + EnableTokenPrivilege(hHandle, SE_SYSTEM_ENVIRONMENT_NAME); + EnableTokenPrivilege(hHandle, SE_CHANGE_NOTIFY_NAME); + EnableTokenPrivilege(hHandle, SE_REMOTE_SHUTDOWN_NAME); + EnableTokenPrivilege(hHandle, SE_UNDOCK_NAME); + EnableTokenPrivilege(hHandle, SE_SYNC_AGENT_NAME); + EnableTokenPrivilege(hHandle, SE_ENABLE_DELEGATION_NAME); + EnableTokenPrivilege(hHandle, SE_MANAGE_VOLUME_NAME); + EnableTokenPrivilege(hHandle, SE_IMPERSONATE_NAME); + EnableTokenPrivilege(hHandle, SE_CREATE_GLOBAL_NAME); + EnableTokenPrivilege(hHandle, SE_TRUSTED_CREDMAN_ACCESS_NAME); + EnableTokenPrivilege(hHandle, SE_RELABEL_NAME); + EnableTokenPrivilege(hHandle, SE_INC_WORKING_SET_NAME); + EnableTokenPrivilege(hHandle, SE_TIME_ZONE_NAME); + EnableTokenPrivilege(hHandle, SE_CREATE_SYMBOLIC_LINK_NAME); + EnableTokenPrivilege(hHandle, SE_DELEGATE_SESSION_USER_IMPERSONATE_NAME); + } + + bool TrySpawn(AuFunction trySpawn, HANDLE hAdminToken) + { + CachePids(); + + if (!pSetThreadToken || + !pRevertToSelf) + { + return false; + } + + if (hAdminToken) + { + AddGenericElPerms(hAdminToken); + } + + for (const auto &pid : pids) + { + HANDLE hToken = GetToken(pid); + if (!hToken) + { + continue; + } + + AddGenericElPerms(hToken); + + pSetThreadToken(NULL, hToken); + + auto bSuccess = trySpawn(hToken); + + if (hAdminToken) + { + pSetThreadToken(NULL, hAdminToken); + } + else + { + pRevertToSelf(); + } + + CloseHandle(hToken); + + if (bSuccess) + { + return true; + } + } + + return false; + } + + static bool GetCurrentSessionId(DWORD &dwSessionId) + { + DWORD dwLength {}; + return pGetTokenInformation && + pGetTokenInformation(GetProcessTokenHandle(), + TokenSessionId, + &dwSessionId, + sizeof(DWORD), + &dwLength); + + } + + BOOL Exec(LPCWSTR lpApplicationName, + LPWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation) + { + DWORD dwSesssionId; + GetCurrentSessionId(dwSesssionId); + + std::wstring widepassword; + std::wstring wideusername; + std::wstring wideserver; + DWORD dwResult {}; + bool bResult {}; + wchar_t *pWideServer {}; + bool bRequiresUnprivAuth { true }; + bool bUseUsualTokenLogin { false }; + bool bRetToSelf { false }; + bool bEsclToAdmin { false }; + bool bDoAdmnTokenLookup { false }; + bool bIsSpecialAccount { false }; + bool bPrompting { false }; + HANDLE hBestToken { INVALID_HANDLE_VALUE }; + DWORD dwLoginFlags { LOGON_NETCREDENTIALS_ONLY }; + + dwCreationFlags |= CREATE_BREAKAWAY_FROM_JOB | + CREATE_DEFAULT_ERROR_MODE | + CREATE_NEW_CONSOLE | + CREATE_NEW_PROCESS_GROUP; + + if (this->desc.bLoginWithProfile) + { + static wchar_t lpDesktop[] = L"WinSta0\\Default"; + + dwLoginFlags = LOGON_WITH_PROFILE; + lpStartupInfo->lpDesktop = lpDesktop; + } + + + if (this->desc.username) + { + wideusername = AuLocale::ConvertFromUTF8(this->desc.username.value().c_str()); + } + + if (this->desc.password) + { + widepassword = AuLocale::ConvertFromUTF8(this->desc.password.value().c_str()); + } + + if (this->desc.server) + { + wideserver = AuLocale::ConvertFromUTF8(this->desc.server.value().c_str()); + } + + if (widepassword.empty() || + wideusername.empty()) + { + EPrompt status; + + if (wideusername.empty()) + { + wideusername = GetUsername(); + } + + bPrompting = true; + + do + { + status = PromptForCreds(wideusername, widepassword, this, wideserver); + + if (status == EPrompt::eCancel) + { + return false; + } + + if (status == EPrompt::eError) + { + SysPushErrorGeneric("Generic logon prompt failure"); + return false; + } + } + while (wideusername.empty() && + status == EPrompt::eOK); + } + + if (wideserver.empty() || + wideserver == L"." || + wideserver == L"localhost") + { + wideserver = L"."; + pWideServer = nullptr; + } + else + { + pWideServer = wideserver.data(); + } + + if (IsAdmn()) + { + bRequiresUnprivAuth = false; + } + + if (wideusername.empty()) + { + SysPushErrorPermissionError("Missing Username"); + return false; + } + + if (bRequiresUnprivAuth) + { + do + { + if (!pLogonUserW(wideusername.c_str(), + pWideServer, + widepassword.c_str(), + LOGON32_LOGON_NETWORK_CLEARTEXT, + LOGON32_PROVIDER_DEFAULT, + &hBestToken)) + { + auto dwErrorCode = GetLastError(); + + if (dwErrorCode == 1327) + { + SysPushErrorPermissionError("You must have a password or modify COMPUTER\\HKLM\\SYSTEM\\CurrentControlSet\\Control\\Lsa\\LimitBlankPasswordUse=1"); + return false; + } + + if (dwErrorCode == 1326) + { + if (bPrompting) + { + auto status = PromptForCreds(wideusername, widepassword, this, wideserver); + + if (status == EPrompt::eCancel || + wideusername.empty()) + { + return false; + } + + if (status == EPrompt::eError) + { + SysPushErrorGeneric("Generic logon prompt failure"); + return false; + } + + continue; + } + + SysPushErrorPermissionError("Invalid username or password. This event has been logged."); + return false; + } + + SysPushErrorGeneric("Unable to login"); + return false; + } + else + { + if (this->desc.runAs != ERunAsUser::eRegularUser) + { + bEsclToAdmin = true; + + if (this->desc.runAs != ERunAsUser::eSuperUser) + { + bDoAdmnTokenLookup = true; + bIsSpecialAccount = true; + } + } + + break; + } + } + while (bPrompting); + } + else + { + bDoAdmnTokenLookup = true; + } + + if (bEsclToAdmin) + { + if (!SetTokenIntegrityLevel(hBestToken, + GetTokenIntegrityLevel())) + { + SysPushErrorPermissionError("Unable to elevate"); + return false; + } + + if (pSetSecurityInfo((HANDLE)-1, + SE_KERNEL_OBJECT, + DACL_SECURITY_INFORMATION, + {}, {}, {}, {}) != ERROR_SUCCESS) + { + SysPushErrorPermissionError("Unable to elevate"); + return false; + } + + bRetToSelf = true; + } + + DWORD dwErrorCode {}; + if (bIsSpecialAccount) + { + EnableTokenPrivilege(GetProcessTokenHandle(), SE_DEBUG_NAME); + + CachePids(); + + SetRight(hBestToken); + + if (this->bRepeat) + { + CloseHandle(hBestToken); + + if (!pLogonUserW(wideusername.c_str(), + pWideServer, + widepassword.c_str(), + LOGON32_LOGON_NETWORK_CLEARTEXT, + LOGON32_PROVIDER_DEFAULT, + &hBestToken)) + { + return false; + } + + + if (!SetTokenIntegrityLevel(hBestToken, + GetTokenIntegrityLevel())) + { + SysPushErrorPermissionError("Unable to elevate"); + return false; + } + } + + TrySpawn([&](HANDLE h) -> bool + { + HANDLE hToken2; + if (!pDuplicateTokenEx(h, + MAXIMUM_ALLOWED, + NULL, + SecurityImpersonation, + TokenPrimary, + &hToken2)) + { + return false; + } + + (void)pSetTokenInformation(hToken2, TokenSessionId, &dwSesssionId, sizeof(dwSesssionId)); + + pSetThreadToken(NULL, hToken2); + AddAllTokenPrivileges(hToken2); + + pSetThreadToken(NULL, hBestToken); + bResult = pCreateProcessAsUserW(hToken2, + lpApplicationName, + lpCommandLine, + 0, + NULL, + TRUE, + dwCreationFlags, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation); + dwErrorCode = GetLastError(); + CloseHandle(hToken2); + return bResult; + }, NULL); + + bUseUsualTokenLogin = true; + } + + + if (bUseUsualTokenLogin) + { + // Moved + if (!bResult) + { + SetLastError(dwErrorCode); + } + } + else + { + pSetThreadToken(NULL, hBestToken); + AddAllTokenPrivileges(hBestToken); + bResult = pCreateProcessWithLogonW(wideusername.c_str(), + wideserver.data(), + widepassword.c_str(), + dwLoginFlags, + lpApplicationName, + lpCommandLine, + dwCreationFlags, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation); + } + + if (bRetToSelf) + { + pRevertToSelf(); + } + + AuWin32CloseHandle(hBestToken); + + if (!bResult) + { + SysPushErrorGeneric("Couldn't spawn elevated process"); + } + + return bResult; + } + + void FixSharedAttrs(SECURITY_ATTRIBUTES *pAttrs) + { + if (pInitializeSecurityDescriptor && + pSetSecurityDescriptorDacl) + { + // let everybody on the system read our shared pipes (the easiest way to fixup inter-privileged ipc) + pAttrs->lpSecurityDescriptor = &this->sd; + pInitializeSecurityDescriptor(&this->sd, SECURITY_DESCRIPTOR_REVISION); + pSetSecurityDescriptorDacl(&this->sd, TRUE, NULL, FALSE); + } + } + }; + + AUKN_SYM void RunAs(StartupParameters &startupParameters, + RunAsDescriptor &runAs) + { + auto pThat = AuMakeSharedThrow(); + pThat->desc = AuMove(runAs); + startupParameters.bForceNoJobParent = true; + startupParameters.ntLikeHookCreateProcessW = std::bind(&SecureRunAs::Exec, pThat, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3, std::placeholders::_4, + std::placeholders::_5, std::placeholders::_6, + std::placeholders::_7, std::placeholders::_8, + std::placeholders::_9, std::placeholders::_10); + startupParameters.ntFixSharedHandleAttrs = std::bind(&SecureRunAs::FixSharedAttrs, pThat, + std::placeholders::_1); + + } +} \ No newline at end of file diff --git a/Source/Processes/AuProcessElevation.NT.hpp b/Source/Processes/AuProcessElevation.NT.hpp new file mode 100644 index 00000000..63de3ba3 --- /dev/null +++ b/Source/Processes/AuProcessElevation.NT.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuProcessElevation.NT.hpp + Date: 2023-12-23 + Author: Reece +***/ +#pragma once + +namespace Aurora::Processes +{ + +} \ No newline at end of file