2051 lines
62 KiB
C
2051 lines
62 KiB
C
/*
|
|
* Copyright 2004-2020 Sandboxie Holdings, LLC
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
#include "dll.h"
|
|
#include "obj.h"
|
|
#include "core/drv/api_defs.h"
|
|
#include "core/svc/ServiceWire.h"
|
|
#include <stdio.h>
|
|
#include <objbase.h>
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Defines
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
#define LDR_TOKEN_PRIMARY -4
|
|
#define LDR_TOKEN_IMPERSONATION -5
|
|
#define LDR_TOKEN_EFFECTIVE -6
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Functions
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
static NTSTATUS Secure_NtOpenProcess(
|
|
HANDLE *ProcessHandle,
|
|
ACCESS_MASK DesiredAccess,
|
|
OBJECT_ATTRIBUTES *ObjectAttributes,
|
|
CLIENT_ID *ClientId);
|
|
|
|
static NTSTATUS Secure_NtOpenThread(
|
|
HANDLE *ThreadHandle,
|
|
ACCESS_MASK DesiredAccess,
|
|
OBJECT_ATTRIBUTES *ObjectAttributes,
|
|
CLIENT_ID *ClientId);
|
|
|
|
static NTSTATUS Secure_NtDuplicateObject(
|
|
HANDLE SourceProcessHandle,
|
|
HANDLE SourceHandle,
|
|
HANDLE TargetProcessHandle,
|
|
HANDLE *TargetHandle,
|
|
ACCESS_MASK DesiredAccess,
|
|
ULONG HandleAttributes,
|
|
ULONG Options);
|
|
|
|
static NTSTATUS Secure_NtQuerySecurityObject(
|
|
HANDLE Handle,
|
|
SECURITY_INFORMATION SecurityInformation,
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
ULONG Length,
|
|
ULONG *LengthNeeded);
|
|
|
|
static NTSTATUS Secure_NtSetSecurityObject(
|
|
HANDLE Handle,
|
|
SECURITY_INFORMATION SecurityInformation,
|
|
SECURITY_DESCRIPTOR *SecurityDescriptor);
|
|
|
|
NTSTATUS Ldr_NtAccessCheckByType(
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
PSID PrincipalSelfSid,
|
|
HANDLE ClientToken,
|
|
ACCESS_MASK DesiredAccess,
|
|
POBJECT_TYPE_LIST ObjectTypeList,
|
|
ULONG ObjectTypeListLength,
|
|
PGENERIC_MAPPING GenericMapping,
|
|
PPRIVILEGE_SET PrivilegeSet,
|
|
PULONG PrivilegeSetLength,
|
|
PACCESS_MASK GrantedAccess,
|
|
PNTSTATUS AccessStatus
|
|
);
|
|
|
|
NTSTATUS Ldr_NtAccessCheckByTypeResultList(
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
PSID PrincipalSelfSid,
|
|
HANDLE ClientToken,
|
|
ACCESS_MASK DesiredAccess,
|
|
POBJECT_TYPE_LIST ObjectTypeList,
|
|
ULONG ObjectTypeListLength,
|
|
PGENERIC_MAPPING GenericMapping,
|
|
PPRIVILEGE_SET PrivilegeSet,
|
|
PULONG PrivilegeSetLength,
|
|
PACCESS_MASK GrantedAccess,
|
|
PNTSTATUS AccessStatus
|
|
);
|
|
|
|
NTSTATUS Ldr_NtAccessCheck(
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN HANDLE ClientToken,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN PGENERIC_MAPPING GenericMapping OPTIONAL,
|
|
OUT PPRIVILEGE_SET RequiredPrivilegesBuffer,
|
|
IN OUT PULONG BufferLength,
|
|
OUT PACCESS_MASK GrantedAccess,
|
|
OUT PNTSTATUS AccessStatus);
|
|
|
|
NTSTATUS Ldr_NtQuerySecurityAttributesToken(
|
|
IN HANDLE TokenHandle,
|
|
IN PUNICODE_STRING Attributes,
|
|
IN ULONG NumberOfAttributes,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG ReturnLength);
|
|
|
|
NTSTATUS Ldr_NtQueryInformationToken(
|
|
HANDLE TokenHandle,
|
|
TOKEN_INFORMATION_CLASS TokenInformationClass,
|
|
void *TokenInformation,
|
|
ULONG TokenInformationLength,
|
|
ULONG *ReturnLength);
|
|
|
|
static BOOL Ldr_NtOpenThreadToken(
|
|
HANDLE ThreadHandle,
|
|
DWORD DesiredAccess,
|
|
BOOL OpenAsSelf,
|
|
PHANDLE TokenHandle);
|
|
|
|
static BOOL Ldr_RtlEqualSid(void * sid1, void * sid2);
|
|
|
|
static NTSTATUS Secure_NtSetInformationToken(
|
|
HANDLE TokenHandle,
|
|
TOKEN_INFORMATION_CLASS TokenInformationClass,
|
|
void *TokenInformation,
|
|
ULONG TokenInformationLength);
|
|
|
|
static NTSTATUS Secure_NtAdjustPrivilegesToken(
|
|
HANDLE TokenHandle,
|
|
BOOLEAN DisableAllPrivileges,
|
|
TOKEN_PRIVILEGES *NewState,
|
|
ULONG BufferLength,
|
|
TOKEN_PRIVILEGES *PreviousState,
|
|
ULONG *ReturnLength);
|
|
|
|
static NTSTATUS Secure_NtDuplicateToken(
|
|
_In_ HANDLE ExistingTokenHandle,
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
|
|
_In_ BOOLEAN EffectiveOnly,
|
|
_In_ TOKEN_TYPE TokenType,
|
|
_Out_ PHANDLE NewTokenHandle);
|
|
|
|
static NTSTATUS Secure_NtFilterToken(
|
|
_In_ HANDLE ExistingTokenHandle,
|
|
_In_ ULONG Flags,
|
|
_In_opt_ PTOKEN_GROUPS SidsToDisable,
|
|
_In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
|
|
_In_opt_ PTOKEN_GROUPS RestrictedSids,
|
|
_Out_ PHANDLE NewTokenHandle);
|
|
|
|
/*static NTSTATUS Secure_NtFilterTokenEx(
|
|
_In_ HANDLE ExistingTokenHandle,
|
|
_In_ ULONG Flags,
|
|
_In_opt_ PTOKEN_GROUPS SidsToDisable,
|
|
_In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
|
|
_In_opt_ PTOKEN_GROUPS RestrictedSids,
|
|
_In_ ULONG DisableUserClaimsCount,
|
|
_In_opt_ PUNICODE_STRING UserClaimsToDisable,
|
|
_In_ ULONG DisableDeviceClaimsCount,
|
|
_In_opt_ PUNICODE_STRING DeviceClaimsToDisable,
|
|
_In_opt_ PTOKEN_GROUPS DeviceGroupsToDisable,
|
|
_In_opt_ PVOID RestrictedUserAttributes,
|
|
_In_opt_ PVOID RestrictedDeviceAttributes,
|
|
_In_opt_ PTOKEN_GROUPS RestrictedDeviceGroups,
|
|
_Out_ PHANDLE NewTokenHandle);*/
|
|
|
|
static NTSTATUS Secure_RtlQueryElevationFlags(ULONG *Flags);
|
|
|
|
static NTSTATUS Secure_RtlCheckTokenMembershipEx(
|
|
HANDLE tokenHandle,
|
|
PSID sidToCheck,
|
|
DWORD flags,
|
|
PUCHAR isMember);
|
|
|
|
static BOOLEAN Secure_IsSameBox(HANDLE idProcess);
|
|
|
|
static BOOLEAN Secure_IsBuiltInAdmin();
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
static P_NtOpenProcess __sys_NtOpenProcess = NULL;
|
|
static P_NtOpenThread __sys_NtOpenThread = NULL;
|
|
static P_NtDuplicateObject __sys_NtDuplicateObject = NULL;
|
|
static P_NtQuerySecurityObject __sys_NtQuerySecurityObject = NULL;
|
|
static P_NtSetSecurityObject __sys_NtSetSecurityObject = NULL;
|
|
static P_NtAccessCheckByType __sys_NtAccessCheckByType = NULL;
|
|
static P_NtAccessCheck __sys_NtAccessCheck = NULL;
|
|
static P_NtQuerySecurityAttributesToken __sys_NtQuerySecurityAttributesToken = NULL;
|
|
static P_NtQueryInformationToken __sys_NtQueryInformationToken = NULL;
|
|
static P_NtAccessCheckByTypeResultList __sys_NtAccessCheckByTypeResultList = NULL;
|
|
static P_NtOpenThreadToken __sys_NtOpenThreadToken = NULL;
|
|
P_RtlEqualSid __sys_RtlEqualSid = NULL;
|
|
static P_NtSetInformationToken __sys_NtSetInformationToken = NULL;
|
|
static P_NtAdjustPrivilegesToken __sys_NtAdjustPrivilegesToken = NULL;
|
|
static P_NtDuplicateToken __sys_NtDuplicateToken = NULL;
|
|
static P_NtFilterToken __sys_NtFilterToken = NULL;
|
|
static P_RtlQueryElevationFlags __sys_RtlQueryElevationFlags = NULL;
|
|
static P_RtlCheckTokenMembershipEx __sys_RtlCheckTokenMembershipEx = NULL;
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Variables
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
PSECURITY_DESCRIPTOR Secure_NormalSD = NULL;
|
|
|
|
PSECURITY_DESCRIPTOR Secure_EveryoneSD = NULL;
|
|
|
|
BOOLEAN Secure_IsInternetExplorerTabProcess = FALSE;
|
|
BOOLEAN Secure_Is_IE_NtQueryInformationToken = FALSE;
|
|
|
|
BOOLEAN Secure_FakeAdmin = FALSE;
|
|
|
|
static UCHAR AdministratorsSid[16] = {
|
|
1, // Revision
|
|
2, // SubAuthorityCount
|
|
0,0,0,0,0,5, // SECURITY_NT_AUTHORITY // IdentifierAuthority
|
|
0x20, 0, 0, 0, // SubAuthority 1 - SECURITY_BUILTIN_DOMAIN_RID
|
|
0x20, 2, 0, 0 // SubAuthority 2 - DOMAIN_ALIAS_RID_ADMINS
|
|
};
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_InitSecurityDescriptors
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
void Secure_InitSecurityDescriptors(void)
|
|
{
|
|
typedef (*P_RtlAddMandatoryAce)(
|
|
PACL Acl, ULONG AceRevision, ULONG AceFlags,
|
|
PSID Sid, ULONG AceType, ULONG MandatoryPolicy);
|
|
|
|
PACL MyAcl;
|
|
P_RtlAddMandatoryAce pRtlAddMandatoryAce;
|
|
|
|
static UCHAR AuthenticatedUsersSid[12] = {
|
|
1, // Revision
|
|
1, // SubAuthorityCount
|
|
0,0,0,0,0,5, // SECURITY_NT_AUTHORITY // IdentifierAuthority
|
|
SECURITY_AUTHENTICATED_USER_RID // SubAuthority
|
|
};
|
|
static UCHAR EveryoneSid[12] = {
|
|
1, // Revision
|
|
1, // SubAuthorityCount
|
|
0,0,0,0,0,1, // SECURITY_WORLD_SID_AUTHORITY
|
|
SECURITY_WORLD_RID // SubAuthority
|
|
};
|
|
/*static UCHAR RestrictedSid[12] = {
|
|
1, // Revision
|
|
1, // SubAuthorityCount
|
|
0,0,0,0,0,5, // SECURITY_NT_AUTHORITY // IdentifierAuthority
|
|
SECURITY_RESTRICTED_CODE_RID // SubAuthority
|
|
};*/
|
|
static UCHAR LowLabelSid[12] = {
|
|
1, // Revision
|
|
1, // SubAuthorityCount
|
|
0,0,0,0,0,16,// SECURITY_MANDATORY_LABEL_AUTHORITY
|
|
0x00, 0x10 // SECURITY_MANDATORY_LOW_RID
|
|
};
|
|
|
|
void *UserSid = NULL;
|
|
|
|
#define MyAddAccessAllowedAce(dacl,owner) \
|
|
RtlAddAccessAllowedAceEx( \
|
|
(dacl), \
|
|
ACL_REVISION, \
|
|
CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE | INHERITED_ACE, \
|
|
GENERIC_ALL, \
|
|
(owner))
|
|
|
|
#define MyAllocAndInitACL(acl,aclsz) \
|
|
(acl) = Dll_Alloc(aclsz); \
|
|
RtlCreateAcl((acl), (aclsz), ACL_REVISION);
|
|
|
|
#define MyAllocAndInitSD(sd) \
|
|
(sd) = Dll_Alloc(64); \
|
|
RtlCreateSecurityDescriptor((sd), SECURITY_DESCRIPTOR_REVISION);
|
|
|
|
//
|
|
// build Normal Security Descriptor used for files, keys, etc
|
|
//
|
|
|
|
MyAllocAndInitACL(MyAcl, 256);
|
|
MyAddAccessAllowedAce(MyAcl, &AuthenticatedUsersSid);
|
|
|
|
if (Dll_SidString) {
|
|
UserSid = Dll_SidStringToSid(Dll_SidString);
|
|
if (UserSid)
|
|
MyAddAccessAllowedAce(MyAcl, UserSid);
|
|
}
|
|
|
|
MyAllocAndInitSD(Secure_NormalSD);
|
|
RtlSetDaclSecurityDescriptor(Secure_NormalSD, TRUE, MyAcl, FALSE);
|
|
|
|
//
|
|
// build Everyone Security Descriptor used for named pipes
|
|
//
|
|
|
|
MyAllocAndInitACL(MyAcl, 128);
|
|
MyAddAccessAllowedAce(MyAcl, &AuthenticatedUsersSid);
|
|
MyAddAccessAllowedAce(MyAcl, &EveryoneSid);
|
|
//MyAddAccessAllowedAce(MyAcl, &RestrictedSid);
|
|
|
|
MyAllocAndInitSD(Secure_EveryoneSD);
|
|
RtlSetDaclSecurityDescriptor(Secure_EveryoneSD, TRUE, MyAcl, FALSE);
|
|
|
|
pRtlAddMandatoryAce = (P_RtlAddMandatoryAce)
|
|
GetProcAddress(Dll_Ntdll, "RtlAddMandatoryAce");
|
|
if (pRtlAddMandatoryAce) {
|
|
|
|
//
|
|
// on Windows Vista, also add a low mandatory label to make
|
|
// it possible for low integrity processes to connect
|
|
//
|
|
|
|
MyAllocAndInitACL(MyAcl, 64);
|
|
pRtlAddMandatoryAce(MyAcl, ACL_REVISION, 0, LowLabelSid,
|
|
SYSTEM_MANDATORY_LABEL_ACE_TYPE,
|
|
SYSTEM_MANDATORY_LABEL_NO_WRITE_UP);
|
|
|
|
RtlSetSaclSecurityDescriptor(Secure_EveryoneSD, TRUE, MyAcl, FALSE);
|
|
}
|
|
|
|
//
|
|
// finish
|
|
//
|
|
|
|
if (UserSid)
|
|
Dll_Free(UserSid);
|
|
|
|
#undef MyAllocAndInitSD
|
|
#undef MyAllocAndInitACL
|
|
#undef MyAddAccessAllowedAce
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_Init
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX BOOLEAN Secure_Init(void)
|
|
{
|
|
void *RtlQueryElevationFlags;
|
|
void *RtlCheckTokenMembershipEx;
|
|
|
|
//
|
|
// intercept NTDLL entry points
|
|
//
|
|
if (!Dll_CompartmentMode && !SbieApi_QueryConfBool(NULL, L"NoSysCallHooks", FALSE)) {
|
|
SBIEDLL_HOOK(Secure_, NtOpenProcess);
|
|
SBIEDLL_HOOK(Secure_, NtOpenThread);
|
|
SBIEDLL_HOOK(Secure_, NtDuplicateObject);
|
|
}
|
|
SBIEDLL_HOOK(Secure_,NtQuerySecurityObject);
|
|
SBIEDLL_HOOK(Secure_,NtSetSecurityObject);
|
|
SBIEDLL_HOOK(Secure_,NtSetInformationToken);
|
|
SBIEDLL_HOOK(Secure_,NtAdjustPrivilegesToken);
|
|
// OriginalToken BEGIN
|
|
if (!Dll_CompartmentMode && !SbieApi_QueryConfBool(NULL, L"OriginalToken", FALSE))
|
|
// OriginalToken END
|
|
if (Dll_OsBuild >= 21286) { // Windows 11
|
|
SBIEDLL_HOOK(Secure_, NtDuplicateToken);
|
|
SBIEDLL_HOOK(Secure_, NtFilterToken);
|
|
//NtFilterTokenEx is only present in windows 8 later windoses return STATUS_NOT_SUPPORTED
|
|
}
|
|
//if (Dll_Windows < 10) {
|
|
// SBIEDLL_HOOK(Secure_, NtQueryInformationToken);
|
|
//}
|
|
|
|
void* RtlEqualSid = (P_RtlEqualSid)GetProcAddress(Dll_Ntdll, "RtlEqualSid");
|
|
|
|
SBIEDLL_HOOK(Ldr_, RtlEqualSid);
|
|
|
|
//
|
|
// install hooks to fake administrator privileges
|
|
// note: when running as the built in administrator we should always act as if we have admin rights
|
|
//
|
|
|
|
Secure_FakeAdmin = Config_GetSettingsForImageName_bool(L"FakeAdminRights", Secure_IsBuiltInAdmin())
|
|
&& (_wcsicmp(Dll_ImageName, L"msedge.exe") != 0); // never for msedge.exe
|
|
|
|
|
|
if (Secure_FakeAdmin || Dll_OsBuild >= 9600) {
|
|
|
|
void* NtAccessCheckByType = GetProcAddress(Dll_Ntdll, "NtAccessCheckByType");
|
|
void* NtAccessCheck = GetProcAddress(Dll_Ntdll, "NtAccessCheck");
|
|
void* NtQuerySecurityAttributesToken = GetProcAddress(Dll_Ntdll, "NtQuerySecurityAttributesToken");
|
|
void* NtQueryInformationToken = GetProcAddress(Dll_Ntdll, "NtQueryInformationToken");
|
|
void* NtAccessCheckByTypeResultList = GetProcAddress(Dll_Ntdll, "NtAccessCheckByTypeResultList");
|
|
|
|
|
|
SBIEDLL_HOOK(Ldr_, NtQuerySecurityAttributesToken);
|
|
SBIEDLL_HOOK(Ldr_, NtAccessCheckByType);
|
|
SBIEDLL_HOOK(Ldr_, NtAccessCheck);
|
|
SBIEDLL_HOOK(Ldr_, NtAccessCheckByTypeResultList);
|
|
SBIEDLL_HOOK(Ldr_, NtQueryInformationToken);
|
|
}
|
|
|
|
if (Dll_OsBuild >= 9600) { // Windows 8.1 and later
|
|
if (DLL_IMAGE_GOOGLE_CHROME == Dll_ImageType) {
|
|
SBIEDLL_HOOK(Ldr_, NtOpenThreadToken);
|
|
}
|
|
}
|
|
|
|
//
|
|
// check if this is an Internet Explorer 8 tab process
|
|
//
|
|
|
|
if (Dll_ImageType == DLL_IMAGE_INTERNET_EXPLORER) {
|
|
|
|
const WCHAR *CmdLine = GetCommandLine();
|
|
if (CmdLine && wcsstr(CmdLine, L"SCODEF:")
|
|
&& wcsstr(CmdLine, L"CREDAT:"))
|
|
Secure_IsInternetExplorerTabProcess = TRUE;
|
|
}
|
|
|
|
|
|
RtlQueryElevationFlags =
|
|
GetProcAddress(Dll_Ntdll, "RtlQueryElevationFlags");
|
|
|
|
if (RtlQueryElevationFlags) {
|
|
|
|
BOOLEAN ShouldFakeRunningAsAdmin = Secure_FakeAdmin
|
|
|| Dll_ImageType == DLL_IMAGE_SANDBOXIE_SBIESVC
|
|
|| Dll_ImageType == DLL_IMAGE_SANDBOXIE_RPCSS
|
|
|| Dll_ImageType == DLL_IMAGE_INTERNET_EXPLORER
|
|
|| (_wcsicmp(Dll_ImageName, L"SynTPEnh.exe") == 0)
|
|
|| (_wcsicmp(Dll_ImageName, L"SynTPHelper.exe") == 0);
|
|
|
|
if (ShouldFakeRunningAsAdmin) {
|
|
|
|
SBIEDLL_HOOK(Secure_,RtlQueryElevationFlags);
|
|
|
|
//
|
|
// if this is an Internet Explorer tab process then we always
|
|
// need to fake administrator privileges because they will be
|
|
// queried by urlmon!InstallBrokerIsNeeded to decide whether
|
|
// to use the Protected Mode ActiveX Installation Broker.
|
|
//
|
|
// or, if Internet Explorer was going to run as administrator
|
|
// but we removed admin privileges due to drop rights, then
|
|
// we have to fake the administrator privileges.
|
|
//
|
|
// otherwise if Internet Explorer was running without admin
|
|
// privileges then we just fake that protected mode is off,
|
|
// see Key_NtQueryValueKeyFakeForInternetExplorer
|
|
//
|
|
// for Internet Explorer 10, we have to fake admin privileges
|
|
// on the parent process as well, otherwise the processes
|
|
// can't connect. (and assuming that we always want to fake
|
|
// the tab process due to urlmon!InstallBrokerIsNeeded.)
|
|
//
|
|
|
|
if (Dll_ImageType == DLL_IMAGE_INTERNET_EXPLORER) {
|
|
|
|
if (Secure_IsInternetExplorerTabProcess ||
|
|
(SH_GetInternetExplorerVersion() >= 10) ||
|
|
(Dll_ProcessFlags & SBIE_FLAG_RIGHTS_DROPPED)) {
|
|
|
|
Secure_Is_IE_NtQueryInformationToken = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RtlCheckTokenMembershipEx =
|
|
GetProcAddress(Dll_Ntdll, "RtlCheckTokenMembershipEx");
|
|
|
|
if (RtlCheckTokenMembershipEx) {
|
|
|
|
if (Secure_FakeAdmin) {
|
|
|
|
SBIEDLL_HOOK(Secure_, RtlCheckTokenMembershipEx);
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SbieDll_OpenProcess
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX HANDLE SbieDll_OpenProcess(ACCESS_MASK DesiredAccess, HANDLE idProcess)
|
|
{
|
|
HANDLE hProcess = OpenProcess(DesiredAccess, FALSE, (DWORD)(UINT_PTR)idProcess);
|
|
if (! hProcess) {
|
|
if (!Dll_CompartmentMode) // NoDriverAssist
|
|
if (! NT_SUCCESS(SbieApi_OpenProcess(&hProcess, (HANDLE)idProcess)))
|
|
hProcess = NULL;
|
|
}
|
|
return hProcess;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_NtOpenProcess
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX NTSTATUS Secure_NtOpenProcess(
|
|
HANDLE *ProcessHandle,
|
|
ACCESS_MASK DesiredAccess,
|
|
OBJECT_ATTRIBUTES *ObjectAttributes,
|
|
CLIENT_ID *ClientId)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// reduce desired access if trying to open a process outside the sandbox
|
|
// (or in another sandbox) otherwise the driver will cancel our process
|
|
//
|
|
|
|
if (ClientId) {
|
|
|
|
ULONG64 OtherProcessFlags =
|
|
SbieApi_QueryProcessInfo(ClientId->UniqueProcess, 0);
|
|
if (OtherProcessFlags) {
|
|
if (! Secure_IsSameBox(ClientId->UniqueProcess))
|
|
OtherProcessFlags = 0;
|
|
}
|
|
|
|
if (! OtherProcessFlags) {
|
|
|
|
if (DesiredAccess & (MAXIMUM_ALLOWED | PROCESS_DUP_HANDLE)) {
|
|
DesiredAccess |= STANDARD_RIGHTS_READ |
|
|
PROCESS_QUERY_INFORMATION |
|
|
SYNCHRONIZE;
|
|
}
|
|
|
|
DesiredAccess &= STANDARD_RIGHTS_READ | SYNCHRONIZE
|
|
| PROCESS_VM_READ
|
|
| PROCESS_QUERY_INFORMATION
|
|
| PROCESS_QUERY_LIMITED_INFORMATION;
|
|
}
|
|
}
|
|
|
|
//
|
|
// open handle to process object
|
|
//
|
|
|
|
status = __sys_NtOpenProcess(
|
|
ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
|
|
|
|
if (status == STATUS_ACCESS_DENIED) {
|
|
|
|
if (DesiredAccess == PROCESS_QUERY_INFORMATION ||
|
|
DesiredAccess == PROCESS_QUERY_LIMITED_INFORMATION) {
|
|
|
|
//
|
|
// possibly an administrator process trying to open a process
|
|
// outside the sandbox, but failing because we stripped the
|
|
// debug privileges
|
|
//
|
|
|
|
return SbieApi_OpenProcess(
|
|
ProcessHandle, ClientId->UniqueProcess);
|
|
}
|
|
|
|
//
|
|
// otherwise reduce the desired access and try again
|
|
//
|
|
|
|
if (DesiredAccess & (MAXIMUM_ALLOWED | PROCESS_DUP_HANDLE)) {
|
|
DesiredAccess |= STANDARD_RIGHTS_READ |
|
|
PROCESS_QUERY_INFORMATION |
|
|
SYNCHRONIZE;
|
|
}
|
|
|
|
DesiredAccess &= STANDARD_RIGHTS_READ | SYNCHRONIZE
|
|
| PROCESS_VM_READ
|
|
| PROCESS_QUERY_INFORMATION
|
|
| PROCESS_QUERY_LIMITED_INFORMATION;
|
|
|
|
if (DesiredAccess) {
|
|
status = __sys_NtOpenProcess(
|
|
ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_NtOpenThread
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX NTSTATUS Secure_NtOpenThread(
|
|
HANDLE *ThreadHandle,
|
|
ACCESS_MASK DesiredAccess,
|
|
OBJECT_ATTRIBUTES *ObjectAttributes,
|
|
CLIENT_ID *ClientId)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
status = __sys_NtOpenThread(
|
|
ThreadHandle, DesiredAccess, ObjectAttributes, ClientId);
|
|
|
|
if (status == STATUS_ACCESS_DENIED) {
|
|
|
|
if (DesiredAccess & MAXIMUM_ALLOWED) {
|
|
DesiredAccess |= STANDARD_RIGHTS_READ |
|
|
THREAD_QUERY_INFORMATION |
|
|
SYNCHRONIZE;
|
|
}
|
|
|
|
DesiredAccess &= STANDARD_RIGHTS_READ |
|
|
THREAD_GET_CONTEXT |
|
|
THREAD_QUERY_INFORMATION |
|
|
SYNCHRONIZE;
|
|
|
|
if (DesiredAccess) {
|
|
status = __sys_NtOpenThread(
|
|
ThreadHandle, DesiredAccess, ObjectAttributes, ClientId);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_NtDuplicateObject
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX NTSTATUS Secure_NtDuplicateObject(
|
|
HANDLE SourceProcessHandle,
|
|
HANDLE SourceHandle,
|
|
HANDLE TargetProcessHandle,
|
|
HANDLE *TargetHandle,
|
|
ACCESS_MASK DesiredAccess,
|
|
ULONG HandleAttributes,
|
|
ULONG Options)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE OtherProcessHandle;
|
|
|
|
//
|
|
// a process may get a read-only handle to a process outside the sandbox
|
|
// and then try to extend the granted access through NtDuplicateObject
|
|
// (for example the GameGaurd process GameDes.mon). we don't want to
|
|
// send that directly to our driver because it would cancel the process
|
|
//
|
|
|
|
if (SourceProcessHandle == NtCurrentProcess() &&
|
|
(! (Options & DUPLICATE_SAME_ACCESS)) &&
|
|
Obj_GetObjectType(SourceHandle) == OBJ_TYPE_PROCESS) {
|
|
|
|
if (DesiredAccess & ( PROCESS_CREATE_THREAD
|
|
| PROCESS_VM_OPERATION
|
|
| PROCESS_VM_WRITE)) {
|
|
|
|
HANDLE OtherProcessId =
|
|
(HANDLE)(ULONG_PTR)GetProcessId(SourceHandle);
|
|
ULONG64 OtherProcessFlags =
|
|
SbieApi_QueryProcessInfo(OtherProcessId, 0);
|
|
if (OtherProcessFlags) {
|
|
if (! Secure_IsSameBox(OtherProcessId))
|
|
OtherProcessFlags = 0;
|
|
}
|
|
|
|
if (! OtherProcessFlags) {
|
|
|
|
DesiredAccess &= ~( PROCESS_CREATE_THREAD
|
|
| PROCESS_VM_OPERATION
|
|
| PROCESS_VM_WRITE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// try to issue the request but we may get STATUS_ACCESS_DENIED so
|
|
// be careful not to close the source handle. on the other hand,
|
|
// if we are successful, then make sure the handle gets closed
|
|
//
|
|
|
|
status = __sys_NtDuplicateObject(
|
|
SourceProcessHandle, SourceHandle, TargetProcessHandle, TargetHandle,
|
|
DesiredAccess, HandleAttributes, Options & ~DUPLICATE_CLOSE_SOURCE);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (Options & DUPLICATE_CLOSE_SOURCE) {
|
|
|
|
//
|
|
// issue NtDuplicateObject again with no TargetProcessHandle
|
|
// to just close the source handle
|
|
//
|
|
|
|
__sys_NtDuplicateObject(
|
|
SourceProcessHandle, SourceHandle, NULL, NULL,
|
|
DesiredAccess, HandleAttributes, DUPLICATE_CLOSE_SOURCE);
|
|
}
|
|
|
|
if (SourceProcessHandle == NtCurrentProcess()) {
|
|
|
|
if (TargetProcessHandle == NtCurrentProcess() && TargetHandle) {
|
|
|
|
//
|
|
// this also duplicates the "recoverability"
|
|
// of the old handle to the new handle. needed in particular for
|
|
// SHFileOperation to recover correctly on Windows Vista
|
|
//
|
|
|
|
if(SourceHandle && *TargetHandle)
|
|
Handle_SetupDuplicate(SourceHandle, *TargetHandle);
|
|
}
|
|
|
|
if (SourceHandle)
|
|
Key_NtClose(SourceHandle);
|
|
}
|
|
|
|
//
|
|
// else invoke the driver to duplicate the object
|
|
//
|
|
|
|
} else if (status == STATUS_ACCESS_DENIED) {
|
|
|
|
OtherProcessHandle = NULL;
|
|
|
|
if (TargetProcessHandle == NtCurrentProcess()) {
|
|
|
|
if (SourceProcessHandle != NtCurrentProcess()) {
|
|
|
|
OtherProcessHandle = SourceProcessHandle;
|
|
/* Options |= DUPLICATE_FROM_OTHER; */
|
|
}
|
|
|
|
} else if (SourceProcessHandle == NtCurrentProcess()) {
|
|
|
|
OtherProcessHandle = TargetProcessHandle;
|
|
Options |= DUPLICATE_INTO_OTHER;
|
|
}
|
|
|
|
if (OtherProcessHandle) {
|
|
|
|
if (HandleAttributes & OBJ_INHERIT)
|
|
Options |= DUPLICATE_INHERIT;
|
|
|
|
status = SbieApi_DuplicateObject(
|
|
TargetHandle, OtherProcessHandle, SourceHandle,
|
|
DesiredAccess, Options);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_NtQuerySecurityObject
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX NTSTATUS Secure_NtQuerySecurityObject(
|
|
HANDLE Handle,
|
|
SECURITY_INFORMATION SecurityInformation,
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
ULONG Length,
|
|
ULONG *LengthNeeded)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE handle2 = NULL;
|
|
BOOLEAN IsOpenPath = FALSE;
|
|
|
|
ULONG type = Obj_GetObjectType(Handle);
|
|
if (type == OBJ_TYPE_FILE)
|
|
handle2 = File_GetTrueHandle(Handle, &IsOpenPath);
|
|
else if (type == OBJ_TYPE_KEY)
|
|
handle2 = Key_GetTrueHandle(Handle, &IsOpenPath);
|
|
|
|
if (IsOpenPath) {
|
|
|
|
if (handle2)
|
|
NtClose(handle2);
|
|
|
|
status = __sys_NtQuerySecurityObject(
|
|
Handle, SecurityInformation, SecurityDescriptor,
|
|
Length, LengthNeeded);
|
|
|
|
return status;
|
|
}
|
|
|
|
if (! handle2)
|
|
handle2 = Handle;
|
|
|
|
status = __sys_NtQuerySecurityObject(
|
|
handle2, SecurityInformation, SecurityDescriptor,
|
|
Length, LengthNeeded);
|
|
|
|
if (status == STATUS_ACCESS_DENIED) {
|
|
|
|
const ULONG SACL_INFO = (SACL_SECURITY_INFORMATION |
|
|
PROTECTED_SACL_SECURITY_INFORMATION |
|
|
UNPROTECTED_SACL_SECURITY_INFORMATION);
|
|
if (SecurityInformation & SACL_INFO) {
|
|
|
|
//
|
|
// we never have ACCESS_SYSTEM_SECURITY access for True objects
|
|
//
|
|
|
|
SecurityInformation &= ~SACL_INFO;
|
|
|
|
status = __sys_NtQuerySecurityObject(
|
|
handle2, SecurityInformation, SecurityDescriptor,
|
|
Length, LengthNeeded);
|
|
}
|
|
}
|
|
|
|
if (handle2 != Handle)
|
|
NtClose(handle2);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_NtSetSecurityObject
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX NTSTATUS Secure_NtSetSecurityObject(
|
|
HANDLE Handle,
|
|
SECURITY_INFORMATION SecurityInformation,
|
|
SECURITY_DESCRIPTOR *SecurityDescriptor)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE handle2 = NULL;
|
|
BOOLEAN IsOpenPath = FALSE;
|
|
BOOLEAN IsUnnamedObject = FALSE;
|
|
|
|
ULONG type = Obj_GetObjectType(Handle);
|
|
if (type == OBJ_TYPE_FILE)
|
|
handle2 = File_GetTrueHandle(Handle, &IsOpenPath);
|
|
else if (type == OBJ_TYPE_KEY)
|
|
handle2 = Key_GetTrueHandle(Handle, &IsOpenPath);
|
|
else {
|
|
|
|
ULONG name_space[16];
|
|
ULONG name_len = sizeof(name_space);
|
|
OBJECT_NAME_INFORMATION *name =
|
|
(OBJECT_NAME_INFORMATION *)name_space;
|
|
status = Obj_GetObjectName(Handle, name, &name_len);
|
|
if (NT_SUCCESS(status) && name->Name.Length == 0) {
|
|
|
|
IsUnnamedObject = TRUE;
|
|
}
|
|
}
|
|
|
|
//WCHAR txt[256];
|
|
//Sbie_snwprintf(txt, 256, L"NtSetSecurityObject Open=%d Handle=%08X Type=%d Info=%08X\n", IsOpenPath, Handle, type, SecurityInformation);
|
|
//OutputDebugString(txt);
|
|
//while (! IsDebuggerPresent()) Sleep(500); __debugbreak();
|
|
|
|
if (IsOpenPath || IsUnnamedObject) {
|
|
|
|
if (handle2)
|
|
NtClose(handle2);
|
|
|
|
status = __sys_NtSetSecurityObject(
|
|
Handle, SecurityInformation, SecurityDescriptor);
|
|
|
|
if (IsOpenPath || (! NT_SUCCESS(status)))
|
|
return status;
|
|
|
|
// if IsUnnamedObject then we continue so that we can adjust
|
|
// the label if necessary
|
|
}
|
|
|
|
//
|
|
// if the program wants to change the integrity label, we assume
|
|
// it must be a broker process trying to make a resource accessible
|
|
// by a managed process, so we use our low integrity label
|
|
//
|
|
|
|
if (Dll_OsBuild >= 6000 &&
|
|
(SecurityInformation & LABEL_SECURITY_INFORMATION)) {
|
|
|
|
__sys_NtSetSecurityObject(
|
|
Handle, LABEL_SECURITY_INFORMATION, Secure_EveryoneSD);
|
|
|
|
//SecurityInformation &= ~LABEL_SECURITY_INFORMATION;
|
|
}
|
|
|
|
//
|
|
// objects created inside the sandbox get a public dacl,
|
|
// and we don't want anyone changing that
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_NtQueryInformationToken
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX void Ldr_TestToken(HANDLE token, PHANDLE hTokenReal, BOOLEAN bImpersonate)
|
|
{
|
|
if (Dll_OsBuild < 9600) // this magic values are available only from windows 8.1 onwards
|
|
return;
|
|
|
|
// OriginalToken BEGIN
|
|
if (Dll_CompartmentMode || SbieApi_QueryConfBool(NULL, L"OriginalToken", FALSE))
|
|
return;
|
|
// OriginalToken END
|
|
|
|
BOOLEAN bDuplicate = FALSE;
|
|
if ((LONG_PTR)token == LDR_TOKEN_PRIMARY) {
|
|
NtOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY | (bImpersonate ? TOKEN_DUPLICATE : 0), hTokenReal);
|
|
bDuplicate = TRUE;
|
|
}
|
|
else if ((LONG_PTR)token == LDR_TOKEN_IMPERSONATION) {
|
|
NtOpenThreadToken(NtCurrentThread(), TOKEN_QUERY, FALSE, hTokenReal);
|
|
}
|
|
else if ((LONG_PTR)token <= LDR_TOKEN_EFFECTIVE) {
|
|
NtOpenThreadToken(NtCurrentThread(), TOKEN_QUERY, FALSE, hTokenReal);
|
|
if (*hTokenReal == NULL) {
|
|
NtOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY | (bImpersonate ? TOKEN_DUPLICATE : 0), hTokenReal);
|
|
bDuplicate = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// SeAccessCheckByType requires the token to eider be
|
|
// an impersonation token of level SecurityIdentification or higher
|
|
// or a pseudo handle, hence we have to convert the token here
|
|
//
|
|
|
|
if (bDuplicate && *hTokenReal != NULL) {
|
|
|
|
HANDLE hTokenRealImp = NULL;
|
|
OBJECT_ATTRIBUTES objattrs;
|
|
SECURITY_QUALITY_OF_SERVICE QoS;
|
|
|
|
InitializeObjectAttributes(&objattrs, NULL, 0, NULL, NULL);
|
|
QoS.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
|
QoS.ImpersonationLevel = SecurityImpersonation;
|
|
QoS.ContextTrackingMode = SECURITY_STATIC_TRACKING;
|
|
QoS.EffectiveOnly = FALSE;
|
|
objattrs.SecurityQualityOfService = &QoS;
|
|
|
|
if (NT_SUCCESS(NtDuplicateToken(*hTokenReal, MAXIMUM_ALLOWED, &objattrs, FALSE, TokenImpersonation, &hTokenRealImp))) {
|
|
|
|
NtClose(*hTokenReal);
|
|
|
|
*hTokenReal = hTokenRealImp;
|
|
}
|
|
}
|
|
}
|
|
|
|
_FX NTSTATUS Ldr_NtQueryInformationToken(
|
|
HANDLE TokenHandle,
|
|
TOKEN_INFORMATION_CLASS TokenInformationClass,
|
|
void *TokenInformation,
|
|
ULONG TokenInformationLength,
|
|
ULONG *ReturnLength)
|
|
{
|
|
NTSTATUS status = 0;
|
|
THREAD_DATA *TlsData = NULL;
|
|
HANDLE hTokenReal = NULL;
|
|
BOOLEAN FakeAdmin = FALSE;
|
|
|
|
Ldr_TestToken(TokenHandle, &hTokenReal, FALSE);
|
|
|
|
status = __sys_NtQueryInformationToken(
|
|
hTokenReal ? hTokenReal : TokenHandle, TokenInformationClass,
|
|
TokenInformation, TokenInformationLength, ReturnLength);
|
|
|
|
//
|
|
// To make the process think we need to chage here a few values
|
|
// we also ensure that tha token belongs to the current process
|
|
//
|
|
|
|
if (Secure_FakeAdmin && (SbieApi_QueryProcessInfoEx(0, 'ippt', (LONG_PTR)(hTokenReal ? hTokenReal : TokenHandle))))
|
|
{
|
|
FakeAdmin = TRUE;
|
|
}
|
|
|
|
if (hTokenReal)
|
|
{
|
|
NtClose(hTokenReal);
|
|
}
|
|
|
|
//
|
|
// NtQueryInformationToken is hooked for Internet Explorer.
|
|
//
|
|
// if the check occurs during CreateProcess, then return the real
|
|
// information, so UAC elevation may occur for the new process.
|
|
//
|
|
// otherwise, this check is related to Protected Mode, so pretend
|
|
// we are running as Administrator
|
|
//
|
|
|
|
TlsData = Dll_GetTlsData(NULL);
|
|
|
|
if (Secure_Is_IE_NtQueryInformationToken && !TlsData->proc_create_process)
|
|
{
|
|
FakeAdmin = TRUE;
|
|
}
|
|
|
|
if (NT_SUCCESS(status) && FakeAdmin) {
|
|
|
|
if (TokenInformationClass == TokenElevation) {
|
|
|
|
*(BOOLEAN *)TokenInformation = TRUE;
|
|
}
|
|
|
|
else if (TokenInformationClass == TokenElevationType) {
|
|
|
|
//
|
|
// on Vista, fake a return value for a full token
|
|
//
|
|
|
|
*(ULONG *)TokenInformation = TokenElevationTypeFull;
|
|
}
|
|
|
|
else if (TokenInformationClass == TokenIntegrityLevel) {
|
|
|
|
//
|
|
// on Vista, fake a high integrity level
|
|
//
|
|
|
|
#include "pshpack4.h"
|
|
|
|
typedef struct {
|
|
|
|
ULONG_PTR Pointer;
|
|
ULONG_PTR Sixty;
|
|
ULONG OneOhOne;
|
|
ULONG HighBitSet;
|
|
ULONG ThreeK;
|
|
|
|
} TOKEN_INTEGRITY_LEVEL;
|
|
|
|
#include "poppack.h"
|
|
|
|
if (TokenInformationLength >= sizeof(TOKEN_INTEGRITY_LEVEL)) {
|
|
|
|
TOKEN_INTEGRITY_LEVEL *Info =
|
|
(TOKEN_INTEGRITY_LEVEL *)TokenInformation;
|
|
|
|
Info->Pointer = (ULONG_PTR)TokenInformation
|
|
+ sizeof(ULONG_PTR) * 2;
|
|
Info->Sixty = 0x60;
|
|
Info->OneOhOne = 0x101;
|
|
Info->HighBitSet = 0x10000000;
|
|
Info->ThreeK = 0x3000;
|
|
|
|
if (ReturnLength)
|
|
*ReturnLength = sizeof(TOKEN_INTEGRITY_LEVEL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
_FX NTSTATUS Ldr_NtQuerySecurityAttributesToken(HANDLE TokenHandle, PUNICODE_STRING Attributes, ULONG NumberOfAttributes, PVOID Buffer, ULONG Length, PULONG ReturnLength)
|
|
{
|
|
NTSTATUS status = 0;
|
|
HANDLE hTokenReal = NULL;
|
|
|
|
Ldr_TestToken(TokenHandle, &hTokenReal, FALSE);
|
|
|
|
status = __sys_NtQuerySecurityAttributesToken(hTokenReal ? hTokenReal : TokenHandle, Attributes, NumberOfAttributes, Buffer, Length, ReturnLength);
|
|
|
|
if (hTokenReal) {
|
|
NtClose(hTokenReal);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS Ldr_NtAccessCheckByType(PSECURITY_DESCRIPTOR SecurityDescriptor, PSID PrincipalSelfSid, HANDLE ClientToken, ACCESS_MASK DesiredAccess, POBJECT_TYPE_LIST ObjectTypeList, ULONG ObjectTypeListLength, PGENERIC_MAPPING GenericMapping, PPRIVILEGE_SET PrivilegeSet, PULONG PrivilegeSetLength, PACCESS_MASK GrantedAccess, PNTSTATUS AccessStatus)
|
|
{
|
|
NTSTATUS rc;
|
|
HANDLE hTokenReal = NULL;
|
|
|
|
// todo: is that right? seams wrong
|
|
if (Dll_ImageType == DLL_IMAGE_SANDBOXIE_BITS ||
|
|
Dll_ImageType == DLL_IMAGE_SANDBOXIE_WUAU ||
|
|
Dll_ImageType == DLL_IMAGE_WUAUCLT) {
|
|
*GrantedAccess = 0xFFFFFFFF;
|
|
*AccessStatus = TRUE;
|
|
SetLastError(0);
|
|
return TRUE;
|
|
}
|
|
|
|
Ldr_TestToken(ClientToken, &hTokenReal, TRUE);
|
|
|
|
rc = __sys_NtAccessCheckByType(SecurityDescriptor, PrincipalSelfSid, hTokenReal ? hTokenReal : ClientToken, DesiredAccess, ObjectTypeList, ObjectTypeListLength, GenericMapping, PrivilegeSet, PrivilegeSetLength, GrantedAccess, AccessStatus);
|
|
|
|
if (hTokenReal) {
|
|
NtClose(hTokenReal);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
_FX NTSTATUS Ldr_NtAccessCheck(PSECURITY_DESCRIPTOR SecurityDescriptor, HANDLE ClientToken, ACCESS_MASK DesiredAccess, PGENERIC_MAPPING GenericMapping, PPRIVILEGE_SET RequiredPrivilegesBuffer, PULONG BufferLength, PACCESS_MASK GrantedAccess, PNTSTATUS AccessStatus)
|
|
{
|
|
NTSTATUS status = 0;
|
|
HANDLE hTokenReal = NULL;
|
|
|
|
if (Secure_FakeAdmin && SecurityDescriptor) {
|
|
BOOLEAN Fake = FALSE;
|
|
|
|
PSID Group, Owner;
|
|
BOOLEAN Dummy1, Dummy2;
|
|
if (NT_SUCCESS(RtlGetGroupSecurityDescriptor(SecurityDescriptor, &Group, &Dummy1))
|
|
&& NT_SUCCESS(RtlGetOwnerSecurityDescriptor(SecurityDescriptor, &Owner, &Dummy2))) {
|
|
Fake = RtlEqualSid(Group, AdministratorsSid) || RtlEqualSid(Owner, AdministratorsSid);
|
|
}
|
|
|
|
if (Fake) {
|
|
*GrantedAccess = 1;
|
|
*AccessStatus = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
Ldr_TestToken(ClientToken, &hTokenReal, TRUE);
|
|
|
|
status = __sys_NtAccessCheck(SecurityDescriptor, hTokenReal ? hTokenReal : ClientToken, DesiredAccess, GenericMapping, RequiredPrivilegesBuffer, BufferLength, GrantedAccess, AccessStatus);
|
|
|
|
if (hTokenReal) {
|
|
NtClose(hTokenReal);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
_FX NTSTATUS Ldr_NtAccessCheckByTypeResultList(PSECURITY_DESCRIPTOR SecurityDescriptor, PSID PrincipalSelfSid, HANDLE ClientToken, ACCESS_MASK DesiredAccess, POBJECT_TYPE_LIST ObjectTypeList, ULONG ObjectTypeListLength, PGENERIC_MAPPING GenericMapping, PPRIVILEGE_SET PrivilegeSet, PULONG PrivilegeSetLength, PACCESS_MASK GrantedAccess, PNTSTATUS AccessStatus)
|
|
{
|
|
NTSTATUS status = 0;
|
|
HANDLE hTokenReal = NULL;
|
|
|
|
Ldr_TestToken(ClientToken, &hTokenReal, TRUE);
|
|
|
|
status = __sys_NtAccessCheckByTypeResultList(SecurityDescriptor, PrincipalSelfSid, ClientToken, DesiredAccess, ObjectTypeList, ObjectTypeListLength, GenericMapping, PrivilegeSet, PrivilegeSetLength, GrantedAccess, AccessStatus);
|
|
|
|
if (hTokenReal) {
|
|
NtClose(hTokenReal);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
BOOL Ldr_NtOpenThreadToken(HANDLE ThreadHandle, DWORD DesiredAccess, BOOL OpenAsSelf, PHANDLE TokenHandle)
|
|
{
|
|
BOOL rc;
|
|
|
|
rc = __sys_NtOpenThreadToken(ThreadHandle, DesiredAccess, OpenAsSelf, TokenHandle);
|
|
if (rc == STATUS_ACCESS_DENIED && OpenAsSelf) {
|
|
rc = __sys_NtOpenThreadToken(ThreadHandle, DesiredAccess, 0, TokenHandle);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
BOOL Ldr_RtlEqualSid(void * sid1, void * sid2)
|
|
{
|
|
if (!sid1 || !sid2) {
|
|
return FALSE;
|
|
}
|
|
return __sys_RtlEqualSid(sid1, sid2);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_NtSetInformationToken
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
static NTSTATUS Secure_NtSetInformationToken(
|
|
HANDLE TokenHandle,
|
|
TOKEN_INFORMATION_CLASS TokenInformationClass,
|
|
void *TokenInformation,
|
|
ULONG TokenInformationLength)
|
|
{
|
|
NTSTATUS status = __sys_NtSetInformationToken(
|
|
TokenHandle, TokenInformationClass,
|
|
TokenInformation, TokenInformationLength);
|
|
|
|
if ((! NT_SUCCESS(status)) && (
|
|
TokenInformationClass == TokenSessionId ||
|
|
TokenInformationClass == TokenIntegrityLevel))
|
|
status = STATUS_SUCCESS;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_NtAdjustPrivilegesToken
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX NTSTATUS Secure_NtAdjustPrivilegesToken(
|
|
HANDLE TokenHandle,
|
|
BOOLEAN DisableAllPrivileges,
|
|
TOKEN_PRIVILEGES *NewState,
|
|
ULONG BufferLength,
|
|
TOKEN_PRIVILEGES *PreviousState,
|
|
ULONG *ReturnLength)
|
|
{
|
|
ULONG status = __sys_NtAdjustPrivilegesToken(
|
|
TokenHandle, DisableAllPrivileges, NewState,
|
|
BufferLength, PreviousState, ReturnLength);
|
|
if (status == STATUS_NOT_ALL_ASSIGNED)
|
|
status = STATUS_SUCCESS;
|
|
return status;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_NtDuplicateToken
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX NTSTATUS Secure_NtDuplicateToken(
|
|
_In_ HANDLE ExistingTokenHandle,
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
|
|
_In_ BOOLEAN EffectiveOnly,
|
|
_In_ TOKEN_TYPE TokenType,
|
|
_Out_ PHANDLE NewTokenHandle)
|
|
{
|
|
//
|
|
// on windows 11 MSIServer fails to duplicte its impersonation token when using it
|
|
// so we drop the impersonation, do the duplication and re impersonate
|
|
//
|
|
|
|
HANDLE hToken = NULL;
|
|
NtOpenThreadToken(NtCurrentThread(), MAXIMUM_ALLOWED, TRUE, &hToken);
|
|
HANDLE hNull = NULL;
|
|
NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hNull, sizeof(HANDLE));
|
|
|
|
ULONG status = __sys_NtDuplicateToken(
|
|
ExistingTokenHandle, DesiredAccess, ObjectAttributes,
|
|
EffectiveOnly, TokenType, NewTokenHandle);
|
|
|
|
if (hToken) {
|
|
NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hToken, sizeof(HANDLE));
|
|
NtClose(hToken);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_NtFilterToken
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX NTSTATUS Secure_NtFilterToken(
|
|
_In_ HANDLE ExistingTokenHandle,
|
|
_In_ ULONG Flags,
|
|
_In_opt_ PTOKEN_GROUPS SidsToDisable,
|
|
_In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
|
|
_In_opt_ PTOKEN_GROUPS RestrictedSids,
|
|
_Out_ PHANDLE NewTokenHandle)
|
|
{
|
|
HANDLE hToken = NULL;
|
|
NtOpenThreadToken(NtCurrentThread(), MAXIMUM_ALLOWED, TRUE, &hToken);
|
|
HANDLE hNull = NULL;
|
|
NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hNull, sizeof(HANDLE));
|
|
|
|
ULONG status = __sys_NtFilterToken(
|
|
ExistingTokenHandle, Flags, SidsToDisable,
|
|
PrivilegesToDelete, RestrictedSids, NewTokenHandle);
|
|
|
|
if (hToken) {
|
|
NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hToken, sizeof(HANDLE));
|
|
NtClose(hToken);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_RtlQueryElevationFlags
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX NTSTATUS Secure_RtlQueryElevationFlags(ULONG *Flags)
|
|
{
|
|
THREAD_DATA *TlsData = Dll_GetTlsData(NULL);
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// RtlQueryElevationFlags
|
|
//
|
|
// Retrieve UAC system information from the shared memory area
|
|
// (KUSER_SHARED_DATA)
|
|
//
|
|
// Return flags:
|
|
// - ElevationEnabled (0x01) - UAC is enabled
|
|
// - VirtEnabled (0x02) - Virtualization is enabled
|
|
// - InstallerDetectEnabled (0x04) - Detection of installers
|
|
//
|
|
|
|
BOOLEAN fake = Secure_FakeAdmin; // FALSE;
|
|
|
|
if (Dll_ImageType == DLL_IMAGE_INTERNET_EXPLORER) {
|
|
|
|
//
|
|
// RtlQueryElevationFlags hook for Internet Explorer:
|
|
//
|
|
// if the check occurs during CreateProcess, then return the real
|
|
// elevation flags, so UAC elevation may occur for the new process.
|
|
//
|
|
// otherwise, this check is related to Protected Mode, so pretend
|
|
// there is no need to elevate
|
|
//
|
|
|
|
if (! TlsData->proc_create_process)
|
|
fake = TRUE;
|
|
|
|
} else if (Dll_ImageType == DLL_IMAGE_SANDBOXIE_SBIESVC) {
|
|
|
|
//
|
|
// RtlQueryElevationFlags hook for SbieSvc UAC elevation process:
|
|
//
|
|
// even when running as Administrator, in some cases the
|
|
// kernel32!CheckElevationEnabled function (called by
|
|
// kernel32!kernel32!CreateProcessInternalW) will decide that
|
|
// elevation is required for some EXEs, and fail CreateProcess
|
|
// with ERROR_ELEVATION_REQUIRED. this will cause SH32_DoRunAs
|
|
// to run and invoke ShellExecuteEx/runas, which will result in
|
|
// another invocation of CreateProcess, and an infinite loop.
|
|
//
|
|
// to work around this problem, we need to turn off the bit
|
|
// InstallerDetectEnabled (returning zero flags is also ok), as
|
|
// this disables the checks in kernel32!CheckElevationEnabled
|
|
//
|
|
// we do this only for the SbieSvc UAC elevator process, because
|
|
// we know that process is already running as Administrator.
|
|
//
|
|
|
|
if (TlsData->proc_create_process)
|
|
fake = TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// RtlQueryElevationFlags hook for anything else:
|
|
//
|
|
// - SandboxieRpcSs, which is used to run elevated COM objects,
|
|
// for example the Internet Explorer Protected Mode ActiveX
|
|
// Installation Broker, or elevated Control Panel applets.
|
|
// we return zero flags and the COM object runs without elevation.
|
|
//
|
|
// - a couple of Synaptics programs, which reportedly caused
|
|
// UAC prompts, and seem to run well without actually elevating
|
|
//
|
|
// pretend there is no need to elevate
|
|
//
|
|
|
|
fake = TRUE;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
if (fake) {
|
|
|
|
*Flags = 0;
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
status = __sys_RtlQueryElevationFlags(Flags);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_IsRestrictedToken
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS Secure_RtlCheckTokenMembershipEx(
|
|
HANDLE tokenHandle,
|
|
PSID sidToCheck,
|
|
DWORD flags,
|
|
PUCHAR isMember)
|
|
{
|
|
if (Secure_FakeAdmin && RtlEqualSid(sidToCheck, AdministratorsSid)) {
|
|
if (isMember) *isMember = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS status = __sys_RtlCheckTokenMembershipEx(tokenHandle, sidToCheck, flags, isMember);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_IsRestrictedToken
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX BOOLEAN Secure_IsRestrictedToken(BOOLEAN CheckThreadToken)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE hToken;
|
|
|
|
static int _restricted_process = -1;
|
|
BOOLEAN have_process_token = FALSE;
|
|
BOOLEAN return_value = FALSE;
|
|
|
|
//
|
|
// open token in current thread or process
|
|
//
|
|
|
|
if (CheckThreadToken) {
|
|
status = NtOpenThreadToken(
|
|
NtCurrentThread(), TOKEN_QUERY, FALSE, &hToken);
|
|
} else
|
|
status = STATUS_NO_TOKEN;
|
|
|
|
if (status == STATUS_NO_TOKEN) {
|
|
|
|
if (_restricted_process != -1)
|
|
return ((_restricted_process == 1) ? TRUE : FALSE);
|
|
|
|
status = NtOpenProcessToken(
|
|
NtCurrentProcess(), TOKEN_QUERY, &hToken);
|
|
|
|
have_process_token = TRUE;
|
|
}
|
|
|
|
//
|
|
// check if token has restricted groups
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
ULONG64 groups_space[8];
|
|
PTOKEN_GROUPS sids = (PTOKEN_GROUPS)groups_space;
|
|
ULONG len;
|
|
|
|
len = sizeof(groups_space);
|
|
status = NtQueryInformationToken(
|
|
hToken, TokenRestrictedSids, sids, len, &len);
|
|
|
|
if (status == STATUS_BUFFER_TOO_SMALL) {
|
|
|
|
sids = Dll_AllocTemp(len);
|
|
status = NtQueryInformationToken(
|
|
hToken, TokenRestrictedSids, sids, len, &len);
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (sids->GroupCount != 0)
|
|
return_value = TRUE;
|
|
|
|
if (_restricted_process == -1 && have_process_token)
|
|
_restricted_process = (return_value ? 1 : 0);
|
|
}
|
|
|
|
if (sids != (PTOKEN_GROUPS)groups_space)
|
|
Dll_Free(sids);
|
|
|
|
NtClose(hToken);
|
|
}
|
|
|
|
return return_value;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_IsTokenLocalSystem
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX BOOL Secure_IsTokenLocalSystem(HANDLE hToken)
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN return_value = FALSE;
|
|
|
|
ULONG64 user_space[88];
|
|
PTOKEN_USER user = (PTOKEN_USER)user_space;
|
|
ULONG len;
|
|
|
|
len = sizeof(user_space);
|
|
status = NtQueryInformationToken(
|
|
hToken, TokenUser, user, len, &len);
|
|
|
|
if (status == STATUS_BUFFER_TOO_SMALL) {
|
|
|
|
user = Dll_AllocTemp(len);
|
|
status = NtQueryInformationToken(
|
|
hToken, TokenUser, user, len, &len);
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
UNICODE_STRING SidString;
|
|
|
|
status = RtlConvertSidToUnicodeString(
|
|
&SidString, user->User.Sid, TRUE);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (_wcsicmp(SidString.Buffer, L"S-1-5-18") == 0)
|
|
return_value = TRUE;
|
|
|
|
RtlFreeUnicodeString(&SidString);
|
|
}
|
|
}
|
|
|
|
if (user != (PTOKEN_USER)user_space)
|
|
Dll_Free(user);
|
|
|
|
return return_value;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_IsLocalSystemToken
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX BOOLEAN Secure_IsLocalSystemToken(BOOLEAN CheckThreadToken)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE hToken;
|
|
|
|
BOOLEAN return_value = FALSE;
|
|
|
|
//
|
|
// open token in current thread or process
|
|
//
|
|
|
|
if (CheckThreadToken) {
|
|
status = NtOpenThreadToken(
|
|
NtCurrentThread(), TOKEN_QUERY, FALSE, &hToken);
|
|
} else
|
|
status = STATUS_NO_TOKEN;
|
|
|
|
if (status == STATUS_NO_TOKEN) {
|
|
|
|
status = NtOpenProcessToken(
|
|
NtCurrentProcess(), TOKEN_QUERY, &hToken);
|
|
}
|
|
|
|
//
|
|
// check if the user in the token is the SYSTEM sid
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
return_value = Secure_IsTokenLocalSystem(hToken);
|
|
|
|
NtClose(hToken);
|
|
}
|
|
|
|
return return_value;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_IsSameBox
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX BOOLEAN Secure_IsSameBox(HANDLE idProcess)
|
|
{
|
|
WCHAR boxname[48];
|
|
ULONG session_id;
|
|
NTSTATUS status =
|
|
SbieApi_QueryProcess(idProcess, boxname, NULL, NULL, &session_id);
|
|
if (! NT_SUCCESS(status))
|
|
return FALSE;
|
|
if (session_id != Dll_SessionId)
|
|
return FALSE;
|
|
if (_wcsicmp(boxname, Dll_BoxName) != 0)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_IsBuiltInAdmin
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX BOOLEAN Secure_IsBuiltInAdmin()
|
|
{
|
|
// Check if this is the built in administrator account its SID is always: S-1-5-21-domain-500
|
|
if (_wcsnicmp(Dll_SidString, L"S-1-5-21-", 9) != 0)
|
|
return FALSE;
|
|
if (Dll_SidStringLen < 4 || _wcsnicmp(Dll_SidString + Dll_SidStringLen - 4, L"-500", 4) != 0)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Support for UAC Elevation
|
|
//
|
|
// UAC elevation is invoked via a direct call to NdrAsyncClientCall
|
|
// (rather than normal COM wrappers) specifying the target program.
|
|
// We intercept the call and signal Sandboxie Service instead.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
/*typedef struct _RPC_ASYNC_STATE {
|
|
|
|
unsigned int Size; // size of this structure
|
|
unsigned long Signature;
|
|
long Lock;
|
|
unsigned long Flags;
|
|
void *StubInfo;
|
|
void *UserInfo;
|
|
void *RuntimeInfo;
|
|
ULONG Event;
|
|
|
|
ULONG NotificationType;
|
|
HANDLE hEvent;
|
|
|
|
} RPC_ASYNC_STATE, *PRPC_ASYNC_STATE;*/
|
|
|
|
|
|
typedef struct _SECURE_UAC_ARGS {
|
|
|
|
RPC_ASYNC_STATE *AsyncState;
|
|
void *BindingHandle;
|
|
union {
|
|
|
|
ULONG_PTR UnknownParameters[16];
|
|
|
|
struct {
|
|
// Elevate Program
|
|
WCHAR *ApplicationName;
|
|
WCHAR *CommandLine;
|
|
ULONG_PTR UnknownParameter5;
|
|
ULONG_PTR UnknownParameter6;
|
|
WCHAR *CurrentDirectory;
|
|
WCHAR *WindowStation;
|
|
ULONG_PTR UnknownParameter9;
|
|
ULONG_PTR UnknownParameter10;
|
|
ULONG_PTR UnknownParameter11;
|
|
HANDLE *ProcessHandle;
|
|
} Args1;
|
|
|
|
} u;
|
|
|
|
} SECURE_UAC_ARGS;
|
|
|
|
|
|
typedef struct _SECURE_UAC_PACKET {
|
|
|
|
//
|
|
// keep in sync with SbieSvc.exe / ServiceServer2
|
|
//
|
|
|
|
ULONG tzuk;
|
|
ULONG len;
|
|
ULONG app_len;
|
|
ULONG app_ofs;
|
|
ULONG cmd_len;
|
|
ULONG cmd_ofs;
|
|
ULONG dir_len;
|
|
ULONG dir_ofs;
|
|
ULONG inv_len;
|
|
ULONG64 hEvent;
|
|
ULONG64 hResult;
|
|
ULONG64 ret_code;
|
|
WCHAR text[1];
|
|
|
|
} SECURE_UAC_PACKET;
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Variables
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
static ULONG Secure_Elevation_Type = 0;
|
|
static RPC_ASYNC_STATE *Secure_Elevation_AsyncState = NULL;
|
|
static HANDLE *Secure_Elevation_ResultHandle = NULL;
|
|
static SECURE_UAC_PACKET *Secure_Elevation_Packet = NULL;
|
|
|
|
static BOOLEAN Secure_Elevation_HookDisabled = FALSE;
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_CheckElevation
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
ALIGNED BOOLEAN __cdecl Secure_CheckElevation(
|
|
/*void* ReturnAddressFromNdrAsyncClientCall,
|
|
PMIDL_STUB_DESC pStubDescriptor, void* pFormat,*/ SECURE_UAC_ARGS* Args)
|
|
{
|
|
static UCHAR elevation_binding_1[16] = {
|
|
0x9A, 0xF9, 0x1E, 0x20, 0xA0, 0x7F, 0x4C, 0x44,
|
|
0x93, 0x99, 0x19, 0xBA, 0x84, 0xF1, 0x2A, 0x1A };
|
|
static UCHAR elevation_binding_2_Vista[16] = {
|
|
0x7D, 0xCE, 0x54, 0x5F, 0x79, 0x5B, 0x75, 0x41,
|
|
0x85, 0x84, 0xCB, 0x65, 0x31, 0x3A, 0x0E, 0x98 };
|
|
static UCHAR elevation_binding_2_Win7[16] = {
|
|
0x23, 0x05, 0x7A, 0xFD, 0x70, 0xDC, 0xDD, 0x43,
|
|
0x9B, 0x2E, 0x9C, 0x5E, 0xD4, 0x82, 0x25, 0xB1 };
|
|
const ULONG RpcNotificationTypeEvent = 1;
|
|
|
|
#ifdef _WIN64
|
|
const SIZEOF_RPC_ASYNC_STATE = 0x70;
|
|
const OFFSET_OF_BINDING_GUID = 0x18;
|
|
#else
|
|
const SIZEOF_RPC_ASYNC_STATE = 0x44;
|
|
const OFFSET_OF_BINDING_GUID = 0x10;
|
|
#endif _WIN64
|
|
|
|
RPC_ASYNC_STATE *AsyncState;
|
|
UCHAR *ptr;
|
|
|
|
BOOLEAN ok = FALSE;
|
|
|
|
__try {
|
|
|
|
//
|
|
// elevation hook disabled?
|
|
//
|
|
|
|
if (Secure_Elevation_HookDisabled)
|
|
__leave;
|
|
|
|
//
|
|
// elevation already in progress?
|
|
//
|
|
|
|
if (Secure_Elevation_AsyncState)
|
|
__leave;
|
|
|
|
//
|
|
// recognize that NdrAsyncClientCall is used to contact the
|
|
// Application Information (AppInfo) service for a UAC request:
|
|
//
|
|
|
|
if (! Args)
|
|
__leave;
|
|
|
|
//
|
|
// confirm RPC_ASYNC_STATE structure with size 0x44 / 0x70,
|
|
// notification type == event, and a handle to an event
|
|
//
|
|
|
|
AsyncState = Args->AsyncState;
|
|
if (! AsyncState)
|
|
__leave;
|
|
if (AsyncState->Size != SIZEOF_RPC_ASYNC_STATE)
|
|
__leave;
|
|
if (AsyncState->NotificationType != RpcNotificationTypeEvent)
|
|
__leave;
|
|
//if (! AsyncState->hEvent)
|
|
if (!AsyncState->u.hEvent)
|
|
__leave;
|
|
|
|
//
|
|
// confirm a binding handle that is the result of calling
|
|
// RpcBindingFromStringBinding with the string parameter:
|
|
// 201ef99a-7fa0-444c-9399-19ba84f12a1a@ncalrpc
|
|
// or
|
|
// 5f54ce7d-5b79-4175-8584-cb65313a0e98@ncalrpc
|
|
//
|
|
|
|
#define IS_BINDING_GUID(n) \
|
|
(0 == memcmp(ptr + OFFSET_OF_BINDING_GUID, elevation_binding_##n, 16))
|
|
|
|
ptr = (UCHAR *)Args->BindingHandle;
|
|
// The name "BindingHandle" implies a handle. But the original code treats it as a ptr to mem (see memcmp above).
|
|
// Windows 10 is passing in real handles sometimes. We need a better solution that this HACK to filter out handles.
|
|
if (! ptr || ptr < (UCHAR*)0x1fff)
|
|
__leave;
|
|
|
|
if (IS_BINDING_GUID(1)) {
|
|
|
|
//
|
|
// type 1 elevation, Elevate Process:
|
|
// confirm output parameter for process handle
|
|
//
|
|
|
|
if (! Args->u.Args1.ProcessHandle)
|
|
__leave;
|
|
|
|
Secure_Elevation_Type = 1;
|
|
|
|
Secure_Elevation_ResultHandle = Args->u.Args1.ProcessHandle;
|
|
|
|
} else if (IS_BINDING_GUID(2_Vista) || IS_BINDING_GUID(2_Win7)) {
|
|
|
|
//
|
|
// type 2 elevation, Get Admin Token
|
|
// scan for parameter with value -1, output token follows
|
|
//
|
|
|
|
ULONG i;
|
|
|
|
for (i = 3; i < 16; ++i) {
|
|
if ((ULONG)Args->u.UnknownParameters[i] == (ULONG)-1) {
|
|
|
|
Secure_Elevation_Type = 2;
|
|
|
|
Secure_Elevation_ResultHandle = (HANDLE *)
|
|
Args->u.UnknownParameters[i + 1];
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (! Secure_Elevation_Type)
|
|
__leave;
|
|
|
|
} else
|
|
__leave;
|
|
|
|
#undef IS_BINDING_GUID
|
|
|
|
//
|
|
// finish
|
|
//
|
|
|
|
Secure_Elevation_AsyncState = Args->AsyncState;
|
|
|
|
ok = TRUE;
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_HandleElevation
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
ALIGNED ULONG_PTR __cdecl Secure_HandleElevation(
|
|
void *pStubDescriptor, void *pFormat, SECURE_UAC_ARGS *Args)
|
|
{
|
|
WCHAR *ApplicationName, *CommandLine, *CurrentDirectory;
|
|
ULONG pkt_len, app_len, cmd_len, dir_len;
|
|
SECURE_UAC_PACKET *pkt;
|
|
WCHAR *ptr;
|
|
SERVICE_UAC_REQ req;
|
|
MSG_HEADER *rpl;
|
|
|
|
//
|
|
// Secure_CheckElevation determined this is an elevation call,
|
|
// build a request packet for Sandboxie Service :: Service Server
|
|
//
|
|
|
|
if (Secure_Elevation_Type == 1) {
|
|
|
|
ApplicationName = Args->u.Args1.ApplicationName;
|
|
CommandLine = Args->u.Args1.CommandLine;
|
|
CurrentDirectory = Args->u.Args1.CurrentDirectory;
|
|
|
|
|
|
} else if (Secure_Elevation_Type == 2) {
|
|
|
|
ApplicationName = L"*MSI*";
|
|
CommandLine = ApplicationName;
|
|
CurrentDirectory = ApplicationName;
|
|
|
|
} else
|
|
return 0;
|
|
|
|
//
|
|
// prepare elevation packet
|
|
//
|
|
|
|
app_len = wcslen(ApplicationName);
|
|
if (app_len > 1024)
|
|
app_len = 1024;
|
|
|
|
cmd_len = wcslen(CommandLine);
|
|
if (cmd_len > 1024)
|
|
cmd_len = 1024;
|
|
|
|
dir_len = wcslen(CurrentDirectory);
|
|
if (dir_len > 1024)
|
|
dir_len = 1024;
|
|
|
|
pkt_len = sizeof(SECURE_UAC_PACKET)
|
|
+ (app_len + cmd_len + dir_len) * sizeof(WCHAR)
|
|
+ sizeof(ULONG);
|
|
|
|
pkt = Dll_Alloc(pkt_len);
|
|
|
|
pkt->tzuk = tzuk;
|
|
pkt->len = pkt_len;
|
|
pkt->inv_len = ~pkt->len;
|
|
|
|
//pkt->hEvent = (ULONG64)(ULONG_PTR)Secure_Elevation_AsyncState->hEvent;
|
|
pkt->hEvent = (ULONG64)(ULONG_PTR)Secure_Elevation_AsyncState->u.hEvent;
|
|
pkt->hResult = 0;
|
|
pkt->ret_code = ERROR_CANCELLED;
|
|
|
|
ptr = pkt->text;
|
|
|
|
pkt->app_len = app_len;
|
|
pkt->app_ofs = (ULONG)((UCHAR *)ptr - (UCHAR *)pkt);
|
|
wmemcpy(ptr, ApplicationName, app_len);
|
|
|
|
ptr += app_len;
|
|
|
|
pkt->cmd_len = cmd_len;
|
|
pkt->cmd_ofs = (ULONG)((UCHAR *)ptr - (UCHAR *)pkt);
|
|
wmemcpy(ptr, CommandLine, cmd_len);
|
|
|
|
ptr += cmd_len;
|
|
|
|
pkt->dir_len = dir_len;
|
|
pkt->dir_ofs = (ULONG)((UCHAR *)ptr - (UCHAR *)pkt);
|
|
wmemcpy(ptr, CurrentDirectory, dir_len);
|
|
|
|
ptr += dir_len;
|
|
|
|
*(ULONG *)ptr = tzuk;
|
|
|
|
//
|
|
// keep parameters for later
|
|
//
|
|
|
|
Secure_Elevation_Packet = pkt;
|
|
|
|
//
|
|
// signal Sandboxie Service
|
|
// processing continues in SbieSvc::ServiceServer::UacHandler
|
|
//
|
|
|
|
req.h.length = sizeof(SERVICE_UAC_REQ);
|
|
req.h.msgid = MSGID_SERVICE_UAC;
|
|
|
|
req.devmap[0] = L'\0';
|
|
File_GetSetDeviceMap(req.devmap);
|
|
|
|
req.uac_pkt_addr = (ULONG64)(ULONG_PTR)pkt;
|
|
req.uac_pkt_len = pkt_len;
|
|
|
|
Gui_AllowSetForegroundWindow();
|
|
|
|
rpl = SbieDll_CallServer(&req.h);
|
|
|
|
if ((! rpl) || (rpl->status != 0))
|
|
SetEvent((HANDLE)pkt->hEvent);
|
|
|
|
if (rpl)
|
|
Dll_Free(rpl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Secure_RpcAsyncCompleteCall
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
ALIGNED BOOLEAN Secure_RpcAsyncCompleteCall(
|
|
RPC_ASYNC_STATE *AsyncState, void *Reply)
|
|
{
|
|
if (AsyncState != Secure_Elevation_AsyncState)
|
|
return FALSE;
|
|
|
|
//
|
|
// Start.exe posted the event referenced by pkt->hEvent (actually
|
|
// AsyncState->hEvent), and used cross-memory calls to update
|
|
// pkt->hProcess and pkt->ret_code, which we can now return to caller
|
|
//
|
|
|
|
*Secure_Elevation_ResultHandle =
|
|
(HANDLE)(ULONG_PTR)Secure_Elevation_Packet->hResult;
|
|
*(ULONG *)Reply = (ULONG)Secure_Elevation_Packet->ret_code;
|
|
|
|
Dll_Free(Secure_Elevation_Packet);
|
|
|
|
Secure_Elevation_Packet = NULL;
|
|
Secure_Elevation_ResultHandle = NULL;
|
|
Secure_Elevation_AsyncState = NULL;
|
|
Secure_Elevation_Type = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SbieDll_DisableElevationHook
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX void SbieDll_DisableElevationHook(void)
|
|
{
|
|
Secure_Elevation_HookDisabled = TRUE;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SbieDll_GetPublicSecurityDescriptor
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
/*_FX const void *SbieDll_GetPublicSecurityDescriptor(void)
|
|
{
|
|
if (! Secure_EveryoneSD)
|
|
Secure_InitSecurityDescriptors();
|
|
return Secure_EveryoneSD;
|
|
}*/
|