[+] AuProcesses::RunAs

This commit is contained in:
Reece Wilson 2023-12-28 21:00:07 +00:00
parent 662dbac0c1
commit f404e8960f
11 changed files with 1358 additions and 5 deletions

View File

@ -14,5 +14,6 @@
#include "StartupParameters.hpp" #include "StartupParameters.hpp"
#include "Spawn.hpp" #include "Spawn.hpp"
#include "OutputOf.hpp" #include "OutputOf.hpp"
#include "RunAs.hpp"
#include "Open.hpp" #include "Open.hpp"

View File

@ -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<int> numericUserId;
AuOptional<AuString> username;
AuOptional<AuString> password;
AuOptional<AuString> server;
AuOptional<AuString> impersonate;
AuOptional<int> 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<AuString> password; to hopefully purge it out of memory asap @
} // SECURITY @ Noting that AuOptional<AuString> password isn't memory safe. We should probably try to memset it after use and during move @
// REGRESSION: make runAs move-only again

View File

@ -81,6 +81,7 @@ namespace Aurora::Processes
LPCWSTR, LPCWSTR,
LPSTARTUPINFOW, LPSTARTUPINFOW,
LPPROCESS_INFORMATION> ntLikeHookCreateProcessW; LPPROCESS_INFORMATION> ntLikeHookCreateProcessW;
AuConsumer<_SECURITY_ATTRIBUTES *> ntFixSharedHandleAttrs;
#else #else
AuSupplierConsumer<int, AuSupplierConsumer<int,
void *, void *,
@ -93,10 +94,14 @@ namespace Aurora::Processes
void *, void *,
void *, void *,
void *> ntLikeHookCreateProcessW; void *> ntLikeHookCreateProcessW;
AuConsumer<void *> ntFixSharedHandleAttrs;
#endif #endif
bool bForceNoJobParent {};
#endif #endif
#if defined(AURORA_IS_POSIX_DERIVED) #if defined(AURORA_IS_POSIX_DERIVED)
AuVoidFunc posixApplySandboxCOW; AuVoidFunc posixApplySandboxCOW;
AuVoidFunc posixPCA;
AuVoidFunc posixPCB;
#endif #endif
}; };
} }

View File

@ -168,6 +168,7 @@ namespace Aurora
ADD_LOAD_LIB(User32); ADD_LOAD_LIB(User32);
ADD_LOAD_LIB(SetupAPI); ADD_LOAD_LIB(SetupAPI);
ADD_LOAD_LIB(Router); ADD_LOAD_LIB(Router);
ADD_LOAD_LIB(CredUI);
#define ADD_GET_PROC(name, proc) \ #define ADD_GET_PROC(name, proc) \
if (!IsBlocked(#proc)) \ if (!IsBlocked(#proc)) \
@ -333,6 +334,30 @@ namespace Aurora
ADD_GET_PROC(AdvancedApi, SetNamedSecurityInfoW) ADD_GET_PROC(AdvancedApi, SetNamedSecurityInfoW)
ADD_GET_PROC(AdvancedApi, FreeSid) 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_INTERNAL_MAP(AdvancedApi, RtlGenRandom, SystemFunction036)
ADD_GET_PROC(BCrypt, BCryptGenRandom) ADD_GET_PROC(BCrypt, BCryptGenRandom)
@ -373,6 +398,8 @@ namespace Aurora
ADD_GET_PROC(Router, WNetOpenEnumW); ADD_GET_PROC(Router, WNetOpenEnumW);
ADD_GET_PROC(Router, WNetGetUniversalNameW); ADD_GET_PROC(Router, WNetGetUniversalNameW);
ADD_GET_PROC(CredUI, CredUIPromptForWindowsCredentialsW);
ADD_GET_PROC(CredUI, CredUnPackAuthenticationBufferW);
if (pNtCreateKeyedEvent && if (pNtCreateKeyedEvent &&
Threading::Primitives::gKeyedEventHandle == INVALID_HANDLE_VALUE) Threading::Primitives::gKeyedEventHandle == INVALID_HANDLE_VALUE)

View File

@ -23,9 +23,11 @@ struct _SP_DEVINFO_DATA;
struct _SP_DEVICE_INTERFACE_DATA; struct _SP_DEVICE_INTERFACE_DATA;
struct _SP_DEVICE_INTERFACE_DETAIL_DATA_W; struct _SP_DEVICE_INTERFACE_DETAIL_DATA_W;
struct _NETRESOURCEW; struct _NETRESOURCEW;
enum _TOKEN_INFORMATION_CLASS;
enum _SE_OBJECT_TYPE; enum _SE_OBJECT_TYPE;
enum _MINIDUMP_TYPE; enum _MINIDUMP_TYPE;
enum _OBJECT_WAIT_TYPE; enum _OBJECT_WAIT_TYPE;
enum _SE_OBJECT_TYPE;
//#if defined(AURORA_COMPILER_MSVC) //#if defined(AURORA_COMPILER_MSVC)
struct _IP_ADAPTER_ADDRESSES_LH; struct _IP_ADAPTER_ADDRESSES_LH;
@ -42,6 +44,21 @@ enum _OBJECT_WAIT_TYPE;
#endif #endif
//#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 namespace Aurora
{ {
void InitNTAddresses(); void InitNTAddresses();
@ -64,6 +81,7 @@ namespace Aurora
static const wchar_t *kUser32DllName { L"User32.dll" }; static const wchar_t *kUser32DllName { L"User32.dll" };
static const wchar_t *kSetupAPIDllName { L"SETUPAPI.dll" }; static const wchar_t *kSetupAPIDllName { L"SETUPAPI.dll" };
static const wchar_t *kRouterDllName { L"MPR.dll" }; static const wchar_t *kRouterDllName { L"MPR.dll" };
static const wchar_t *kCredUIDllName { L"credui.dll" };
struct WIN32_MEMORY_RANGE_ENTRY2 struct WIN32_MEMORY_RANGE_ENTRY2
{ {
@ -665,6 +683,172 @@ namespace Aurora
VOID * pSid 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 // 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] // [then give up and write a win32 emulator in a memelang, probably]
@ -973,6 +1157,33 @@ namespace Aurora
LPINT lpAddressLength 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 gUseNativeWaitMutex {};
inline bool gUseNativeWaitCondvar {}; inline bool gUseNativeWaitCondvar {};
inline bool gUseNativeWaitSemapahore {}; inline bool gUseNativeWaitSemapahore {};

View File

@ -235,6 +235,12 @@ namespace Aurora::Processes
return WriteFile(this->pipeStdInWrite_, in.ptr, size, &size, NULL) && size == in.length; 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() bool ProcessImpl::Init()
{ {
SECURITY_ATTRIBUTES saAttr {}; SECURITY_ATTRIBUTES saAttr {};
@ -260,6 +266,11 @@ namespace Aurora::Processes
return false; return false;
} }
if (this->startup_.ntFixSharedHandleAttrs)
{
this->startup_.ntFixSharedHandleAttrs(&saAttr);
}
this->exitCode_ = 0x10110100; this->exitCode_ = 0x10110100;
if (this->startup_.fwdOut == EStreamForward::eAsyncPipe) if (this->startup_.fwdOut == EStreamForward::eAsyncPipe)
{ {
@ -637,8 +648,7 @@ namespace Aurora::Processes
uCreateFlags |= CREATE_UNICODE_ENVIRONMENT; uCreateFlags |= CREATE_UNICODE_ENVIRONMENT;
} }
BOOL result {}; DWORD result;
if (auto &func = this->startup_.ntLikeHookCreateProcessW) if (auto &func = this->startup_.ntLikeHookCreateProcessW)
{ {
result = func(Locale::ConvertFromUTF8(this->startup_.process).c_str(), result = func(Locale::ConvertFromUTF8(this->startup_.process).c_str(),
@ -667,10 +677,12 @@ namespace Aurora::Processes
&this->processInfo_); &this->processInfo_);
} }
this->RestLeakyMem();
if (!result) if (!result)
{ {
DWORD wr = GetLastError(); DWORD wr = GetLastError();
SysPushErrorGen("CreateProcess failed"); SysPushErrorGen("CreateProcess failed: {}", wr);
return false; return false;
} }
@ -712,7 +724,8 @@ namespace Aurora::Processes
return false; return false;
} }
if (this->type_ == ESpawnType::eSpawnChildProcessWorker) if (this->type_ == ESpawnType::eSpawnChildProcessWorker &&
!this->startup_.bForceNoJobParent)
{ {
#if defined(AURORA_PLATFORM_WIN32) #if defined(AURORA_PLATFORM_WIN32)
AssignJobWorker(this->processInfo_.hProcess); AssignJobWorker(this->processInfo_.hProcess);
@ -790,6 +803,7 @@ namespace Aurora::Processes
if (!hi->Init()) if (!hi->Init())
{ {
hi->RestLeakyMem();
delete hi; delete hi;
return {}; return {};
} }

View File

@ -44,7 +44,7 @@ namespace Aurora::Processes
bool Start() override; bool Start() override;
bool Init(); bool Init();
void RestLeakyMem();
void RelOtherHandles(); void RelOtherHandles();
private: private:

View File

@ -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 <Source/RuntimeInternal.hpp>
#include "AuProcessElevation.Linux.hpp"
namespace Aurora::Processes
{
AUKN_SYM void RunAs(StartupParameters &startupParameters,
RunAsDescriptor &runAs)
{
// ...
}
}

View File

@ -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
{
}

View File

@ -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 <Source/RuntimeInternal.hpp>
#include "AuProcessElevation.NT.hpp"
#include <tlhelp32.h>
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<int> 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<AuUInt8> buffer,
PSID *ppSid)
{
static const auto kDefaultSize = 32;
buffer.resize(kDefaultSize);
bool bOk {};
if (!pLookupAccountNameW)
{
return false;
}
while (!bOk)
{
SID_NAME_USE eSidType;
AuList<wchar_t> 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<void(PSID)> callback)
{
auto hToken = GetProcessTokenHandle();
if (!pGetTokenInformation)
{
return;
}
DWORD dwSize {};
if (!pGetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize) &&
GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
return;
}
AuList<AuInt8> 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<AuInt8> 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<bool(HANDLE)> 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<SecureRunAs>();
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);
}
}

View File

@ -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
{
}